-
Notifications
You must be signed in to change notification settings - Fork 0
/
Clear-MachineKeys.ps1
248 lines (220 loc) · 10.8 KB
/
Clear-MachineKeys.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
<#PSScriptInfo
.VERSION 1.3
.GUID d4b03f9b-ddb6-420b-8417-d390a89cba50
.AUTHOR Tomas Kouba (S&T CZ)
.COMPANYNAME S&T CZ
.COPYRIGHT (c) 2022-23 S&T CZ. All rights reserved.
.TAGS RSA MachineKeys
.LICENSEURI https://raw.githubusercontent.com/sntcz/Clear-MachineKeys/main/LICENSE
.PROJECTURI https://github.com/sntcz/Clear-MachineKeys
.ICONURI
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
#>
<#
.SYNOPSIS
Clean (move or delete) Machine Key files from RSA MachineKeys folder.
.DESCRIPTION
A large number of files are found in the operating system's Machine Keys folder
(typically C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys). These files may consume excessive disk space
on the application server which can adversely affect operation of services or applications hosted on the server.
Cleaning respects well-known keys and existing machine keys from machine key store.
You have been WARNED, use at your own RISK.
.PARAMETER Path
File path to the keys folder exhibiting this issue. For example:
C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys (default value)
C:\ProgramData\Microsoft\Crypto\RSA\S-1-5-18
C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-19
C:\Windows\ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-20
C:\Windows\ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\SystemCertificates\Request\Certificates
C:\Users\All Users\Microsoft\Crypto\RSA\MachineKeys (on Windows 10 same as C:\ProgramData\...)
C:\Users\All Users\Microsoft\Crypto\RSA\S-1-5-18 (on Windows 10 same as C:\ProgramData\...)
.PARAMETER CreatedBefore
Defines retention period and takes value in days. Only files created before
the specified time are considered for deletion or moving. Default value is 90 days.
.PARAMETER Delete
Files are deleted rather than moved. Moving files allowing simple way to restore them
if any issues are observed. Files are moved to $MovePath folder. Use this
parameter carefully.
.PARAMETER LimitFiles
Limit processed files to max count. Files which cannot be moved or deleted are not counted.
.PARAMETER LimitErrors
Stop process after limited errors.
.PARAMETER AsUser
Exclude user private keys too. Experimental feature.
.PARAMETER MovePath
Destination path for backup. Default value is $Path\_saved.
.INPUTS
None.
.OUTPUTS
None.
.EXAMPLE
C:\PS> .\Clear-MachineKeys.ps1
Description
-----------
This command move all possible keys from default folder older than 90 days.
.EXAMPLE
C:\PS> .\Clear-MachineKeys.ps1 -LimitFiles 100 -Delete
Description
-----------
This command delete 100 keys from default folder older than 90 days.
.NOTES
Version : 1.2, 2023-02-09
File Name : Clear-MachineKeys.ps1
Requires : PowerShell
.LINK
https://social.msdn.microsoft.com/Forums/en-US/35176c80-3199-4df7-a2bf-9124d31e3621
https://port135.com/remove-older-files-machinekeys/
https://kb.vmware.com/s/article/82553
https://learn.microsoft.com/en-us/answers/questions/293983/accidentally-deleted-rsa-machine-key-from-one-clus
#>
[CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Move')]
Param(
[Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$False)]
[string]$Path = "C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys",
[Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$False)]
[int]$CreatedBefore = 90,
[Parameter(ParameterSetName='Delete',Mandatory=$False,ValueFromPipelineByPropertyName=$False)]
[switch]$Delete,
[Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$False)]
[int]$LimitFiles = [Int32]::MaxValue,
[Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$False)]
[int]$LimitErrors = [Int32]::MaxValue,
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$False)]
[switch]$AsUser,
[Parameter(ParameterSetName='Move',Mandatory=$False,ValueFromPipelineByPropertyName=$False)]
[string]$MovePath = ""
)
PROCESS {
# Get container names for stored certificates
function Get-StoreCertificates ($StoreName) {
foreach ($Store in (Get-ChildItem $StoreName)) {
Write-Verbose "Store: $($StoreName)\$($Store.Name)"
try {
foreach ($Key in (Get-ChildItem "$($StoreName)\$($Store.Name)" -ErrorAction SilentlyContinue)) {
$ContainerName = $Key.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
if (-not [string]::IsNullOrWhiteSpace($ContainerName)) {
Write-Debug "Container: $ContainerName "
Write-Output $ContainerName
}
}
}
catch {
Write-Warning "Store '$($StoreName)\$($Store.Name)' error: $_"
}
}
}
try {
$machineGuid = (Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography -Name MachineGuid).MachineGuid
Write-Verbose "Machine GUID: $($machineGuid)"
$lastWrite = (Get-Date).AddDays(-$CreatedBefore)
$lastBoot = (Get-CimInstance -ComputerName localhost -Class CIM_OperatingSystem -ErrorAction Ignore).LastBootUpTime
if ($null -ne $lastBoot -and $lastBoot.AddDays(-1) -gt $lastWrite) {
Write-Host "Last boot was $lastBoot, but you want to process files before $lastWrite. Try use '-CreatedBefore $(((Get-Date) - $lastBoot).Days + 1)'"
}
# Well-known exclusions
$excludeFiles = @(
"6de9cb26d2b98c01ec4e9e8b34824aa2_$machineGuid", # iisConfigurationKey
"d6d986f09a1ee04e24c949879fdb506c_$machineGuid", # NetFrameworkConfigurationKey
"76944fb33636aeddb9590521c2e8815a_$machineGuid", # iisWasKey
"c2319c42033a5ca7f44e731bfd3fa2b5_$machineGuid", # Microsoft Internet Information Server
"bedbf0b4da5f8061b6444baedf4c00b1_$machineGuid", # WMSvc Certificate Key Container
"7a436fe806e483969f48a894af2fe9a1_$machineGuid", # MS IIS DCOM Server
"f686aace6942fb7f7ceb231212eef4a4_$machineGuid", # TSSecKeySet1
"ebe703b502d1b47c601316a0c4fb6047_$machineGuid", # OPC Router Machine Key
"4f692a7dc1b824e1f679f93fadd08a3b_$machineGuid" # Failover Cluster (ClusInfraCert)
)
Write-Progress -Activity "Clear-MachineKeys" -Status "Enumeratin exclusions"
# Add exclusions from local machine cert store
$excludeFiles = $excludeFiles + @(Get-StoreCertificates 'Cert:\LocalMachine')
if ($AsUser) {
# Add exclusions from current user cert store
$excludeFiles = $excludeFiles + @(Get-StoreCertificates 'Cert:\CurrentUser')
}
# Define and initialize counters
$processedFiles = 0
$errorFiles = 0
$skippedFiles = 0
# Initialize move path and create folder it is not exist
if (-not $Delete) {
if ($MovePath.Length -eq 0) {
$MovePath = Join-Path $Path "_saved"
}
Write-Verbose "Move path: $MovePath"
if (-not (Test-Path $MovePath)) {
New-Item -Path $MovePath -ItemType Directory -Force -ErrorAction Stop | Out-Null
}
}
Write-Progress -Activity "Clear-MachineKeys" -Status "Loading directory contents"
# Process folder
foreach ($fileName in [IO.Directory]::EnumerateFiles($Path)) {
# Stop process on limits
if (($LimitFiles -ne 0 -and $processedFiles -ge $LimitFiles) -or ($LimitErrors -ne 0 -and $errorFiles -ge $LimitErrors)) {
break
}
$file = Get-Item $fileName
# Detect age of file
if ($file.LastWriteTime -le $lastWrite -and -not $excludeFiles.Contains($file.Name)) {
if ($WhatIfPreference) {
# WhatIf switch is on.
Write-Host "WhatIf: I will $(if($Delete){"delete"}else{"move"}) $($file.Name)" -ForegroundColor Yellow
$processedFiles++
} else {
# WhatIf switch is off.
if($PSCmdlet.ShouldProcess($file.Name, $(if($Delete){"DELETE"}else{"MOVE"}))){
if ($Delete) {
try {
$file.Delete()
$processedFiles++
}
catch {
Write-Warning $_
$errorFiles++
}
}
else {
try {
$file.MoveTo($(Join-Path $MovePath $file.Name))
$processedFiles++
}
catch {
Write-Warning "$($file.name): $_"
$errorFiles++
}
}
}
}
$currentOperation = "$(if($Delete){"Delete"}else{"Move"}): $($file.Name)"
}
else {
if ($WhatIfPreference) {
Write-Host "WhatIf: I will skip $($file.Name)" -ForegroundColor Yellow
}
else {
Write-Debug "Skip: $($file.Name)"
}
$currentOperation = "Skip: $($file.Name)"
$skippedFiles++
}
if ((($processedFiles + $skippedFiles) % 100) -eq 0) {
if ($LimitFiles -lt [Int32]::MaxValue) {
Write-Progress -Activity "Clear-MachineKeys" -Status "$(if($Delete){"Deleting files"}else{"Moving files"}): $($processedFiles)/$($LimitFiles), Skipped files: $skippedFiles" -CurrentOperation $currentOperation -PercentComplete ($processedFiles/$LimitFiles*100)
}
else {
Write-Progress -Activity "Clear-MachineKeys" -Status "$(if($Delete){"Deleting files"}else{"Moving files"}): $processedFiles, Skipped files: $skippedFiles," -CurrentOperation $currentOperation
}
}
}
Write-Progress -Activity "Clear-MachineKeys" -Status "Done" -Completed
if ($WhatIfPreference) {
Write-Host "WhatIf: I will $(if($Delete) {'delete'} else {'move'}) $ProcessedFiles files. Skip $skippedFiles files." -ForegroundColor Yellow
} else {
Write-Host "$processedFiles files $(if($Delete) {'Deleted'} else {'moved'}) and $errorFiles errors. Skipped $skippedFiles files." -ForegroundColor $(if ($errorFiles -eq 0) {"Green"} else {"Red"})
}
}
catch {
Write-Error $_
}
}