#NetApp DataOnTap and Invoke-NaSysstat

So I have been play with the NetApp DataOnTap add in for a while now want to share this.  Essentially all it does is run Invoke-NaSysstat at timed intervals and saves it to an hourly csv file

[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')
$filer   = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the comma seperated filer name", "Enter Filer Name", "")

#Change to match you path
Import-Module E:\PsMON\netapp\DataONTAP.psd1
Connect-NaController $Filer -Credential root

$matrix = @()
$doit = $True

$timer = 10

$LastHour = (Get-Date -format "HH")

while($doit){
  $ThisHour = (Get-Date -format "HH")
  $tSysStat = Invoke-NaSysstat -Count 1 | Select
  $tSysStat
  $matrix += $tSysStat
  If($ThisHour -ne $LastHour){
    $outFile = $filer + "_" + (Get-Date -format "yyyy-MM-dd_HHmm") + ".csv";
    $Matrix | Export-Csv $outFile -NoTypeInformation;$matrix = @();
    "-$outFile-"
  }
  $LastHour = $ThisHour

  For($i=0;$i-le $timer;$i++){Sleep 1 }
}

$outFile = $filer + "_" + (Get-Date -format "yyyy-MM-dd_HHmm") + ".csv";$Matrix | Export-Csv $outFile -NoTypeInformation

Exchange Server 2010 Group Usage with Windows Powershell

#MsExchange #IAMMEC

So I have been running some discovery scripts on an M&A and wanted to check DL’s and when they were last used.  Came up with this ..

$days = 90
Set-ADServerSettings -ViewEntireForest:$true
$results = Get-TransportServer | get-messagetrackinglog -eventid expand -resultsize unlimited -start (get-date).addDays(-$days) | sort timestamp -desc
$results.count

IF(![string]::IsNullOrEmpty($results)) {
  $report = @()
  $lists = @()
  $data = $results | group relatedrecipientaddress | sort name
  $lists = get-distributiongroup -resultsize unlimited | Select Alias, primarysmtpaddress, Name, RecipientType, OrganizationalUnit, @{Expression={""};Label="Count"}, @{Expression={""};Label="LastUsed"}
  $lists += Get-DynamicDistributionGroup -ResultSize unlimited | Select Alias, primarysmtpaddress, Name, RecipientType, OrganizationalUnit, @{Expression={""};Label="Count"}, @{Expression={""};Label="LastUsed"}
  $lists = $lists | sort alias
  foreach ($list in $lists) {
    $check = $null
    $check = $data | ?{$_.name -like "$($list.primarySMTPaddress.tostring())"}
    if ($check) {
      $List.Count = $Check.Count
      $List.LastUsed = ($check | select -expand group | select -first 1).TimeStamp
    }
  }
}

$lists | Export-csv GroupsWithUsage.csv -NoTypeInformation -Encoding  Unicode

Enjoy

#MsExchange 2010 Get-MailboxDatabaseCopyStatus

So Tony Redmond gave me a blank look when I spoke to him for like 30 seconds at MEC, but this little code snippet is featured on PG 491 of Microsoft Exchange 2010 Inside Out.

I use this a lot, and have it wrapped in to a .ps1. Just discovered that like Get-ExchangeServer you can pipe an array of objects to Get-MailboxDatabase. So you could say run:

"db01","db02" | Get-MailboxDatabase
Now that is cool. I have an issue at the moment where some Donkey thought it would be a good idea to reseed two 600GB databases (We use NetApp storage so there is a better way to do it!). I wanted a way to keep an eye on them so I updated the original script.

Do you can use the -database switch like this to get the database copy status for db01 and db04
 .\Check-DatabaseCopyStatus.ps1 -Database @("db01","db04")

-and you could you the server switch to get the database copy status for db01 and db04 just on ex1
 .\Check-DatabaseCopyStatus.ps1 -Database @("db01","db04") -Server ex1

Enjoy
PARAM([String]$Server="", [String[]]$Database = "*")
If($server -ne ""){$srvtxt=$server;$Server="\" + $server.ToUpper()}ELSE{$srvTxt="All"}
Write-Host "Server:" $srvtxt "`nDatabase:"$Database "`n"

##########################################################################################
#Load the Exchange 2010 bits & bobs
#########################################################################################
$xPsCheck = Get-PSSnapin | Select Name | Where {$_.Name -Like "*Exchange*"}
If ($xPsCheck -eq $Null) {Write-Host "Loading Exchange Snapin"; Add-PsSnapin Microsoft.Exchange.Management.PowerShell.e2010}

$Database | Get-MailboxDatabase | Sort Name | FOREACH {$db=$_.Name; $xNow=$_.Server.Name ;$dbown=$_.ActivationPreference| Where {$_.Value -eq 1}; Write-Host $db "on" $xNow "Should be on" $dbOwn.Key -NoNewLine; If ( $xNow -ne $dbOwn.Key){Write-host " WRONG" -ForegroundColor Red; }ELSE{Write-Host " OK" -Foregroundcolor Green};Get-MailboxDatabaseCopyStatus $db$Server;"`n"}

Download it from here

#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

#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://blog.flaphead.com/wp-content/uploads/2014/03/get-certificate.zip

Enjoy

#Powershell WhoAmI

This is a funky function I have in my profile.ps1

Function WhoAmI([switch]$All=$False){
  If($All){$xUser = [System.Security.Principal.WindowsIdentity]::GetCurrent() }ELSE{ $xUser = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).Name}
  Write-Host $xUser -Foregroundcolor Green
}

You can run:

WhoAmI

which give provide your user name -or-

WhoAmI -All

will give you extra goodness
Enjoy

#Powershell Check if Running

So I have a number of scripts that run as scheduled tasks.  The issue is that some scripts take longer to run and at times I have multiple copies of the same script running.  I wanted to stop this.  So I knocked up this.  Essentially it checks to see if the script that has started is already running, and then it kills the old process, and the script continues.

#AppName is the name of the script
$AppName = "Get-PerformanceInfo.ps1"

Write-Host "Checking if script is already running"
#Get the PID of this script
$myPID = ([System.Diagnostics.Process]::GetCurrentProcess()).id
Write-Host $myPID

#Who is the user running the script
$cu = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).Name.split("\")[-1];

#Check to see if the script is already running
$xRunning=get-wmiobject win32_process|where{$_.name -eq "powershell.exe"}|select name,processid,commandline,@{n="owner"; e={$_.getowner().user}} | where {($_.owner -eq $cu) -AND ($_.commandline -like "*$AppName*")  -AND ($_.processid -ne $myPID)};

#If it's running kill it 
IF(-NOT [string]::IsNullOrEmpty($xRunning)){Write-Host "Running! Terminating!";$xRunning | FL; $xRunning | forEach{$xpid = $_.processid; $xpid;Stop-Process -id $xpid -force -confirm:$false}}ELSE{Write-Host "Not Running!"}

Powershell Flip-Array Function

You know when you get some output from say Get-ExchangeServer that has load of values and when you output it to HTML it goes off the page and looks pants?  I do quite a bit of this, and thought, can I flip the array, so around.  Yeah baby you can! So here you go.  This v2 with some bugs ironed out.  Enjoy

function Flip-Array([Array]$Array, [String]$key){
  $matrix =@()
  $KeyValue = @()
  $Array | ForEach{$KeyValue += $_.$key}
  $cols = @(); $cols += "Property" ; $cols += $keyValue
  $rows = @(); $array | Get-Member | Where {($_.MemberType -like "*Property") -AND ($_.Name -ne $key)} | ForEach{$Rows += $_.Name}
  $Rows = $Rows | sort unique
  $rows | ForEach{
    $tmpMatrix = "" | Select $cols
    $tmpMatrix.Property = $_
    $Matrix += $tmpMatrix
  }

  ForEach($item in $Array){
    $ItemKey = $item.$key
    ForEach($Row in $Rows){
      $itemvalue = $Item.$Row
      if($itemvalue.GetType.Name -like "MultiValued*"){$itemvalue = $Item.$Row -Join "!#!"}
      ($Matrix | Where {$_.Property -eq $row}).$ItemKey = $itemvalue 
    }
  }
  $matrix = $matrix | sort property -Unique
Return $Matrix
}

$e = get-exchangeserver
$h = Flip-Array $e Name | ConvertTo-Html
$h | out-file .\exchange.html