#Powershell and #NetApp SnapDrive

I blogged this script earlier, but discovered an undocumented feature where the last entry in the output was not added to the array object.  Here is the updated version

Write-Host "Running sdcli disk list"
$diskInfo = Invoke-Expression "sdcli disk list"
$sdclidisks  = @()

$fields  = "UNCPath","LUNPath","StorageSystem","StorageSystemPath","Type","Diskserialnumber","BackedbySnapshotCopy"
$fields += "Shared","BootOrSystemDisk","SCSIport","Bus","Target","LUN","Readonly","Size","SnapmirrorSource","SnapvaultPrimary"
$fields += "DiskPartitionStyle","CloneSplitRestorestatus","DiskID","VolumeName","Mountpoints","IPAddresses","FCinitiatorWWPN"
ForEach($Item in $DiskInfo){
  $tmpItem = $Item.Trim()
  $tmpItemSplit = $tmpItem.Split(":")
  Switch -Wildcard ($tmpItem){
    "The operation completed successfully.*" {$sdclidisks  += $sdcliDiskList}
    "UNC Path:*"  {$sdclidisks  += $sdcliDiskList
                   $sdcliDiskList = "" | Select $fields
                   $sdcliDiskList.UNCPath = $tmpItemSplit[-1]}

    "LUN Path:*"                   {$sdcliDiskList.LUNPath                 = $tmpItemSplit[-1]}
    "Storage System:*"             {$sdcliDiskList.StorageSystem           = $tmpItemSplit[-1].trim()}
    "Storage System Path:*"        {$sdcliDiskList.StorageSystemPath       = $tmpItemSplit[-1].trim()}
    "Type:*"                       {$sdcliDiskList.Type                    = $tmpItemSplit[-1].trim()}
    "Disk serial number:*"         {$sdcliDiskList.Diskserialnumber        = $tmpItemSplit[-1].trim()}
    "Backed by Snapshot Copy:*"    {$sdcliDiskList.BackedbySnapshotCopy    = $tmpItemSplit[-1].trim()}
    "Shared:*"                     {$sdcliDiskList.Shared                  = $tmpItemSplit[-1].trim()}
    "BootOrSystem Disk:*"          {$sdcliDiskList.BootOrSystemDisk        = $tmpItemSplit[-1].trim()}
    "SCSI port:*"                  {$sdcliDiskList.SCSIport                = $tmpItemSplit[-1].trim()}
    "Bus:*"                        {$sdcliDiskList.Bus                     = $tmpItemSplit[-1].trim()}
    "Target:*"                     {$sdcliDiskList.Target                  = $tmpItemSplit[-1].trim()}
    "LUN:*"                        {$sdcliDiskList.Lun                     = $tmpItemSplit[-1].trim()}
    "Readonly:*"                   {$sdcliDiskList.Readonly                = $tmpItemSplit[-1].trim()}
    "Size:*"                       {$sdcliDiskList.Size                    = $tmpItemSplit[-1].trim()}
    "Snapmirror Source:*"          {$sdcliDiskList.SnapmirrorSource        = $tmpItemSplit[-1].trim()}
    "Snapvault Primary:*"          {$sdcliDiskList.SnapvaultPrimary        = $tmpItemSplit[-1].trim()}
    "Disk Partition Style:*"       {$sdcliDiskList.DiskPartitionStyle      = $tmpItemSplit[-1].trim()}
    "Clone Split Restore status:*" {$sdcliDiskList.CloneSplitRestorestatus = $tmpItemSplit[-1].trim()}
    "DiskID:*"                     {$sdcliDiskList.DiskID                  = $tmpItemSplit[-1].trim()}
    "Volume Name:*"                {$sdcliDiskList.VolumeName              = $tmpItemSplit[-1].trim()}
    "*Mount points:*"              {$sdcliDiskList.Mountpoints             = $tmpItem.Split("`t")[-1].trim()}
    "IP Addresses:*"               {$sdcliDiskList.IPAddresses             = $tmpItemSplit[-1].trim()}
   "FC initiator WWPN:*"           {$sdcliDiskList.FCinitiatorWWPN         = $tmpItem.Split("`t")[-1].trim()}
  }
 }

