Powershell retrieving cert by Thumbprint as string versus string variable - powershell

I'm trying to piece together some PowerShell code to loop through a list of servers, return some info regarding their IIS sites and bindings, and if they have an https binding, get the certificateHash and use that locate the cert by thumbprint and return its expiration date.
The problem I am having is, when i run my code below $binding.cerficateHash seems to return what I would expect, a string of the cert Hash, but when I use that certificateHash property to try and get the cert by its thumbprint, it doesnt work... but when I take the raw string value of the certificateHash value and hardcode it, it works...
I've inspected the certificateHash.GetType() and it appears to be just a string, so i dont understand what im doing wrong, and ive tried a handful of things, with no avail, granted this is my first crack at powershell so there's lots I don't know.
$sites = Invoke-Command -ComputerName $serverName { Import-Module WebAdministration; Get-ChildItem -path IIS:\Sites } -ErrorAction SilentlyContinue
foreach($site in $sites)
{
$serverName
$site.name
$site.physicalPath
foreach($binding in $site.bindings.Collection)
{
$binding.protocol
$binding.bindingInformation
$binding.certificateHash
$binding.certificateStoreName
if($binding.certificateHash)
{
# This outputs AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$binding.certificateHash
# this retrieves a cert and returns its expiration date, Woohooo!
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -eq "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" })[0].GetExpirationDateString() }
# this does not find a cert, and ive tried many things, and no dice.
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -eq $binding.certificateHash })[0].GetExpirationDateString() }
# i've tried extracting the hash via "tostring" and using that, no dice
$hash = $binding.certificateHash.ToString()
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -eq $hash })[0].GetExpirationDateString() }
# i've tried adding some wildcards and using the -like operator, no dice.
$hash = "*" + $binding.certificateHash + "*"
Start-Job Invoke-Command -ComputerName $serverName -ScriptBlock { (Get-ChildItem -path Cert:\LocalMachine\WebHosting | Where-Object {$_.Thumbprint -lilke $hash })[0].GetExpirationDateString() }
}
}
}
Example output for a site.
Site1
D:\Apps\site1
http
*:80:Site1-test.ourdomain.com
https
*:443:Site1-test.ourdomain.com
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
WebHosting

The computer you invoke the script block on doesn't know about the $binding variable in your local session. (That's also why it works when passing a literal string.)
Try passing the value as argument:
Invoke-Command -Computer $serverName -Script {
param ($hash)
(gci Cert:\LocalMachine\WebHosting | ? Thumbprint -eq $hash)[0].GetExpirationDateString()
} -Arg $binding.certificateHash

Related

Using Powershell Get-ItemProperty through all of AD computers object

I'm a complete newbie in Powershell (and programming as you may have guessed), I want to get the result of the following PS command for each of our AD computer object and print the result in a text file...but I'm completely lost. Does anyone have a lifeline I could hold on to?
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-String -Pattern "mysoftwarename"
Thank you very much.
$ScriptBlock = {Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | Select-String -Pattern "mysoftwarename"}
$Computers = (Get-ADComputers -filter * ).name
$Creds = (Get-Credential)
foreach ($Computer in $Computers)
{
"`n`n$Computer`n" >> .\file.txt # "`n" just emulates Enter key press
Invoke-Command -ComputerName $Computer -ScriptBlock $ScriptBlock -Credential $Creds >> .\file.txt
}
This will work fine if you have all your computers online and PS remoting configured properly. Otherwise, it will require modifications.

How to find PST files on remote computers using Powershell script

