#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 (https://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: https://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.

#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

#MsExchange Versions

#IAMMEC

So if you want to find out the version of Exchange you are running you can always run:

Get-ExchangeServer | Select Name, ServerRole, Edition, AdminDisplayVersion

Now this is cool, but it only shows the major version and not the CU.

You can find exchange build information from two main places:

I have dropped the information into a CSV file [ExchangeBuilds].   I found that updating a CSV file with new builds was easier than editing code all the time.

Now code below to create a summary matrix.  Enjoy

#Import the csv and covert to a hash table
$ExchangeBuilds = import-csv .\ExchangeBuilds.csv
$BuildMatrix = @{}
ForEach($Item in $ExchangeBuilds){
  $BuildMatrix.Add($item.Build, $item.version)
}

#Get exchange servers
Get-ExchangeServer | Select Name, ServerRole, Edition, AdminDisplayVersion

#Loop the servers to create the matrix
$matrix =@()
$ExchangeServers = Get-ExchangeServer | Sort Name
ForEach($Item in $ExchangeServers){
  $tmpServer = $item.Name + "." + $item.domain
  Write-Host $tmpServer 
  $tmpMatrix = "" | Select Name, Roles, Edition, FileVersion,UpdateRollup
  $tmpMatrix.Name = $tmpServer 
  $tmpMatrix.Roles = $Item.ServerRole
  $tmpMatrix.Edition = $Item.Edition

  Switch($item.AdminDisplayVersion.Major){
     6 {$key="SOFTWARE\Microsoft\Exchange\Setup"}
     8 {$key="SOFTWARE\Microsoft\Exchange\Setup"}
    14 {$Key="SOFTWARE\Microsoft\ExchangeServer\v14\Setup"}
  }

  #Find the exchange binary path
  $type = [Microsoft.Win32.RegistryHive]::LocalMachine
  $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($type, $tmpServer )
  $regKey = $regKey.OpenSubKey($key)

  #work out the file we need to use
  Switch($item.AdminDisplayVersion.Major){
     6      {$tmpPath = $regKey.GetValue("Services");      $tmpFile = "mad.exe";    $tmpPath += "\bin\"}
    Default {$tmpPath = $regKey.GetValue("MsiInstallPath");$tmpFile = "ExSetup.exe";$tmpPath += "bin\"}
  }

  #Get the file verision of mad.exe or exsetup.exe
  $tmpPath = $tmpPath.replace(":", "$")
  $tmpunc  = "\\" + $tmpServer + "\" + $tmpPath + $tmpFile

  IF (test-path $tmpunc){
    $tmpVer = (dir "$tmpunc").VersionInfo.FileVersion
    $tmpMatrix.FileVersion = $tmpVer
    $tmpMatrix.UpdateRollup = $BuildMatrix.$tmpver
  }
  $Matrix += $tmpMatrix
}
$Matrix