$sdclidisks = $sdclidisks | where {$_.DiskID -ne $Null}

Now you have $sdclidisks you can say export to csv.

Download

Enjoy

#Powershell and Get-ExchangeServer

#IAMMEC

Now that is cool.  Been playing with the cmdlet Get-ExchangeServer.  I wanted to get a group of different server names in one go, without having the run the cmdlet multiple times.

So I thought I would try and chuck an array at Get-ExchangeServer and see what it does?  It only bloody works ;-)

$s=@()
$s +="MyServers*"
$s += "Exch*"
$s | Get-ExchangeServer

Neat trick, and you can also pass arrays in as a script parameter too.  Found this: http://santoshbenjamin.wordpress.com/2008/09/30/powershell-and-arrays-as-named-parameters/

The key for a script or function parameter is [String[]]$Server=”*”

I Like it ALOT!

#Powershell and Windows Update

So I have been using this for a while, but can’t remember if I shared it, so here it is.  I have two functions to get Installed Updated and Pending Updates.

They use winrm, so if it doesn’t work against a remote server you just need to run winrm qc

function Get-InstalledSoftwareUpdates {
  param($ComputerName=(HOSTNAME), $Credential)

  $code = {
    $Session = New-Object -ComObject Microsoft.Update.Session
    $Searcher = $Session.CreateUpdateSearcher()
    $HistoryCount = $Searcher.GetTotalHistoryCount()
    $Searcher.QueryHistory(1,$HistoryCount)
  } 

  Invoke-Command $code @psboundparameters | Select @{Expression={$ComputerName};Label="Machine"},  Date, Title, Description, ResultCode
}

-and-

function Get-PendingSoftwareUpdates {
  param($ComputerName=(HOSTNAME), $Credential)

  $code = {
    $Session = New-Object -ComObject Microsoft.Update.Session
    $Searcher = $Session.CreateUpdateSearcher()
    $HistoryCount = $Searcher.GetTotalHistoryCount()
    $SearchResult = $Searcher.Search("IsInstalled=0 and Type='Software'")
    For ($X = 0; $X -lt $SearchResult.Updates.Count; $X++){$SearchResult.Updates.Item($X)}
  } 

  Invoke-Command $code @psboundparameters | Select @{Expression={$ComputerName};Label="Machine"}, Title, Description, IsHidden, IsMandatory
}

You can run it against your windows desktop too, just paste in the functions and run them.  Prob best to put them in to a variable so:

$installed = Get-InstalledSoftwareUpdates
$pending = Get-PendingSoftwareUpdates

I also do some funky html output too, but I will let you play with that ;-)

Enjoy

Windows Certificates (Schannel 36885)

Ohh, Blog post by Email ;-)

Okay so following on from my last post (http://blog.flaphead.com/2014/03/16/windowspowershell-and-windows-certificates-2/) I wanted to share the reason behind the script.

We have been seeing lots of these in the System Event Log on out Exchange Servers:

Log Name: System
Source: Schannel
Event ID: 36885
Description:
When asking for client authentication, this server sends a list of trusted certificate authorities to the client. The client uses this list to choose a client certificate that is trusted by the server. Currently, this server trusts so many certificate authorities that the list has grown too long. This list has thus been truncated. The administrator of this machine should review the certificate authorities trusted for client authentication and remove those that do not really need to be trusted.

It turns out this is because Update for Root Certificates For Windows Server 2008 R2 x64 Edition [December 2012] (KB931125) was installed on some of the Exchange Servers but not all.

So I have another script to show Installed and Pending windows updates ;-) that I will share later.

Hope this helps

#WindowsPowerShell and Windows Certificates

So I had a need to get an idea of certificates installed on my Exchange 2010 Servers.  So after a but of “binging” I adapted and created this function.

