Skip to content

Instantly share code, notes, and snippets.

@grenade
Last active April 14, 2024 12:42
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save grenade/7dc3048753f8f298a51b to your computer and use it in GitHub Desktop.
Save grenade/7dc3048753f8f298a51b to your computer and use it in GitHub Desktop.
#--------------------
#
# Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
#
#--------------------
<#
.SYNOPSIS
Can either patch, or prepare the instance to be bundled
into an AMI.
.DESCRIPTION
Script to more easily patch/maintain and bundle instances
into AMI's.
Automation across large numbers of hosts can be done by
using the Sysinternals (psexec.exe) toolset from technet.
.LINK
https://forums.aws.amazon.com/forum.jspa?forumID=30
.EXAMPLE
"Runs cleanup on instance, then runs sysprep and shuts down -
Administrator password set to regenerate on boot."
powershell .\InstallUpdates.ps1 -patch 0
.EXAMPLE
"Installs all the latest Patches (including Recommended)"
powershell .\InstallUpdates.ps1 -patch 1
.EXAMPLE
"Installs only the Required Patches"
powershell .\InstallUpdates.ps1 -patch 2
#>
Param(
[string]$patch
)
#Runs the EC2Config Service and bundles Instance into new AMI
function EC2ConfigPackage
{
#Determine OS Version so appropriate cleanup tasks can be performed:
switch -wildcard ((Get-WmiObject Win32_OperatingSystem).Caption)
{
"*2008*" #Windows 2008
{
write-log "Windows 2008 Detected -- running cleanup"
$OS = "2008"
#Cleanup Profile Usage Information
remove-item -Path "C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Recent\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
remove-item -Path "C:\Users\Administrator\AppData\Local\Temp\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
remove-item -path "C:\Windows\System32\sysprep\Panther\setupact.log" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
remove-item -path "C:\Windows\System32\sysprep\Panther\setuperr.log" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
Remove-Item -Path "C:\Windows\System32\sysprep\Panther\IE\setupact.log" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
#Checks License status before continuing; required for Sysprep to be successful
foreach ($item in (gwmi SoftwareLicensingProduct))
{
if ($item.LicenseStatus -eq 1)
{
write-log "Windows is Licensed"
$licensed = $true
}
}
}
"*Server 2003*" #Windows 2003
{
write-log "Windows 2003 Detected -- running cleanup"
$OS = "2003"
###Run Disk Cleanup
cleanmgr.exe /VERYLOWDISK
Wait-Process -Name cleanmgr
#Cleanup Profile Usage Information
remove-item -Path "C:\Documents and Settings\Administrator\Recent\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
remove-item -Path "C:\Documents and Settings\Administrator\Local Settings\Temp\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
#Checks License status before continuing; required for Sysprep to be successful
foreach ($item in (gwmi Win32_WindowsProductActivation))
{
if ($item.ActivationRequired -eq 0)
{
write-log "Windows is Licensed"
$licensed = $true
}
}
}
default
{
write-log "No OS Match found -- using default"
$OS = "Unknown"
#Cleanup Profile Usage Information
remove-item -Path "C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Recent\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
#Checks License status before continuing; required for Sysprep to be successful
foreach ($item in (gwmi SoftwareLicensingProduct))
{
if ($item.LicenseStatus -eq 1)
{
write-log "Windows is Licensed"
$licensed = $true
}
}
}
}
#Ensures that the service is started
start-service ec2config
#Removes Temporary Files
remove-item -Path "C:\Windows\Temp\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
#Removes old EC2Config Log
remove-item -Path "C:\Program Files\Amazon\Ec2ConfigService\Logs\Ec2ConfigLog.txt" -Force -Confirm:$false -ErrorAction SilentlyContinue
#Clears Start Menu Run History
foreach ($item in (Get-ChildItem -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist)){Clear-Item -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\$($item.PSChildName)\Count}
#Clears Explorer Run History
Clear-Item -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU #Returns error if no entries exist to clear
#Removes any previous Memory Dump files
remove-item -Path "C:\Windows\*.DMP" -Force -Confirm:$false -ErrorAction SilentlyContinue
remove-item -Path "C:\Windows\Minidump" -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue
#Clear IE history
RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 255
RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 1
RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 2
RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 8
RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 16
RunDll32.exe InetCpl.cpl,ClearMyTracksByProcess 32
#Pre-Compiles Queued .net Assemblies prior to Sysprep
start -wait C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ngen.exe -ArgumentList 'executequeueditems'
#Defragments the Drive
defrag c:
#Securely deletes files
write-log "Permanently erasing deleted files"
& 'C:\Program Files\Amazon\Ec2ConfigService\Scripts\sdelete.exe' -c -accepteula
del 'C:\Program Files\Amazon\Ec2ConfigService\Scripts\sdelete.exe'
write-log "Removing any mapped drives"
net use \\$RemoteIPAddress\C$ /delete /Y
#Removes any UserData Scripts
Remove-Item -Path "C:\Program Files\Amazon\Ec2ConfigService\Scripts\UserScript.bat" -Force -ErrorAction SilentlyContinue
Remove-Item -Path "C:\Program Files\Amazon\Ec2ConfigService\Scripts\UserScript.ps1" -Force -ErrorAction SilentlyContinue
#Resets desktop wallpaper
Start-Process "C:\Program Files\Amazon\Ec2ConfigService\Ec2ConfigServiceSettings.exe" -ArgumentList -resetwallpaper
#############################
### Run EC2Config Service ###
#Gets the content of EC2Config Config.xml and enables Password Generation, UserData and DynamicVolumeSize for next boot
$EC2SettingsFile="C:\Program Files\Amazon\Ec2ConfigService\Settings\Config.xml"
$xml = [xml](get-content $EC2SettingsFile)
$xmlElement = $xml.get_DocumentElement()
$xmlElementToModify = $xmlElement.Plugins
write-log "Setting Config.xml"
foreach ($element in $xmlElementToModify.Plugin)
{
write-log " $($element.name)"
if ($element.name -eq "Ec2SetPassword")
{
$element.State="Enabled"
}
elseif ($element.name -eq "Ec2HandleUserData")
{
$element.State="Enabled"
}
elseif ($element.name -eq "Ec2DynamicBootVolumeSize")
{
#Except for Windows 2003, enable dynamic Root Volume Sizing
if ($OS -ne "2003")
{
$element.State="Enabled"
}
}
write-log " $($element.State)"
}
$xml.Save($EC2SettingsFile)
#Gets the content of EC2Config BundleConfig.xml and enables the RDP Cert element so new cert is generated
$EC2SettingsFile="C:\Program Files\Amazon\Ec2ConfigService\Settings\BundleConfig.xml"
$xml = [xml](get-content $EC2SettingsFile)
$xmlElement = $xml.get_DocumentElement()
$xmlElementToModify = $xmlElement.Property
write-log "Setting BundleConfig.xml"
foreach ($element in $xmlElementToModify)
{
write-log " $($element.name)"
if ($element.name -eq "SetRDPCertificate")
{
#For Windows 2003 OS, generates a new RDP Certificate
if ($OS -eq "2003")
{
$element.Value="Yes"
}
}
elseif ($element.name -eq "SetPasswordAfterSysprep")
{
$element.Value="Yes"
}
write-log " $($element.Value)"
}
$xml.Save($EC2SettingsFile)
#Clear event logs
write-log "Clearing Event Logs"
Clear-EventLog Application
Clear-EventLog System
Clear-EventLog Security
#Checks licensed status prior to running Sysprep
if ($licensed -eq $true)
{
write-log "Starting Sysprep"
Set-ExecutionPolicy Restricted
Start-Process "C:\Program Files\Amazon\Ec2ConfigService\ec2config.exe" -ArgumentList -sysprep
}
else{write-log "Windows is NOT Licensed, unable to run Sysprep"}
}
#Adds the date/timestamp to write-log for logging
function write-log
{ param([string]$data)
$date = get-date -format "yyyyMMdd_hhmm:ss"
write-host "$date $data"
Out-File -InputObject "$date $data" -FilePath $LoggingFile -Append
}
#Determines the Status of Windows Updates that are being installed
function Get-WIAStatusValue($value)
{
switch -exact ($value)
{
0 {"NotStarted"}
1 {"InProgress"}
2 {"Succeeded"}
3 {"SucceededWithErrors"}
4 {"Failed"}
5 {"Aborted"}
}
}
#Patches the instance then Shuts Down
function patchInstance
{ Param([string]$Option)
$needsReboot = $false
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
write-log " - Searching for Updates"
switch ($Option)
{
0 #Finds all Updates
{
$SearchResult = $UpdateSearcher.Search("IsHidden=0 and IsInstalled=0")
}
1 #Finds only Required Updates
{
$SearchResult = $UpdateSearcher.Search("IsAssigned=1 and IsHidden=0 and IsInstalled=0")
}
default #Finds all Updates}
{
$SearchResult = $UpdateSearcher.Search("IsHidden=0 and IsInstalled=0")
}
}
write-log " - Found [$($SearchResult.Updates.count)] Updates to Download and install"
$i=0 #Initializes counter -- used to track number of update
foreach($Update in $SearchResult.Updates)
{
$i++ #Increments counter
# Add Update to Collection
$UpdatesCollection = New-Object -ComObject Microsoft.Update.UpdateColl
if ( $Update.EulaAccepted -eq 0 ) { $Update.AcceptEula() }
$UpdatesCollection.Add($Update) | out-null
# Download
write-log " + Downloading [$i of $($SearchResult.Updates.count)] $($Update.Title)"
$UpdatesDownloader = $UpdateSession.CreateUpdateDownloader()
$UpdatesDownloader.Updates = $UpdatesCollection
$DownloadResult = $UpdatesDownloader.Download()
$Message = " - Download {0}" -f (Get-WIAStatusValue $DownloadResult.ResultCode)
write-log $message
# Install
write-log " - Installing Update"
$UpdatesInstaller = $UpdateSession.CreateUpdateInstaller()
$UpdatesInstaller.Updates = $UpdatesCollection
$InstallResult = $UpdatesInstaller.Install()
$Message = " - Install {0}" -f (Get-WIAStatusValue $DownloadResult.ResultCode)
write-log $message
write-log
}
}
##################################
#START OF SCRIPT
##################################
# Starts log File
$LoggingFile = "C:\Program Files\Amazon\Ec2ConfigService\Logs\InstallUpdates_" + $patch + ".txt"
del $LoggingFile #Removes previous log file
$CurrentTime = Get-Date
write-log "STARTING: $CurrentTime"
#Validates parameters
switch ($patch)
{
0 {
write-log "########################"
write-log "###PACKAGING INSTANCE###"
write-log "########################"
EC2ConfigPackage
} #Prepares the instance for bundling into an AMI -- will shut down as part of EC2Config
1 #Patch the Instance, then Shut down
{
write-log "############################"
write-log "###INSTALLING ALL UPDATES###"
write-log "############################"
Start-Process "C:\Program Files\Amazon\Ec2ConfigService\Ec2ConfigServiceSettings.exe" -ArgumentList -resetwallpaper
#Clears any User Pinned Start Menu items
remove-item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
patchInstance 0 #Installs Required and Recommended Updates
Stop-Computer -Force #Turns off when done
}
2 #Patch the Instance, then Shut down
{
write-log "########################"
write-log "###INSTALLING UPDATES###"
write-log "########################"
Start-Process "C:\Program Files\Amazon\Ec2ConfigService\Ec2ConfigServiceSettings.exe" -ArgumentList -resetwallpaper
#Clears any User Pinned Start Menu items
remove-item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\*" -Force -Recurse -Confirm:$false -ErrorAction SilentlyContinue
patchInstance 1 #Installs only Required Updates
Stop-Computer -Force #Turns off when done
}
default
{
write-log "Must specify an valid option -- exiting"
write-log " 0 = Bundle"
write-log " 1 = Patch (Including Recommended Updates)"
write-log " 2 = Patch (Only Required Updates)"
return -1
}
}
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="generalize">
<component name="Microsoft-Windows-PnpSysprep" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
<DoNotCleanUpNonPresentDevices>true</DoNotCleanUpNonPresentDevices>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
<NetworkLocation>Other</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<BluetoothTaskbarIconEnabled>false</BluetoothTaskbarIconEnabled>
<TimeZone>UTC</TimeZone>
<RegisteredOrganization>Amazon.com</RegisteredOrganization>
<RegisteredOwner>Amazon</RegisteredOwner>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- The ComputerName parameter can be used to specify the computer name. Note:: This will cause the machine name to be changed twice; initial by sysprep, then again with the new parameter.
The second name change will break the SQL Server name, which is corrected in the 'scripts/SysprepSpecializePhase.cmd' file, so will need to be manually updated and MSSQLService restarted.
<ComputerName>*</ComputerName>
-->
<CopyProfile>true</CopyProfile>
<RegisteredOrganization>Amazon</RegisteredOrganization>
<TimeZone>UTC</TimeZone>
</component>
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RunSynchronous>
<RunSynchronousCommand wcm:action="add">
<Order>1</Order>
<Path>net user root /ACTIVE:YES /LOGONPASSWORDCHG:NO /EXPIRES:NEVER /PASSWORDREQ:YES</Path>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>2</Order>
<Path>"C:\Program Files\Amazon\Ec2ConfigService\ScramblePassword.exe" -u root</Path>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>3</Order>
<Path>sc config ec2config start= auto</Path>
</RunSynchronousCommand>
<RunSynchronousCommand wcm:action="add">
<Order>4</Order>
<Path>"C:\Program Files\Amazon\Ec2ConfigService\Scripts\SysprepSpecializePhase.cmd"</Path>
</RunSynchronousCommand>
</RunSynchronous>
</component>
<component name="Microsoft-Windows-ServerManager-SvrMgrNc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DoNotOpenServerManagerAtLogon>true</DoNotOpenServerManagerAtLogon>
</component>
</settings>
<cpi:offlineImage cpi:source="wim:c:/wimbuild/server2008r2x64/install.wim#Windows Server 2008 R2 SERVERDATACENTER" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment