You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
7.3 KiB
216 lines
7.3 KiB
#MIT License
|
|
#
|
|
#Copyright (c) 2017 Rui Lopes
|
|
#
|
|
#Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
#of this software and associated documentation files (the "Software"), to deal
|
|
#in the Software without restriction, including without limitation the rights
|
|
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
#copies of the Software, and to permit persons to whom the Software is
|
|
#furnished to do so, subject to the following conditions:
|
|
#
|
|
#The above copyright notice and this permission notice shall be included in all
|
|
#copies or substantial portions of the Software.
|
|
#
|
|
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
#SOFTWARE.
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
$ErrorActionPreference = 'Stop'
|
|
trap {
|
|
Write-Host
|
|
Write-Host "ERROR: $_"
|
|
($_.ScriptStackTrace -split '\r?\n') -replace '^(.*)$','ERROR: $1' | Write-Host
|
|
($_.Exception.ToString() -split '\r?\n') -replace '^(.*)$','ERROR EXCEPTION: $1' | Write-Host
|
|
Write-Host
|
|
Write-Host 'Sleeping for 60m to give you time to look around the virtual machine before self-destruction...'
|
|
Start-Sleep -Seconds (60*60)
|
|
Exit 1
|
|
}
|
|
|
|
|
|
#
|
|
# enable TLS 1.2.
|
|
|
|
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol `
|
|
-bor [Net.SecurityProtocolType]::Tls12
|
|
|
|
|
|
#
|
|
# run automatic maintenance.
|
|
|
|
Add-Type @'
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
public static class Windows
|
|
{
|
|
[DllImport("kernel32", SetLastError=true)]
|
|
public static extern UInt64 GetTickCount64();
|
|
|
|
public static TimeSpan GetUptime()
|
|
{
|
|
return TimeSpan.FromMilliseconds(GetTickCount64());
|
|
}
|
|
}
|
|
'@
|
|
|
|
function Wait-Condition {
|
|
param(
|
|
[scriptblock]$Condition,
|
|
[int]$DebounceSeconds=15
|
|
)
|
|
process {
|
|
$begin = [Windows]::GetUptime()
|
|
do {
|
|
Start-Sleep -Seconds 3
|
|
try {
|
|
$result = &$Condition
|
|
} catch {
|
|
$result = $false
|
|
}
|
|
if (-not $result) {
|
|
$begin = [Windows]::GetUptime()
|
|
continue
|
|
}
|
|
} while ((([Windows]::GetUptime()) - $begin).TotalSeconds -lt $DebounceSeconds)
|
|
}
|
|
}
|
|
|
|
function Get-ScheduledTasks() {
|
|
$s = New-Object -ComObject 'Schedule.Service'
|
|
try {
|
|
$s.Connect()
|
|
Get-ScheduledTasksInternal $s.GetFolder('\')
|
|
} finally {
|
|
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($s) | Out-Null
|
|
}
|
|
}
|
|
|
|
function Get-ScheduledTasksInternal($Folder) {
|
|
$Folder.GetTasks(0)
|
|
$Folder.GetFolders(0) | ForEach-Object {
|
|
Get-ScheduledTasksInternal $_
|
|
}
|
|
}
|
|
|
|
function Test-IsMaintenanceTask([xml]$definition) {
|
|
# see MaintenanceSettings (maintenanceSettingsType) Element at https://msdn.microsoft.com/en-us/library/windows/desktop/hh832151(v=vs.85).aspx
|
|
$ns = New-Object System.Xml.XmlNamespaceManager($definition.NameTable)
|
|
$ns.AddNamespace('t', $definition.DocumentElement.NamespaceURI)
|
|
$null -ne $definition.SelectSingleNode("/t:Task/t:Settings/t:MaintenanceSettings", $ns)
|
|
}
|
|
|
|
Write-Host 'Running Automatic Maintenance...'
|
|
MSchedExe.exe Start
|
|
Wait-Condition {@(Get-ScheduledTasks | Where-Object {($_.State -ge 4) -and (Test-IsMaintenanceTask $_.XML)}).Count -eq 0} -DebounceSeconds 60
|
|
|
|
|
|
#
|
|
# generate the .net frameworks native images.
|
|
# NB this is normally done in the Automatic Maintenance step, but for
|
|
# some reason, sometimes its not.
|
|
# see https://docs.microsoft.com/en-us/dotnet/framework/tools/ngen-exe-native-image-generator
|
|
|
|
Get-ChildItem "$env:windir\Microsoft.NET\*\*\ngen.exe" | ForEach-Object {
|
|
Write-Host "Generating the .NET Framework native images with $_..."
|
|
&$_ executeQueuedItems /nologo /silent
|
|
}
|
|
|
|
|
|
#
|
|
# remove temporary files.
|
|
# NB we ignore the packer generated files so it won't complain in the output.
|
|
|
|
Write-Host 'Stopping services that might interfere with temporary file removal...'
|
|
function Stop-ServiceForReal($name) {
|
|
while ($true) {
|
|
Stop-Service -ErrorAction SilentlyContinue $name
|
|
if ((Get-Service $name).Status -eq 'Stopped') {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
Stop-ServiceForReal TrustedInstaller # Windows Modules Installer
|
|
Stop-ServiceForReal wuauserv # Windows Update
|
|
Stop-ServiceForReal BITS # Background Intelligent Transfer Service
|
|
@(
|
|
"$env:LOCALAPPDATA\Temp\*"
|
|
"$env:windir\Temp\*"
|
|
"$env:windir\Logs\*"
|
|
"$env:windir\Panther\*"
|
|
"$env:windir\WinSxS\ManifestCache\*"
|
|
"$env:windir\SoftwareDistribution\Download"
|
|
) | Where-Object {Test-Path $_} | ForEach-Object {
|
|
Write-Host "Removing temporary files $_..."
|
|
try {
|
|
takeown.exe /D Y /R /F $_ | Out-Null
|
|
icacls.exe $_ /grant:r Administrators:F /T /C /Q 2>&1 | Out-Null
|
|
} catch {
|
|
Write-Host "Ignoring taking ownership of temporary files error: $_"
|
|
}
|
|
try {
|
|
Remove-Item $_ -Exclude 'packer-*' -Recurse -Force -ErrorAction SilentlyContinue | Out-Null
|
|
} catch {
|
|
Write-Host "Ignoring failure to remove files error: $_"
|
|
}
|
|
}
|
|
|
|
|
|
#
|
|
# cleanup the WinSxS folder.
|
|
|
|
# NB even thou the automatic maintenance includes a component cleanup task,
|
|
# it will not clean everything, as such, dism will clean the rest.
|
|
# NB to analyse the used space use: dism.exe /Online /Cleanup-Image /AnalyzeComponentStore
|
|
# see https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/clean-up-the-winsxs-folder
|
|
Write-Host 'Cleaning up the WinSxS folder...'
|
|
dism.exe /Online /Quiet /Cleanup-Image /StartComponentCleanup /ResetBase
|
|
if ($LASTEXITCODE) {
|
|
throw "Failed with Exit Code $LASTEXITCODE"
|
|
}
|
|
|
|
# NB even after cleaning up the WinSxS folder the "Backups and Disabled Features"
|
|
# field of the analysis report will display a non-zero number because the
|
|
# disabled features packages are still on disk. you can remove them with:
|
|
# Get-WindowsOptionalFeature -Online `
|
|
# | Where-Object {$_.State -eq 'Disabled'} `
|
|
# | ForEach-Object {
|
|
# Write-Host "Removing feature $($_.FeatureName)..."
|
|
# dism.exe /Online /Quiet /Disable-Feature "/FeatureName:$($_.FeatureName)" /Remove
|
|
# }
|
|
# NB a removed feature can still be installed from other sources (e.g. windows update).
|
|
Write-Host 'Analyzing the WinSxS folder...'
|
|
dism.exe /Online /Cleanup-Image /AnalyzeComponentStore
|
|
|
|
|
|
#
|
|
# reclaim the free disk space.
|
|
|
|
Write-Host 'Reclaiming the free disk space...'
|
|
$results = defrag.exe C: /H /L
|
|
if ($results -eq 'The operation completed successfully.')
|
|
{
|
|
$results
|
|
}
|
|
else
|
|
{
|
|
if ((Get-CimInstance Win32_OperatingSystem).version -eq "6.3.9600")
|
|
{
|
|
return
|
|
}
|
|
else
|
|
{
|
|
Write-Host 'Zero filling the free disk space...'
|
|
(New-Object System.Net.WebClient).DownloadFile('https://download.sysinternals.com/files/SDelete.zip', "$env:TEMP\SDelete.zip")
|
|
Expand-Archive "$env:TEMP\SDelete.zip" $env:TEMP
|
|
Remove-Item "$env:TEMP\SDelete.zip"
|
|
&"$env:TEMP\sdelete64.exe" -accepteula -z C:
|
|
}
|
|
}
|
|
|