Hi Everyone,
I have a list of VMs where I want to delete a few Certificates, some of these certificates on these windows boxes have the same name so I am trying to get the certificate thumbprint and then remove by using that. While also trying to build in some logic to the script.
I am having an issue with this line specifically
Invoke-VMScript -VM $vm -Server $hosts -ScriptText "Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -EQ $certificateThumbprint3}" -GuestCredential $cred
For whatever reason the command does not like $_.Thumbprint, when i run this command locally on a server it functions without issue. Seems to only be an issue using invoke-vm
One way I got it to work was to use this syntax Where-Object -Property Thumbprint -EQ $certificateThumbprint3 the issue is I am struggling trying to add the other 2 variables to the syntax.
Below is the script any help would be greatly appreciated.
# Setup Array With Hosts
$hosts = @(
"example vcenter",
"example vcenter"
);
# Connect to both vCenters
# Connect-VIServer -Server $hosts
# Admin Credential for VMs you are connecting to
$cred = Get-Credential
# Define the thumbprint of the certificate to check for
$certificateThumbprint1 = "Thumb Print"
$certificateThumbprint2 = "Thumb Print"
$certificateThumbprint3 = "Thumb Print"
# Get a list of all VMs
#$vms = Get-VM -Name "*core*" -Server $hosts | Where-Object {$_.PowerState -eq "PoweredOn"}
#$vms = Get-Content C:\ps1\Cert_Status.txt
$vms = get-vm -Name "VMName" -Server $hosts
# Create an empty array to store the results
$results = @()
# Loop through each VM
foreach ($vm in $vms) {
# Connect to the VM
Invoke-VMScript -VM $vm -Server $hosts -ScriptText "Test-Path Cert:\LocalMachine\My" -GuestCredential $cred
if ($LastExitCode -eq 0) {
# Path exist
Invoke-VMScript -VM $vm -Server $hosts -ScriptText "Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -EQ $certificateThumbprint3}" -GuestCredential $cred
if ($LastExitCode -eq 0) {
# Certificate is present, remove it
Invoke-VMScript -VM $vm -Server $hosts -ScriptText "Remove-Item -Path Cert:\LocalMachine\My -Recurse | Where-Object {$_.Thumbprint -eq '$certificateThumbprint1' -or $_.Thumbprint -eq '$certificateThumbprint2' -or $_.Thumbprint -eq '$certificateThumbprint3' }" -GuestCredential $cred
# Add the VM name and status to the results array
$results += New-Object PSObject -Property @{
VMName = $vm.Name
Status = "Certificate removed"
}
} else {
# Add the VM name and status to the results array
$results += New-Object PSObject -Property @{
VMName = $vm.Name
Status = "Certificate not found"
}
}
} else {
# Path not exist
$results += New-Object PSObject -Property @{
VMName = $vm.Name
Status = "Cert:\LocalMachine\My not exist"
}
}
}
# Output the results to a text file
$results | Export-Csv -NoTypeInformation -Path C:\export\Cert_Removal.csv
You are AWESOME!
I fixed some things, and I moved the new code down to the other IF statement to align the certificates on the ones that were deleted and not found. The script is perfect now! Thanks for all of your help and having to endure my million questions.
Final Script:
clear
# Setup Array With Hosts
$hosts = @(
"vcenter",
"vcenter1"
);
# Connect to both vCenters
# Connect-VIServer -Server $hosts
# Credential for VMs you are connecting to
# $cred = Get-Credential
# Define the thumbprint of the certificate to check for
$certificateThumbprint1 = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
$certificateThumbprint2 = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
$certificateThumbprint3 = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
#Script block used to gather the information we are requesting
$scriptblock = @'
$wwwsvc = Get-Service w3svc -ErrorAction SilentlyContinue -ErrorVariable ServiceError
If ($ServiceError){
"Cannot find any service with service name 'w3svc'- IIS Is Not Installed"
}
elseif($wwwsvc.Status -ne "Running"){
"Service 'w3svc' found - not running"
}
else{
"Service 'w3svc' found - running"
}
'@
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My |
Where-Object {`$_.Thumbprint -eq "$certificateThumbprint1" -or
`$_.Thumbprint -eq "$certificateThumbprint2" -or
`$_.Thumbprint -eq "$certificateThumbprint3"} |
Select -ExpandProperty Thumbprint
'@
$code2 = @'
Get-ChildItem -Path Cert:\LocalMachine\My -Recurse |
Where-Object {`$_.Thumbprint -eq "$certificateThumbprint1" -or
`$_.Thumbprint -eq "$certificateThumbprint2" -or
`$_.Thumbprint -eq "$certificateThumbprint3"} |
Remove-Item -Force
'@
# Add the VMs that you want to check the cert on
$list = Get-Content C:\ps1\Cert_Status.txt
$count = 0
$outFile = 'C:\Export\HostingCert_Status.txt'
foreach ($item in $list) {
"$item" | Out-File -FilePath $outFile -Append
#Adding VM Counter to OutPut Window
$count++
$percentComplete = [math]::Round(($count / $list.Count) * 100, 1)
Write-Progress -Activity "Collecting Certificate Information On $($item)" -Status "$percentComplete% Complete" -PercentComplete $percentComplete
$outputtest = Invoke-VMScript -VM $item -Server $hosts -ScriptText $scriptblock -GuestCredential $cred
"`t$($outputtest.ScriptOutput)" | Out-File -FilePath $outFile -Append
if ($outputtest.ScriptOutput -match "^Service 'w3svc' found - running") {
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
$result = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode -GuestCredential $cred
if ('' -ne $result.ScriptOutput) {
# Certificate(s) is/are present, remove it/them
"`tThumbprint(s) found" | Out-File -FilePath $outFile -Append
"`tRemoving certificate(s) with thumbprint:" | Out-File -FilePath $outFile -Append
$result.ScriptOutput.Split("`n") | ForEach-Object -Process {
"`t$($_.Trim("`r"))" | Out-File -FilePath $outFile -Append
}
$subCode2 = $ExecutionContext.InvokeCommand.ExpandString($code2)
$result2 = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode2 -GuestCredential $cred
$result2.ScriptOutput | Out-File -FilePath $outFile -Append
} else {
"`tNo thumbprints found" | Out-File -FilePath $outFile -Append
}
}
}
Since you are using double quotes on the ScriptText value, the PS engine will first substitute all variables.
This includes the $_ variable.
The easiest way to avoid substitution for the $_ variable and do substitution for the $certificateThumbprint3 variables is to use the ExpandString method and escape (back-tick) the variables you don't want to get substituted.
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {`$_.Thumbprint -EQ $certificateThumbprint3}
'@
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode -GuestCredential $cred
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi LucD, thanks for the response.
I tried to add your code above, the output its below
ScriptOutput
-----------------------------------------------------------------------------------------------------------------------| Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {.Thumbprint -EQ
1234567890987654321 -or .Thumbprint -eq '0987654321234567890 }
I guess essentially the output that I am looking for which works locally is this
Thumbprint
---------- -------
09876543211234567890
12345678900987654321
Ultimately I guess I am not concerned what the output looks like I just dont think the scriptoutput you gave me will work
If you know of a different way or a better way I am all ears
$code = @'
"Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {`$_.Thumbprint -EQ $certificateThumbprint3 -or `$_.Thumbprint -EQ $certificateThumbprint2}"
'@
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode -GuestCredential $cred
This is the code that I added to the script
That should be
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {`$_.Thumbprint -EQ $certificateThumbprint3 -or `$_.Thumbprint -EQ $certificateThumbprint2}
'@
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode -GuestCredential $cred
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I should have mentioned that I tried both double quotes and no quotes, with no quotes I get the below error
Try with
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {`$_.Thumbprint -eq "$certificateThumbprint3" -or `$_.Thumbprint -eq "$certificateThumbprint2"}
'@
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode -GuestCredential $cred
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Unbelievable, it was the case sensitive -EQ ?!
Looks like that part of the script is working now, I will try and edit the below lines for removal, I may have another question or 2. Thanks LucD!
No, the EQ I changed because I don't like operators in uppercase 😅
The solution is to have the certificate variables between quotes, that way PS knows it is a String
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
The -EQ was driving me nuts too lol - I was going to fix when the script is actually working
Seems things are working better, a few issues that I would like to see if you have any suggestions for
It seems like after we get the thumbprints the script does not seem to process the next invoke-vm which would be to remove the thumbprints. I don't think the script likes the part where it is using if($lastexit code -eq 0) do you have any other suggestions for that IF statement?
If I run the remove scriptblock only it will actually remove a certificate, I noticed though that if it finds 3 certs its only removing 1 of them, if i run the block again it removes another one, then if i run it again it will remove the last one, it is not removing all of them at once.
Adding Snippet of Script
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {`$_.Thumbprint -eq "$certificateThumbprint1" -or `$_.Thumbprint -eq "$certificateThumbprint2" -or `$_.Thumbprint -eq "$certificateThumbprint3"}
'@
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode -GuestCredential $cred
#Invoke-VMScript -VM $vm -Server $hosts -ScriptText "Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -EQ $certificateThumbprint3}" -GuestCredential $cred
if ($LastExitCode -eq 0) {
# Certificate is present, remove it
$code2 = @'
Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {`$_.Thumbprint -contains "$certificateThumbprint1" -or `$_.Thumbprint -contains "$certificateThumbprint2" -or `$_.Thumbprint -contains "$certificateThumbprint3"} | remove-item -force
'@
$subCode2 = $ExecutionContext.InvokeCommand.ExpandString($code2)
Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode2 -GuestCredential $cred
Imho, a better way to check if any of the thumbprints is to check the output of the script.
Then loop through all the thumbprints, and remove them one by one.
Afaik, that $LastExit variable doesn't really work with Invoke-VMScript.
You could check the $result.ExitCode, but again I'm not sure if that actually will work correctly.
You would need to use the exit statement in the code you invoke.
Perhaps try it like this?
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My |
Where-Object {`$_.Thumbprint -eq "$certificateThumbprint1" -or
`$_.Thumbprint -eq "$certificateThumbprint2" -or
`$_.Thumbprint -eq "$certificateThumbprint3"} |
Select -ExpandProperty Thumbprint
'@
$code2 = @'
Get-ChildItem -Path Cert:\LocalMachine\My -Recurse |
Where-Object {`$_.Thumbprint -contains "$_"} |
Remove-Item -force
'@
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
$result = Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode -GuestCredential $cred
if ($null -ne $result.ScriptOutput) {
# Certificate(s) is/are present, remove it/them
"Thumprint(s) found"
$result.ScriptOutput | foreach-object -Process {
"Removing certificate with thumbprint: $_"
$subCode2 = $ExecutionContext.InvokeCommand.ExpandString($code2)
Invoke-VMScript -VM $vm -Server $hosts -ScriptText $subCode2 -GuestCredential $cred
}
}
else {
"No thumprints found"
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Since your last post regarding the exit code potential issue I tried a different way to see if the actual VM will have the IIS service running / installed. The script is some what working but I still had to run it multiple times to get it to delete the thumb prints. Im also trying to add some logging to a txt file, basically just want the VM Name, is the service is not installed to log it, then the script output of the certificate being removed
Clear
# Setup Array With Hosts
$hosts = @(
"vcenter",
"vcenter"
);
# Connect to both vCenters
# Connect-VIServer -Server $hosts
# Credential for VMs you are connecting to
$cred = Get-Credential
# Define the thumbprint of the certificate to check for
$certificateThumbprint1 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$certificateThumbprint2 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$certificateThumbprint3 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Add the VMs that you want to check the cert on
$list = Get-Content C:\ps1\Cert_Status.txt
$count = 0
foreach($item in $list){
#Adding VM Counter to OutPut Window
$count++
$percentComplete = [math]::Round(($count / $list.Count) * 100,1)
Write-Progress -Activity "Collecting Certificate Information On $($item)" -Status "$percentComplete% Complete" -PercentComplete $percentComplete
#Script block used to gather the information we are requesting
$scriptblock = @'
$wwwsvc = Get-Service w3svc -ErrorAction SilentlyContinue -ErrorVariable ServiceError;
If ($ServiceError){
Write-warning -message "Cannot find any service with service name 'w3svc'- IIS Is Not Installed"
}
If($wwwsvc.Status -ne "Running"){
#$servername = hostname
#$servername
#Write-Host "IIS is not Installed! This is most likely not a StoreFront VM!"
$servername = hostname
#$servername
sleep -Seconds 5
exit
}
'@
$outputtest = Invoke-VMScript -VM $item -Server $hosts -ScriptText $scriptblock -GuestCredential $cred
$outputtest
add-content -path C:\Export\HostingCert_Status35.txt -Value $item, $outputtest.ScriptOutput
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My |
Where-Object {`$_.Thumbprint -eq "$certificateThumbprint1" -or
`$_.Thumbprint -eq "$certificateThumbprint2" -or
`$_.Thumbprint -eq "$certificateThumbprint3"} |
Select -ExpandProperty Thumbprint
'@
$code2 = @'
Get-ChildItem -Path Cert:\LocalMachine\My -Recurse |
Where-Object {`$_.Thumbprint -contains "$certificateThumbprint1" -or
`$_.Thumbprint -eq "$certificateThumbprint2" -or
`$_.Thumbprint -eq "$certificateThumbprint3"} |
Remove-Item -force
'@
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
$result = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode -GuestCredential $cred
if ($null -ne $result.ScriptOutput) {
# Certificate(s) is/are present, remove it/them
"Thumprint(s) found"
$result.ScriptOutput | foreach-object -Process {
"Removing certificate with thumbprint: $_"
$subCode2 = $ExecutionContext.InvokeCommand.ExpandString($code2)
Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode2 -GuestCredential $cred
add-content -path C:\Export\HostingCert_Status35.txt -Value $result.ScriptOutput
}
}
else {
"No thumprints found"
}
}
Something like this?
clear
# Setup Array With Hosts
$hosts = @(
"vcenter",
"vcenter"
);
# Connect to both vCenters
# Connect-VIServer -Server $hosts
# Credential for VMs you are connecting to
$cred = Get-Credential
# Define the thumbprint of the certificate to check for
$certificateThumbprint1 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$certificateThumbprint2 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$certificateThumbprint3 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#Script block used to gather the information we are requesting
$scriptblock = @'
$wwwsvc = Get-Service w3svc -ErrorAction SilentlyContinue -ErrorVariable ServiceError
If ($ServiceError){
"Cannot find any service with service name 'w3svc'- IIS Is Not Installed"
}
If($wwwsvc.Status -ne "Running"){
"Service 'w3svc' found - not running"
}
else{
"Service 'w3svc' found - running"
}
'@
$code = @'
Get-ChildItem -Path Cert:\LocalMachine\My |
Where-Object {`$_.Thumbprint -eq "$certificateThumbprint1" -or
`$_.Thumbprint -eq "$certificateThumbprint2" -or
`$_.Thumbprint -eq "$certificateThumbprint3"} |
Select -ExpandProperty Thumbprint
'@
$code2 = @'
Get-ChildItem -Path Cert:\LocalMachine\My -Recurse |
Where-Object {`$_.Thumbprint -contains "$certificateThumbprint1" -or
`$_.Thumbprint -eq "$certificateThumbprint2" -or
`$_.Thumbprint -eq "$certificateThumbprint3"} |
Remove-Item -force
'@
# Add the VMs that you want to check the cert on
$list = Get-Content C:\ps1\Cert_Status.txt
$count = 0
$outFile = 'C:\Export\HostingCert_Status35.txt'
foreach ($item in $list) {
"Looking at VM $item" | Out-File -FilePath $outFile -Append
#Adding VM Counter to OutPut Window
$count++
$percentComplete = [math]::Round(($count / $list.Count) * 100, 1)
Write-Progress -Activity "Collecting Certificate Information On $($item)" -Status "$percentComplete% Complete" -PercentComplete $percentComplete
$outputtest = Invoke-VMScript -VM $item -Server $hosts -ScriptText $scriptblock -GuestCredential $cred
"`t$($outputtest.ScriptOutput)"| Out-File -FilePath $outFile -Append
if ($outputtest.ScriptOutput -eq "Service 'w3svc' found - running") {
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
$result = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode -GuestCredential $cred
if ($null -ne $result.ScriptOutput) {
# Certificate(s) is/are present, remove it/them
"`tThumprint(s) found" | Out-File -FilePath $outFile -Append
$result.ScriptOutput | ForEach-Object -Process {
"`tRemoving certificate with thumbprint: $_" | Out-File -FilePath $outFile -Append
$subCode2 = $ExecutionContext.InvokeCommand.ExpandString($code2)
$result2 = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode2 -GuestCredential $cred
$result2.ScriptOutput | Out-File -FilePath $outFile -Append
}
} else {
"`tNo thumprints found" | Out-File -FilePath $outFile -Append
}
}
}
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
Hi LucD, Thank You! I think if I can get this script to work it would accomplish what I am looking for.
The script now is getting the service and outputting the status but nothing else is happening after that, I think it is getting hung up on the below. For example, I have a VM with the service running and one of the thumbprints installed but it is not removing it or applying the proper output. I do not really see any issue with the code.
if ($outputtest.ScriptOutput -eq "Service 'w3svc' found - running") {
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
$result = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode -GuestCredential $cred
if ($null -ne $result.ScriptOutput) {
# Certificate(s) is/are present, remove it/them
"`tThumprint(s) found" | Out-File -FilePath $outFile -Append
$result.ScriptOutput | ForEach-Object -Process {
"`tRemoving certificate with thumbprint: $_" | Out-File -FilePath $outFile -Append
$subCode2 = $ExecutionContext.InvokeCommand.ExpandString($code2)
Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode2 -GuestCredential $cred
$result.ScriptOutput | Out-File -FilePath $outFile -Append
}
} else {
"`tNo thumprints found" | Out-File -FilePath $outFile -Append
}
}
}
Can you check what
$result.ScriptOutput.Count
returns when at least a certificate is found?
Also, does the output contain the line
Removing certificate with thumbprint:
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
$result.ScriptOutput.Count
returns 0
Only thing in the Output is the VM name and Service status
I ran this code line only
$subCode = $ExecutionContext.InvokeCommand.ExpandString($code)
then this line
$result = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode -GuestCredential $cred
And it is getting me what I should see
I don't think the IF statement is working properly
Can you show me what exactly is in the 'C:\Export\HostingCert_Status35.txt' file?
You can anonymize the text of needed.
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I found an error in the last code I posted.
I forgot to capture the result of the last call to Invoke-VMScript.
I corrected the code above, output now in $result2.
These are the lines involved
$subCode2 = $ExecutionContext.InvokeCommand.ExpandString($code2)
$result2 = Invoke-VMScript -VM $item -Server $hosts -ScriptText $subCode2 -GuestCredential $cred
$result2.ScriptOutput | Out-File -FilePath $outFile -Append
Blog: lucd.info Twitter: @LucD22 Co-author PowerCLI Reference
I re-copied the script and reconfigured my vCenters and Thumbprints, the script is still not working
$result is populating with the proper amount / thumbprint
$result2 has nothing