I need some help. I cant’t find what’s wrong with my PowerShell script.
The goal is quite simple. I have to find (.*pst)-files on the users profile on domain computers in the network. Location to search is “C:\Users\”.
List of the PC names where exported to listcomputer.txt. The trouble is the script run with no errors and no message at all.
$computers = Get-Content c:\temp\listcomputer.txt
$filePath = "C:\Users\"
foreach($computer in $computers)
{
if(Test-Connection -ComputerName $computer -Quiet)
{
Invoke-Command -ComputerName $computer -ScriptBlock
{Get-ChildItem -Path $filePath -Recurse -Include '*.pst, *.ost'} }}
First of all I’ve to check connectivity to hosts by Test-Connection cmdlet.
Separately each of the command run successfully. I've tried it.
For example: Test-Connection -ComputerName $computer
runs with “true” result and it’s OK.
Also
Invoke-Command -ComputerName $computer -ScriptBlock {Get-ChildItem -Path $filePath -Recurse -Include '*.pst'} The result is displayed data with information about files were find in folders.
But all together run with no visible result in the PowerShell console console view result
Regards!
.pst can be located anywhere, even other drives, or attached storage. You are only looking for C:\.
So maybe this refactor to hit all potential connected drives.:
Get-Content -Path 'c:\temp\listcomputer.txt' |
ForEach-Object {
if(Test-Connection -ComputerName $PSItem -Quiet)
{
Invoke-Command -ComputerName $PSItem -ScriptBlock {
(Get-CimInstance -ClassName Win32_LogicalDisk).DeviceID |
ForEach-Object {
If ($PSItem -eq 'C:')
{Get-ChildItem -Path "$PSItem\Users" -Recurse -Include '*.pst, *.ost'}
Else {Get-ChildItem -Path $PSItem -Recurse -Include '*.pst, *.ost'}
}
}
}
}

Pass Varibles read from file to invoke-command

