Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
# Windows AMIs don't have WinRM enabled by default -- this script will enable WinRM
# AND install 7-zip, curl and .NET 4 if its missing.
# Then use the EC2 tools to create a new AMI from the result, and you have a system
# that will execute user-data as a PowerShell script after the instance fires up!
# This has been tested on Windows 2008 SP2 64bits AMIs provided by Amazon
# Inject this as user-data of a Windows 2008 AMI, like this (edit the adminPassword to your needs):
# <powershell>
# Set-ExecutionPolicy Unrestricted
# icm $executioncontext.InvokeCommand.NewScriptBlock((New-Object Net.WebClient).DownloadString('')) -ArgumentList "adminPassword"
# </powershell>
Start-Transcript -Path 'c:\bootstrap-transcript.txt' -Force
Set-StrictMode -Version Latest
Set-ExecutionPolicy Unrestricted
$log = 'c:\Bootstrap.txt'
while (($AdminPassword -eq $null) -or ($AdminPassword -eq ''))
$AdminPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((Read-Host "Enter a non-null / non-empty Administrator password" -AsSecureString)))
$systemPath = [Environment]::GetFolderPath([Environment+SpecialFolder]::System)
$sysNative = [IO.Path]::Combine($env:windir, "sysnative")
$Is32Bit = (($Env:PROCESSOR_ARCHITECTURE -eq 'x86') -and ($Env:PROCESSOR_ARCHITEW6432 -eq $null))
Add-Content $log -value "Is 32-bit [$Is32Bit]"
$coreEditions = @(0x0c,0x27,0x0e,0x29,0x2a,0x0d,0x28,0x1d)
$IsCore = $coreEditions -contains (Get-WmiObject -Query "Select OperatingSystemSKU from Win32_OperatingSystem" | Select -ExpandProperty OperatingSystemSKU)
Add-Content $log -value "Is Core [$IsCore]"
# move to home, PS is incredibly complex :)
Set-Location -Path $Env:USERPROFILE
[Environment]::CurrentDirectory=(Get-Location -PSProvider FileSystem).ProviderPath
#change admin password
net user Administrator $AdminPassword
Add-Content $log -value "Changed Administrator password"
$client = new-object System.Net.WebClient 4
if ((Test-Path "${Env:windir}\Microsoft.NET\Framework\v4.0.30319") -eq $false)
$netUrl = if ($IsCore) {'' } `
else { '' }
$client.DownloadFile( $netUrl, 'dotNetFx40_Full.exe')
Start-Process -FilePath 'C:\Users\Administrator\dotNetFx40_Full.exe' -ArgumentList '/norestart /q /ChainingPackage ADMINDEPLOYMENT' -Wait -NoNewWindow
del dotNetFx40_Full.exe
Add-Content $log -value "Found that .NET4 was not installed and downloaded / installed"
#configure powershell to use .net 4
$config = @'
<?xml version="1.0" encoding="utf-8" ?>
<!-- -->
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
<supportedRuntime version="v2.0.50727" />
if (Test-Path "${Env:windir}\SysWOW64\WindowsPowerShell\v1.0\powershell.exe")
$config | Set-Content "${Env:windir}\SysWOW64\WindowsPowerShell\v1.0\powershell.exe.config"
Add-Content $log -value "Configured 32-bit Powershell on x64 OS to use .NET 4"
if (Test-Path "${Env:windir}\system32\WindowsPowerShell\v1.0\powershell.exe")
$config | Set-Content "${Env:windir}\system32\WindowsPowerShell\v1.0\powershell.exe.config"
Add-Content $log -value "Configured host OS specific Powershell at ${Env:windir}\system32\ to use .NET 4"
#check winrm id, if it's not valid and LocalAccountTokenFilterPolicy isn't established, do it
$id = &winrm id
if (($id -eq $null) -and (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -name LocalAccountTokenFilterPolicy -ErrorAction SilentlyContinue) -eq $null)
New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System -name LocalAccountTokenFilterPolicy -value 1 -propertyType dword
Add-Content $log -value "Added LocalAccountTokenFilterPolicy since winrm id could not be executed"
#enable powershell servermanager cmdlets (only for 2008 r2 + above)
if ($IsCore)
DISM /Online /Enable-Feature /FeatureName:MicrosoftWindowsPowerShell /FeatureName:ServerManager-PSH-Cmdlets /FeatureName:BestPractices-PSH-Cmdlets
Add-Content $log -value "Enabled ServerManager and BestPractices Cmdlets"
#enable .NET flavors - on server core only -- errors on regular 2008
DISM /Online /Enable-Feature /FeatureName:NetFx2-ServerCore /FeatureName:NetFx2-ServerCore-WOW64 /FeatureName:NetFx3-ServerCore /FeatureName:NetFx3-ServerCore-WOW64
Add-Content $log -value "Enabled .NET frameworks 2 and 3 for x86 and x64"
$7zUri = if ($Is32Bit) { '' } `
else { '' }
$client.DownloadFile( $7zUri, '7z922.msi')
Start-Process -FilePath "msiexec.exe" -ArgumentList '/i 7z922.msi /norestart /q INSTALLDIR="c:\program files\7-zip"' -Wait
SetX Path "${Env:Path};C:\Program Files\7-zip" /m
$Env:Path += ';C:\Program Files\7-Zip'
del 7z922.msi
Add-Content $log -value "Installed 7-zip from $7zUri and updated path"
#vc 2010 redstributable
$vcredist = if ($Is32Bit) { ''} `
else { '' }
$client.DownloadFile( $vcredist, 'vcredist.exe')
Start-Process -FilePath 'C:\Users\Administrator\vcredist.exe' -ArgumentList '/norestart /q' -Wait
del vcredist.exe
Add-Content $log -value "Installed VC++ 2010 Redistributable from $vcredist and updated path"
#vc 2008 redstributable
$vcredist = if ($Is32Bit) { ''} `
else { '' }
$client.DownloadFile( $vcredist, 'vcredist.exe')
Start-Process -FilePath 'C:\Users\Administrator\vcredist.exe' -ArgumentList '/norestart /q' -Wait
del vcredist.exe
Add-Content $log -value "Installed VC++ 2008 Redistributable from $vcredist and updated path"
$curlUri = if ($Is32Bit) { '' } `
else { '' }
$client.DownloadFile( $curlUri, '')
&7z e `-o`"c:\program files\curl`"
if ($Is32Bit)
$client.DownloadFile( '', '')
&7z e `-o`"c:\program files\curl`"
SetX Path "${Env:Path};C:\Program Files\Curl" /m
$Env:Path += ';C:\Program Files\Curl'
Add-Content $log -value "Installed Curl from $curlUri and updated path"
& 'C:\Program Files\Curl\curl.exe' -# -G -k -L -o 2>&1 > "$log"
& 'C:\Program Files\Curl\curl.exe' -# -G -k -L -o 2>&1 > "$log"
Get-ChildItem -Filter vim73*.zip |
% { &7z x `"$($_.FullName)`"; del $_.FullName; }
SetX Path "${Env:Path};C:\Program Files\Vim" /m
$Env:Path += ';C:\Program Files\Vim'
Move-Item .\vim\vim73 -Destination "${Env:ProgramFiles}\Vim"
Add-Content $log -value "Installed Vim text editor and updated path"
#chocolatey - standard one line installer doesn't work on Core b/c Shell.Application can't unzip
if (-not $IsCore)
Invoke-Expression ((new-object net.webclient).DownloadString(''))
#[Environment]::SetEnvironmentVariable('ChocolateyInstall', 'c:\nuget', [System.EnvironmentVariableTarget]::User)
#if (![System.IO.Directory]::Exists('c:\nuget')) {[System.IO.Directory]::CreateDirectory('c:\nuget')}
$tempDir = Join-Path $env:TEMP "chocInstall"
if (![System.IO.Directory]::Exists($tempDir)) {[System.IO.Directory]::CreateDirectory($tempDir)}
$file = Join-Path $tempDir ""
$client.DownloadFile("", $file)
&7z x $file `-o`"$tempDir`"
Add-Content $log -value 'Extracted Chocolatey'
$chocInstallPS1 = Join-Path (Join-Path $tempDir 'tools') 'chocolateyInstall.ps1'
& $chocInstallPS1
Add-Content $log -value 'Installed Chocolatey / Verifying Paths'
Add-Content $log -value "Installed Chocolatey"
# install puppet
& 'C:\Program Files\Curl\curl.exe' -# -G -k -L -o puppet-3.2.4.msi 2>&1 > "$log"
Start-Process -FilePath "msiexec.exe" -ArgumentList '/qn /passive /i puppet-3.2.4.msi /norestart' -Wait
SetX Path "${Env:Path};C:\Program Files\Puppet Labs\Puppet\bin" /m
&sc.exe config puppet start= demand
Add-Content $log -value "Installed Puppet"
&winrm quickconfig `-q
&winrm set winrm/config/client/auth '@{Basic="true"}'
&winrm set winrm/config/service/auth '@{Basic="true"}'
&winrm set winrm/config/service '@{AllowUnencrypted="true"}'
Add-Content $log -value "Ran quickconfig for winrm"
&netsh firewall set portopening tcp 445 smb enable
Add-Content $log -value "Ran firewall config to allow incoming smb/tcp"
#run SMRemoting script to enable event log management, etc - available only on R2
$remotingScript = [IO.Path]::Combine($systemPath, 'Configure-SMRemoting.ps1')
if (-not (Test-Path $remotingScript)) { $remotingScript = [IO.Path]::Combine($sysNative, 'Configure-SMRemoting.ps1') }
Add-Content $log -value "Found Remoting Script: [$(Test-Path $remotingScript)] at $remotingScript"
if (Test-Path $remotingScript)
. $remotingScript -force -enable
Add-Content $log -value 'Ran Configure-SMRemoting.ps1'
#wait a bit, it's windows after all
Start-Sleep -m 10000
#Write-Host "Press any key to reboot and finish image configuration"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.