function Get-Certificate{
  param([switch]$verbose=$False,
        [string]$Server=(hostname),
        [ValidateSet("AuthRoot","Root","CA","MY","Disallowed", "AddressBook","TrustedPeople","TrustedPublisher")] [string]$MemberName="root")
  IF($verbose){Write-Host $Server"\"$MemberName -Foregroundcolor Yellow}
  $ro=[System.Security.Cryptography.X509Certificates.OpenFlags]"ReadOnly"
  $lm=[System.Security.Cryptography.X509Certificates.StoreLocation]"LocalMachine"
  $store=new-object System.Security.Cryptography.X509Certificates.X509Store("\\$Server\$MemberName",$lm)
  $store.Open($ro)
  $store.Certificates

}

Now this is okay, but I wanted to get it for all my Exchange Servers.  So I bolted this around the function. (NB you have to run it in the Exchange Management Shell). Now this part

$CertStoresCSV =@()
$CertStoresCSV+="CertStore,Name"
$CertStoresCSV+="AuthRoot,Third-Part Root Certification Authorities"
$CertStoresCSV+="CA,Intermediate Certification Authorities"
$CertStoresCSV+="My,Personal"
$CertStoresCSV+="Root,Trusted Root Certification Authorities"
$CertStoresCSV+="AddressBook,AddressBook"
$CertStoresCSV+="TrustedPeople,TrustedPeople"
$CertStoresCSV+="TrustedPublisher,TrustedPublisher"
$CertStores = $CertStoresCSV | ConvertFrom-CSV
$matrix=@()
$s=@(); Get-ExchangeServer | where {-NOT $_.IsEdgeServer}| ForEach{$s+= $_.Name+"."+$_.Domain}
ForEach($Server in $s){
  ForEach($CertStore in $CertStores){
    $cs = $CertStore.CertStore
    $n  = $CertStore.Name
    Write-Host $server"\"$cs"\"$n -Foregroundcolor Yellow
    $c=Get-Certificate -Server $server -MemberName $cs
    [array]$tmpMatrix = $c | Select @{Expression={$server};Label="Server"}, @{Expression={$cs};Label="Certstore"}, @{Expression={$n};Label="CertStoreName"},FriendlyName, Issuer, Subject, Thumbprint, NotAfter, NotBefore, HasPrivateKey
    $Matrix += $tmpMatrix
  }
  Write-Host "-"
}

Okay so that is cool.  We now have an array object called $Matrix, how about a bit of output. This creates another array that we can output to html in a bit.

$HtmlMatrix = @()
$byServer = $Matrix | Group Server | Sort Name
$tmpColS  = @();$byServer | ForEach{$tmpColS += $_.Name}
$tmpCol   = @();$tmpCol += "Certstore","CertStoreName", "FriendlyName", "Subject","Thumbprint"; 
$tmpCol  += $tmpColS

[array]$byCert = $Matrix | Group Certstore, CertStoreName, Subject, FriendlyName, Thumbprint | Sort Name
ForEach($tmpCert in $byCert){
  $tmpHtmlMatrix = "" | Select $tmpCol
  [Array]$tmpName = $tmpCert.Name.Split(",")
  $tmpHtmlMatrix.CertStore = $tmpCert.Group[0].CertStore
  $tmpHtmlMatrix.CertStoreName = $tmpCert.Group[0].CertStoreName
  $tmpHtmlMatrix.FriendlyName = $tmpCert.Group[0].FriendlyName
  [Array]$tmpCertGroup = $tmpCert.Group
  ForEach($tmpGroup in $tmpCertGroup){
    $tmpS = $tmpGroup.Server
    $tmpHtmlMatrix.$tmpS = "x"
    $tmpHtmlMatrix.Subject = $tmpGroup.Subject
    $tmpHtmlMatrix.Thumbprint = $tmpGroup.Thumbprint 
  }
  $HtmlMatrix += $tmpHtmlMatrix
}

Now that is a bit funky, we have taken the array and then flipped it ;-) We now have servers as columns and certificates as rows. What the it then does is against each server / certificate, it puts an x in column. The result is $HtmlMatrix.  Lets format that bad boy:

$today = Get-Date
$OutHtml = @()
$OutHtml += @"
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
<head>
<TITLE>Certificate Information</TITLE></HEAD></span>
<span style="font-family: Consolas;"><Style>
TABLE{border-width: 1px;padding: 1px;border-style: solid;border-color: black;border-collapse: collapse; width=100%}
TD{border-width: 1px;padding: 1px;border-style: solid;border-color: black;}
TH{font-family:'Arial';font-size:12px;border-width: 1px;padding: 1px;border-style: solid;border-color: black;background-color:peachpuff;}
TR{font-family:'Arial';font-size:10px}
P{font-family:'Arial';font-size:12px;}
H2{font-family:'Arial';}
</Style>
</head>
<B><FONT size='2' face='VERDANA'>Certificate Information</B></font>
<BR><FONT size='1' face='VERDANA'>Last updated: $today</FONT></FONT>
"@

