Updated 31-05-2016: Numerous improvements include; better error-handling, progress indicators, option to delete already expired certificates etc. etc.
Jesper Arnecke asked me to rework one of my earlier scripts, to add some functionality and improve usability, so I did! 🙂
This script connects to the DC, gets a list of machines with the “OperatingSystem” value set to “*Server*”, if it can succesfully ping the machine(s), it will return a list of certificates that expire after the date specified and compile a CSV file of the results.
<# .SYNOPSIS Finds expired certificates on servers in a domain. .DESCRIPTION Connects to a DC, gets a list of machines with the “OperatingSystem” value set to “*Server*”, if it can succesfully ping the machine(s), it will return a list of certificates that expire after the date specified and compile a CSV file of the results. .PARAMETER cleanup Delete already expired certificates .PARAMETER date Sets the expiration date to look for. .PARAMETER days How many days in the future to set the expiration date to. .PARAMETER verbose Generate additional output to screen. .EXAMPLE ./certificateExpiry.ps1 -cleanup .EXAMPLE ./certificateExpiry.ps1 -date 6/6/66 .EXAMPLE ./certificateExpiry.ps1 -days 666 .EXAMPLE ./certificateExpiry.ps1 -Verbose .NOTES This script requires the following permissions: 1) Domain User access to read computer objects in AD. 2) Remote Powershell access 3) Access to Certificate MMC. The easiest way to achieve this is with domain admin rights. Also please dont use both the -date and the -days parameters simultaneously :) .LINKCertificate Expiry#> [CmdletBinding()] Param( [Parameter(Mandatory=$false)] [Switch]$cleanup, [Parameter(Mandatory=$false)] [Int]$days = 365, [Parameter(Mandatory=$false)] [DateTime]$date ) function TimeStamp {"[$($sw.Elapsed.Hours)h$($sw.Elapsed.Minutes)m$($sw.Elapsed.Seconds)s]"} $sw = New-Object System.Diagnostics.Stopwatch;$sw.Start() Push-Location (Split-Path $MyInvocation.MyCommand.Path) @('certs.csv','fail.csv','expired.csv') | Remove-Item -ErrorAction SilentlyContinue if($date -eq $null){$date = (Get-Date).AddDays($days)} #if([bool](Write-Verbose ([String]::Empty) 4>&1)){Clear-Host} # Clears screen if script is run with -Verbose parameter $splitDate = $date.ToString().Split(" ")[0] Write-Output "Expiration date is set to: $splitDate" $DC = ([system.net.dns]::GetHostByAddress(([System.Net.Dns]::GetHostAddresses($(Get-WmiObject -Class WIN32_ComputerSystem).Domain)[0].IPAddressToString))).hostname.split(".")[0] $OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $DC $osVersion = [Float]($OS.version.Split('.')[0]+'.'+$OS.version.Split('.')[1]) if($osVersion -lt 6.1) { Write-Warning "Domain Controller does not meet minimum operating system requirements" Write-Host "`n","Minimum OS is: Microsoft Windows Server 2008 R2`n", "Current OS is:" $OS.Caption Exit } $DCsession = New-PSSession -ComputerName $DC -Authentication Kerberos $envs = Invoke-Command -Session $DCsession -ScriptBlock { Import-Module ActiveDirectory Get-ADComputer -Filter {OperatingSystem -like "*Server*"} | sort Name Remove-Module ActiveDirectory } Remove-PSSession $DCsession $envNo = 0 $envsCount = $envs.Count while($envNo -lt $envsCount){ foreach($env in $envs){ $envNo++ $percentDone = "{0:N0}" -f (($envNo/$envs.Count)*100) Write-Progress -activity "Currently working on server $envNo of $envsCount" -status "$percentDone% done" -percentComplete $percentDone if($env.DNSHostName -ne $null){$hostname = $env.DNSHostName}else{$hostname = $env.Name} Write-Verbose "" Write-Verbose "$(TimeStamp) $HostName" if(Test-Connection -ComputerName $HostName -Count 1 -Quiet){ $output = $null Try {$output = Invoke-Command -ComputerName $HostName -Authentication Kerberos -ScriptBlock {Get-ChildItem Cert:\LocalMachine -Recurse} -ErrorAction Stop} Catch{ $csv = @() $csv += New-Object PSObject -Property @{Server=$env.Name;FailReason=(((($Error[0].Exception | Select-Object Message -ExpandProperty Message) -replace '`r`n','') -replace '\s+', ' ') -replace ' -',' ').Substring(0,1000)} $csv | Export-CSV fail.csv -NoTypeInformation -Append Write-Verbose "$(TimeStamp) Ping successful, unable to remote. Check fail.csv for details" } if($output -ne $null) { if($cleanup){ $expired = $output | where {($_.NotAfter -ne $null) -and ($_.NotAfter -lt (Get-Date))} if($expired -ne $null){ Invoke-Command -ComputerName $env.Name -Authentication Kerberos -ScriptBlock {Get-ChildItem Cert:\LocalMachine -Recurse | where {($_.NotAfter -ne $null) -and ($_.NotAfter -lt (Get-Date))} | Remove-Item} Write-Verbose "$(TimeStamp) $($expired.count) already expired certificates found (and removed)" $expired | select PSComputerName,Subject,NotAfter,Thumbprint,PSPath | Export-Csv expired.csv -NoTypeInformation -Append } } else{ $output | Where-Object {($_.NotAfter -ne $null) -AND ($_.NotAfter -lt $date)} | Select-Object @{ Label = 'Server'; Expression = { $HostName } }, @{ Label = 'Location'; Expression = { ($_.PSParentPath).TrimStart("Microsoft.PowerShell.Security\Certificate") }}, @{ Label = 'IssuedTo'; Expression = { $_.GetNameInfo( 'SimpleName', $false ) } },@{ Label = 'IssuedBy' ;Expression = { $_.GetNameInfo( 'SimpleName', $true ) } }, NotAfter, ThumbPrint, FriendlyName | Export-Csv -Path certs.csv -NoTypeInformation -Append Write-Verbose "$(TimeStamp) $(($output | Where-Object {($_.NotAfter -ne $null) -AND ($_.NotAfter -lt $date)}).Count) certificate(s) will have expired by $splitDate" } } else {} }else{ $csv = @() $csv += New-Object PSObject -Property @{Server=$env.Name;FailReason='Ping failed'} $csv | Export-CSV fail.csv -NoTypeInformation -Append Write-verbose "$(TimeStamp) Ping failed" } } } Write-Verbose "" $fails = (Import-Csv fail.csv).Count if(Test-Path certs.csv){Write-Output "On $(($envs.Count)-$fails) machine(s), a total of $((Import-Csv certs.csv).Count) certificate(s) will have expired by $splitDate"} if(Test-Path expired.csv){Write-Output "On $(($envs.Count)-$fails) machines, a total of $((Import-Csv expired.csv).Count) expired certificates were removed"} if(Test-Path fail.csv){Write-Output "On $fails other machine(s), the script failed for various reasons. See fail.csv"} Write-Output "Script total runtime: $($sw.Elapsed.Hours)h$($sw.Elapsed.Minutes)m$($sw.Elapsed.Seconds).$($sw.Elapsed.Milliseconds)s" Pop-Location $sw.Stop();$sw.Reset()
1 Comment
Comments are closed.