I have a simple script to show all certificates on a server, I would like to expand that script to then remove all expired certificates
I have tried several scripts from MS and 3rd parties to find a remove certs but have had no luck with them working properly
The first code I am using is:
Get-ChildItem Cert:\ -Recurse
This Powershell script shows all certificates on a server.
Example output is below for each certificate. I want to target the NotAfter field and have the script then remove the certificate if it's old than todays date
Subject:
Issuer:
Thumbprint:
FriendlyName:
NotBefore:
NotAfter:
Extensions
I would also like to do this for a list of servers, have the script run on each server in a text document, query all certificates, then remove the certs that are expired and move on to the next server.
I have seen some code targeting the date like the following:
ForEach-Object -begin { $now = get-date } -process { if ($PSItem.NotAfter -lt $now ) { $PSItem } } | Remove-Item
I would like the script to go out and query a servers certificates, then deletes out the expired certificates
What you are after is this. This should work perfectly for you. You were close in your logic, just the execution seemed to be a bit off.
$ListOfServers = Get-Content "c:\temp\serv.txt"
Foreach($Server in $ListOfServers) {
Invoke-Command -ComputerName $Server -ScriptBlock {
# Get Certificate list and assign to a variable
$Certs = Get-ChildItem "Cert:\LocalMachine\My" -Recurse
# Loop through each object in $Certs
Foreach($Cert in $Certs) {
# If The objects property "NotAfter" is older than the current time, delete
If($Cert.NotAfter -lt (Get-Date)) {
$Cert | Remove-Item
}
}
}
}
Edited based on comment to prevent accidental destruction of all certs.
To get a list of all cert storage locations.
(Get-ChildItem -Path "Cert:" -Recurse `
| Where-Object {($_).GetType().Name -eq 'X509Store'}).Name
Related
Very new to powershell and AD, so apologies if this post has an obvious answer. I have done some research and I am still not finding the answers I am looking for. My script is below for reference.
I have created a simple powershell script that will run on an admin vm i have setup on my domain. I have a separate SQL vm running a backup script that consume a lot of storage over time. I am trying to run this very simple script. My question is, do I need to modify this script in order to store it on my admin vm but have it run on my sql vm? Or can i leave the path as is and just set up in AD task scheduler. I have tried targeting the FQDN and the IP, but it doesn't seem to be working either way.
$backups_file = 'E:\blahBlahBla\SQL\Backups' or
$backups_file = '<IP_ADDRESS>\E:\blahBlahBla\SQL\Backups' or
$backups_file = '<FQDN>E:\blahBlahBla\SQL\Backups'
$backup_file_exist = (Test-Path -Path $backups_file)
if ($backup_file_exist){
# Verifies the folder exists
Write-Output -InputObject "This folder exists"
# returns all the files in the folder.
Get-ChildItem -Path $backups_file
# Deletes all files in the folder that are older that 7 days.
Get-ChildItem -Path $backups_file -Recurse | Where-Object {($_.LastWriteTime -lt (Get-
Date).AddDays(-7))} | Remove-Item
}
else
{
Write-Output -InputObject "Unable to access this directory."
}
Thanks.
well all your $backups_file solutions seems wrong to me.
If you want excess a directory on a Remote system, it has to be at least a fileshare or a administrative share like \\computer\e$\folder\folder\
But why using file shares or something like that when you just simple can connect to a Powershell Session on the Remote Host? here is a example.:
$mySQLServer = "Server1.domain.name", "server2.domain.name"
$backupFolder = "E:\blahBlahBla\SQL\Backups"
foreach ($server in $mySQLServer)
{
$session = New-PSSession -ComputerName $server #maybe -cred if needed
Invoke-Command -Session $session -ArgumentList $backupFolder -ScriptBlock {
param(
$directoy
)
if ($backup_file_exist)
{
# Verifies the folder exists
Write-Output -InputObject "This folder exists"
# returns all the files in the folder.
Get-ChildItem -Path $directoy
# Deletes all files in the folder that are older that 7 days.
Get-ChildItem -Path $directoy -Recurse | Where-Object { ($_.LastWriteTime -lt (Get-Date).AddDays(-7))
} | Remove-Item
}
}
Remove-PSSession
}
Good Luck!
My organization requires the filtering, and removal of all .PFX and .P12 files from our computers and servers. The script we are currently running every week does not go deep enough or far enough per higher guidance. What I'm trying to do is take my current working script, and filter for both file extensions. The person who wrote the script is all but gone from this world so I'm working on a script I didn't write but I'm trying to become familiar with.
I've already tried changing some of the variables inside of the Get-ChildItem cmdlt to apply the filtering there instead of a variable. This includes attempts like:
$Files = Get-ChildItem -Path \\$client\c$\Users -Filter -filter {(Description -eq "school") -or (Description -eq "college")} -Recurse -ErrorAction SilentlyContinue
Here is a portion of the Code, not the entire thing. There is logging and notes and other administrative tasks that are done other than this, I've only included the portion of the code that is creating errors.
$computers = Get-ADComputer -Filter * -SearchBase "AD OU PATH OMITTED"
$destination = "****\Software\aPatching_Tools\Log Files\Soft Cert\Workstations\AUG 19\WEEK 2"
$ext = "*.pfx"
foreach ($computer in $computers)
{
$client = $computer.name
if (Test-Connection -ComputerName $client -Count 1 -ErrorAction SilentlyContinue)
{
$outputdir = "$destination\$client"
$filerepo = "$outputdir\Files"
$files = Get-ChildItem -Path \\$client\c$\Users -Filter $ext -Recurse -ErrorAction SilentlyContinue
if (!$files)
{
Write-Host -ForegroundColor Green "There are no .pfx files on $client."
}
else
{
Write-Host -ForegroundColor Cyan "PFX files found on $client"
The expected and normal operation of the script is that it goes through each machine, tests it, moves on if it's offline, or if it's online, there is a 4-5 minute pause while it searches and moves on.
The errors I get when I make changes such as doing a $ext = "*.p12", ".pfx" is that -Filter does not support this operation. Or if I try the above mentioned change to the filtering, the script takes 1-2 seconds per machine, and with at times, 15-20 users in the C:\Users folder, it's nearly impossible to search that fast over the network.
Instead of passing your extensions as the -filter, pass them using -include - that is, $files = Get-ChildItem -Path \\$client\c$\Users\* -include $ext -Recurse -ErrorAction SilentlyContinue. You can then define $ext as an array of strings, e.g., $ext = "*.pfx","*.p12", and Get-ChildItem will return only those files with the indicated extensions.
While remoting in using a PS Session and as a Remote Admin, I need to delete a certificate from the store: CurrentUser\My.
I've noticed that while there are similar questions already, they do NOT answer my question. None of them address the problem of inserting local user as the context for the command.
I have tried recursing through the directories of ...\Roaming\Microsoft\Crypto\RSA, changing the cert store to LocalMachine and inserting the username manually. Nothing works.
THIS WORKS
Get-ChildItem cert:"CurrentUser\My" | where {($_.Subject -like "*OU=CO*")} | remove-item -WhatIf
THIS DOES NOT (with remote session active)
PS C:\Users\Remote Admin: Get-ChildItem cert:"CurrentUser\My" | where {($_.Subject -like "*OU=CO*")} | remove-item -WhatIf
Certificates are stored in the Registry or in LocalAppData.
If you have administrative rights you can access the registry remotely via .Net for that user or the file system and remove the certificates as needed. Here's a test I ran.
First I created a dummy cert on a remote machine. This adds the cert to CurrentUser\My and CurrentUser\CA
New-SelfSignedCertificate -DnsName "www.fabrikam.com" -CertStoreLocation "Cert:\CurrentUser\My"
Thumbprint Subject
---------- -------
E5A33C1BB6FBA8A6DA397C6BFE2CE489F751AF10 CN=www.fabrikam.com
Next I remotely access and remove the cert by thumbprint from Registry CA store.
$computer = 'ComputerA'
$targetSID = 'S-1-5-21-1234567890-1234567890-1234567890-12345' #get-aduser username
$reg = [Microsoft.win32.registryKey]::OpenRemoteBaseKey('Users', $computer) #open remote registry
$avaliableSID = #($($reg.GetSubKeyNames() | Where-Object { $_ -match 'S-\d-\d+-(\d+-){1,14}\d+$' })) #get all users SIDS
if($avaliableSID -contains $targetSID ) #if it contains the one we want
{
$otherUserStore = $reg.OpenSubKey("$targetSID\software\microsoft\systemcertificates\ca\certificates", $true) #open profile for writing
$otherUserStore.DeleteSubKey('E5A33C1BB6FBA8A6DA397C6BFE2CE489F751AF10', $false) #delete key and suppress error if missing
}
I then remove it from the My store in %localappdata%
Remove-Item "\\$computer\c$\users\SOME.USER\appdata\roaming\microsoft\systemcertificates\my\certificates\$thumbprint" -Force #force required or you get access denied
Checking cert:\currentuser\my as that user shows no sign of the certificate. Of course you should add error checking, etc. but this example should help.
I'm working on a disaster recovery project and I am recommending as part of the plan to do regular audits of the primary and secondary sites. One of the audit tasks is to make sure that the secondary site has the same certificates installed as the primary site. I think I can accomplish this using Powershell
Get-ChildItem -Path Cert:\LocalMachine\My
Get-ChildItem -Path Cert:\LocalMachine\Root
I know I can use the above commands to get a list of certs but what I'm having trouble with is trying to do this all in one script. I would like to get the list of certs on one server and then get the list of certs on another server and then compare the two lists. I'm very new to Powershell so I'm not to sure where to start.
To retrieve the certificates you would use the underlying .NET classes since the Certificates provider does not expose remote machine connectivity by default. You may find another possibility with PS remoting as well. Here is the function:
function Get-Certificates {
Param(
$Computer = $env:COMPUTERNAME,
[System.Security.Cryptography.X509Certificates.StoreLocation]$StoreLocation,
[System.Security.Cryptography.X509Certificates.StoreName]$StoreName
)
$Store = New-Object System.Security.Cryptography.X509Certificates.X509Store("\\$computer\$StoreName",$StoreLocation)
$Store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]"ReadOnly")
$Store.Certificates
}
And here is how you would use it to compare two lists:
$Left = Get-Certificates -StoreLocation LocalMachine -StoreName Root
$Right = Get-Certificates -StoreLocation LocalMachine -StoreName Root -Computer "REMOTE-PC"
# Dump to console
Compare-Object $Left $Right -property Thumbprint, FriendlyName, Subject, NotAfter | Format-Table
# Export results to file
Compare-Object $Left $Right -property Thumbprint, FriendlyName, Subject, NotAfter | Export-Csv Comparison.csv
I'm trying to choose a certificate from the computer certificate store LocalMachine\My that serves for Server Exchange or Authentication (I'm unsure regarding the exact nomenclature) purposes. I know there are filters to return only certificates of a certain type, as such:
PS> Get-ChildItem Cert:\LocalMachine\My -codesigning
and what I want is to do something like:
PS> Get-ChildItem Cert:\LocalMachine\My -exchange
Which fails in PowerShell.
If such a filter keyword doesn't exists, can some venture a way to do it, or at least how to go about it?
To get a list of certs with server authentication you can filter by the enhanced key usage properties:
dir cert:\localmachine\my | ? {
$_.Extensions | % {
$_.EnhancedKeyUsages | ? {
$_.FriendlyName -eq "Server Authentication"}}}
You can read each certificate properties in this way:
Get-ChildItem Cert:\LocalMachine\My | fl *
then you can filter by property, in this case filtering by name of issuer:
Get-ChildItem Cert:\LocalMachine\My | where-object { $_.issuer -match "servername" }