$tmpColH = @(); $tmpColH += "FriendlyName", "Subject","Thumbprint"; $tmpColH += $tmpColS
$htmlMatrix | Group CertStoreName | ForEach{$OutHtml += "<HR Size=6 Color=Green><H2>" + $_.Name + " [" + $_.Count + "]</H2>";$OutHtml += $_.Group | Select $tmpColH | ConvertTo-Html -Fragment}
$x=0
$i=0; $OutHtml | foreach{
If($x -eq 0){$bgcolor = "style='background-color:#dddddd'";$x=1}ELSE{$bgcolor = ""; $x=0}
Switch -wildcard ($_){
    "*<tr>*"       { $OutHtml[$i] = $OutHtml[$i].Replace("<tr>", "<tr $bgcolor>")}
    "*<td>x</td>*" { $OutHtml[$i] = $OutHtml[$i].Replace("<td>x</td>","<td bgcolor=Yellow align=center><B>x</B></td>")}
    "*<th>*"       { $OutHtml[$i] = $OutHtml[$i].Replace("<th>", "<th class='vertical'>")}
}
$i++ 
}
$OutHtml | out-file CertificateInformation.html

The end result should be CertificateInformation.html.

Code to download is here: http://ucinfo.files.wordpress.com/2014/03/get-certificate.zip

Enjoy

#Lenovo Helix

Its been a while since my original post [June 2013], and I wanted to update you on how my Helix and I was getting along.

Let me start by saying I had hoped it would be “the” laptop for me, but it’s not.  Its lacking two key things for me that would make a lot of difference and make it awesome.

The two things it is missing are a microsd slot and an accessible USB port!

Why you say? So I am working on a powerpoint off a USB.  I have to go to a meeting, but its one of those where you know your not going to have to do much, so you just want to take the tablet and leave the dock at you desk.  Have you seen the issue yet?  My USB stick is plugged in to the keyboard dock, so I have save and close powerpoint, eject the USB, remove the tablet from the dock and then reinsert in the USB stick in the bottom of the tablet!  It would be sooooooooooo much better there was a USB port was on the side of the tablet!

Next is the microsd.  I typically was TV on the way to work in the morning.  I currently use a Microsoft Surface RT to do just that.  With the USB ports in the bottom of the Helix tablet it’s a pain in the butt to balance the tablet on your lap with a USB sticking out the bottom.  The Helix has a full size SIM slot in the bottom as well as two stupid label trays.  Why not make the sim slot a mini or micro sim and have a tray that can take a microsd, in similar way to the Nokia tablet?

.. and finally, the Pen.  You can ink with the bad boy, but unlike my two previous tablets, the pen doesn’t have an eraser!  Huh? Aye, on the Tosh you write with one end and then turn it around and erase with the other! Cool, but not with the Helix!

Anyway, rant off.  I use my Helix every day and it does what I need, but could do better! A few simple things could make the Helix 2 even better.

Calculate the number of workdays using Excel

This is a cool function, that I didn’t know about.  I basically wanted to find out the number of working days between two dates.  Found the NETWORKDAYS function! This is extracted from the Help file ;-)

Description
Returns the number of whole working days between start_date and end_date. Working days exclude weekends and any dates identified in holidays. Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days worked during a specific term.
Tip: To calculate whole workdays between two dates by using parameters to indicate which and how many days are weekend days, use the NETWORKDAYS.INTL function.

Syntax
NETWORKDAYS(start_date, end_date, [holidays])

The NETWORKDAYS function syntax has the following arguments:
Start_date    Required. A date that represents the start date.
End_date    Required. A date that represents the end date.
Holidays    Optional. An optional range of one or more dates to exclude from the working calendar, such as state and federal holidays and floating holidays. The list can be either a range of cells that contains the dates or an array constant of the serial numbers that represent the dates.
Follow

Get every new post delivered to your Inbox.