I'm struggling to remotely recycle all IIS app pools with powershell with the word "Test" in the name, but ALSO exclude a couple of specific AppPools with Test in the name. I can do it locally with:
## List of Apppool Names to Exclude
$Exclusions = Get-Content "C:\temp\Recycle TEST app pools Exclusions.txt"
## Load IIS module:
Import-Module WebAdministration
## Restart app pools with test in the name
Get-ChildItem –Path IIS:\AppPools -Exclude $Exclusions | WHERE { $_.Name -like "*test*" } | restart-WebAppPool}
However I can't get it to exclude the app pools from the list when I use:
$server = 'SERVER01', 'SERVER02'
## List of Apppool Names to Exclude
$Exclusions = Get-Content "C:\temp\Recycle TEST app pools Exclusions.txt"
## Load IIS module:
Import-Module WebAdministration
## Restart app pools with test in the name
invoke-command -computername $server -ScriptBlock {Get-ChildItem –Path IIS:\AppPools -Exclude $args[0] | WHERE { $_.Name -like "*test*" } | restart-WebAppPool}} -ArgumentList $Exclusions
The file "C:\temp\Recycle TEST app pools Exclusions.txt" does exist on the remote computer , but also does it need to? Can the list be passed in to the Invoke-Command also if it can be got to work?
Thanks in advance
While passing arrays as a single parameter can be difficult, you can take advantage of it here because you only have one argument type anyway.
invoke-command -computername $server -ScriptBlock {Get-ChildItem –Path IIS:\AppPools -Exclude $args[0] | WHERE { $_.Name -like "*test*" } | restart-WebAppPool}} -ArgumentList $Exclusions
In this, you use $args[0], but this is equivalent to $Exclusions[0] because all items in the array have been passed as arguments.
But if they've all been passed as arguments... that's what $args is. So use it exactly as you used $Exclusions locally.
Invoke-Command `
-ComputerName $server `
-ArgumentList $Exclusions `
-ScriptBlock {
Get-ChildItem –Path "IIS:\AppPools" -Exclude $args |
Where-Object Name -like "*test*" |
Restart-WebAppPool
}

Counting the results of Invoke-Command

I have a job that goes off and checks for specified windows processes that have taken over 5 minutes of CPU time, across a range of servers.
The processes and servers are supplied via arrays, and looped through with some loops, this works nicely.
However, what I want to do is count how many results are found for each process as it loops through.
For the purpose of this example, the set variables would be
$seconds = 300
$server = "SERVER1"
$process = "notepad.exe"
And the command I run is as follows
$list = (Invoke-Command -ComputerName $server -ScriptBlock {
Param($Rseconds, $Rprocess)
Get-Process | Where {
($_.CPU -gt $Rseconds) -and
($_.Path -like "*$Rprocess”)
} | ForEach-Object {
$_.Kill()
}
} -ArgumentList $seconds, $process)
As far as killing the process, it works perfectly, and respects the values input, but what I can't get it to do is count how many of each process it killed
I've tried simply incrementing a counter within the ForEach-Object block, and tried sticking Measure-Object in various places to try and return a value, so I can call on something like
$list.Count
But nothing seems to work. It simply returns a blank value.
Found the answer to this
$listCount = (Invoke-Command -ComputerName $server -ScriptBlock { param($Rseconds,$Rprocess) Get-Process | Where { ($_.CPU -gt $Rseconds) -and ($_.Path -like "*$Rprocess”) } | Measure-Object } -ArgumentList $seconds,$process)
Had to add in the Measure-Object, but I was also being fooled by my own setup. I'd limited the server scope to one server, but I was checking the wrong one, so led myself down a blind alley, and seems I'd had the solution previously anyway
All working now
Use Stop-Process with the parameter -PassThru instead of calling Kill() in a loop.
$list = Invoke-Command -ComputerName $server -ScriptBlock {
Param($Rseconds, $Rprocess)
Get-Process | Where-Object {
$_.CPU -gt $Rseconds -and
$_.Path -like "*$Rprocess"
} | Stop-Process -Force -PassThru
} -ArgumentList $seconds, $process
If you want only the count returned instead of a list of process objects you could do something like this instead:
$list = Invoke-Command -ComputerName $server -ScriptBlock {
Param($Rseconds, $Rprocess)
$proc = Get-Process | Where-Object {
$_.CPU -gt $Rseconds -and
$_.Path -like "*$Rprocess"
}
$proc | Stop-Process -Force
$proc.Count
} -ArgumentList $seconds, $process

Powershell script to find currently bound expiring certificates in IIS remotely

I'm currently working on a script that will send an email once the certificates binded in my web servers' IIS are nearing there expiration date. I do have the script to send it in email. All I need to know is how to compare the certificates available in the store query versus the certificates currently in use. For now, here's what I have:
$Date= (Get-Date)
$SMTPServer = "smtp.test.com"
$From = "testmail#noreply.com"
Import-Module WebAdministration
$Servers = #("WEBSERVER1", "WEBSERVER2")
$certificates = foreach($server in $Servers){
Invoke-Command -ComputerName $server -ScriptBlock { $CertAll = Get-ChildItem -Path Cert:\LocalMachine\My }
Invoke-Command -ComputerName $server -ScriptBlock { $CertInUse = Get-ChildItem -Path IIS:\SslBindings }
Invoke-Command -ComputerName $server -ScriptBlock { $CertSame = Compare-Object -ReferenceObject $CertAll -DifferenceObject $CertInUse -Property Thumbprint -IncludeEqual -ExcludeDifferent }
Invoke-Command -ComputerName $server -ScriptBlock { $cert = $CertSame | ForEach {Get-ChildItem -Path Cert:\LocalMachine\My\$($_.thumbprint)} |
Select-Object Subject, DaysUntilExpired, NotAfter, #{n='ExpireInDays';e={($_.notafter - ($Date)).Days}}}
}
$certificates | Sort DisplayName
Any help and suggestion would be appreciated. Thanks!
The script above never works as you are creating variables in separate sessions to the same computer.
You can do it in two ways.
Create a session object targeting the destination server once and reuse it. So that you will be able to get the variables defined in the session in subsequent Invoke-command executions.
Without creating a session object, but by executing everything on the remote server in a single Invoke-Command .
example:-
Invoke-command -computerName $Server {
$CertAll = ....
$CertInUse = ....
$CertSame = ....
$cert = $CertSame | ForEach ..... |
Select-Object Subject, DaysUntilExpired .....
}
if you don't have any further actions on the remote server after identifying the certiricates expire date, I would suggest to use the second option.
#PRASOON I already managed to check my certificates remotely. I try to work with different references I found in google. Anyhow, here's the script.
$Date = Get-Date
$servers = Get-Content C:\servers.txt
$cert = Foreach ($server in $servers) {
Invoke-Command -ComputerName $server -ScriptBlock{
Import-Module WebAdministration; Get-ChildItem -Path IIS:SslBindings | ForEach-Object -Process{
if ($_.Sites)
{
$certificate = Get-ChildItem -Path CERT:LocalMachine\My |
Where-Object -Property Thumbprint -EQ -Value $_.Thumbprint
[PSCustomObject]#{
Sites = $_.Sites.Value
DnsNameList = $certificate.DnsNameList
NotAfter = $certificate.NotAfter
ExpireInDays = ($certificate.NotAfter - (Get-Date)).Days}
}
}
}
}
$cert | Select PSComputerName, DnsNameList, NotAfter, ExpireInDays | Where-Object {$_.ExpireInDays -lt 30} | Out-File C:\results.txt
So basically, this will display the certficates which will expire exactly or within 30 days from now. I am still working on this because what I'm trying to do is to send an email when the script detects a certificate that will expire 30 days from the current date and send an email notification. I will ask my concern about that in another post.