Set-ExecutionPolicy Unrestricted -Force -Scope CurrentUser [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $sshdUser = "sshd_service" $tmp = "$env:Temp\mu-userdata" mkdir $tmp $logfile = "c:/Mu-Bootstrap-$([Environment]::UserName).log" $basedir = 'c:/bin' $cygwin_dir = "$basedir/cygwin" $username = (whoami).Split('\')[1] $WebClient = New-Object System.Net.WebClient $awsmeta = "http://169.254.169.254/latest" $pydir = 'c:\bin\python\python27' $pyv = '2.7.14' $env:Path += ";$pydir\Scripts;$pydir" function log { Write-Host $args Add-Content "c:/Mu-Bootstrap-$([Environment]::UserName).log" "$(Get-Date -f MM-dd-yyyy_HH:mm:ss) $args" Add-Content "c:/Mu-Bootstrap-GLOBAL.log" "$(Get-Date -f MM-dd-yyyy_HH:mm:ss) $args" } function fetchSecret([string]$file){ log "Fetching s3://<%= MU.adminBucketName %>/$file to $tmp/$file" aws.cmd --region $region s3 cp s3://<%= MU.adminBucketName %>/$file $tmp/$file } function importCert([string]$cert, [string]$store){ fetchSecret($cert) if(!(Test-Path "$tmp/$cert")){ return $null } # XXX guard better (check thumbprint & CN) if($store -ne "Root"){ Remove-Item -Path Cert:/LocalMachine/$store/* -Force -Recurse } if($cert -Match ".pfx$"){ return Import-PfxCertificate -FilePath $tmp/$cert -CertStoreLocation Cert:\LocalMachine\$store } else { return Import-Certificate -FilePath $tmp/$cert -CertStoreLocation Cert:\LocalMachine\$store } Remove-Item -Force "$tmp/$cert" } log "- Invoked as $([Environment]::UserName) (system started at $(Get-CimInstance -ClassName win32_operatingsystem | select lastbootuptime)) -" <% if !$mu.skipApplyUpdates %> If (!(Test-Path "c:/mu-installer-ran-updates")){ Stop-Service -ErrorAction SilentlyContinue sshd } <% end %> <% if $mu.platform != "win2k16" %> If ([Environment]::OSVersion.Version.Major -lt 10) { If ("$($myInvocation.MyCommand.Path)" -ne "$tmp/realuserdata_stripped.ps1"){ $Error.Clear() Invoke-WebRequest -Uri "$awsmeta/user-data" -OutFile $tmp/realuserdata.ps1 while($Error.count -gt 0){ $Error.Clear() log "Failed to retrieve current userdata from $awsmeta/user-data, waiting 15s and retrying" sleep 15 Invoke-WebRequest -Uri "$awsmeta/user-data" -OutFile $tmp/realuserdata.ps1 } Get-Content $tmp/realuserdata.ps1 | Select-String -pattern '^#','^<' -notmatch | Set-Content $tmp/realuserdata_stripped.ps1 If (Compare-Object (Get-Content $myInvocation.MyCommand.Path) (Get-Content $tmp/realuserdata_stripped.ps1)){ log "Invoking $tmp/realuserdata.ps1 in lieu of $($myInvocation.MyCommand.Path)" Invoke-Expression $tmp/realuserdata_stripped.ps1 exit } } } <% end %> $admin_username = (Get-WmiObject -Query 'Select * from Win32_UserAccount Where (LocalAccount=True and SID like "%-500")').name log "Local admin: $admin_username" Add-Type -Assembly System.Web $password = [Web.Security.Membership]::GeneratePassword(15,2) If (!(Test-Path $basedir)){ mkdir $basedir } <% if $mu.platform != "win2k16" %> If ([Environment]::OSVersion.Version.Major -lt 10) { If (!(Get-ScheduledTask -TaskName 'run-userdata')){ log "Adding run-userdata scheduled task (user NT AUTHORITY\SYSTEM)" Invoke-WebRequest -Uri "https://s3.amazonaws.com/cloudamatic/run-userdata_scheduledtask.xml" -OutFile $tmp/run-userdata_scheduledtask.xml Register-ScheduledTask -Xml (Get-Content "$tmp/run-userdata_scheduledtask.xml" | out-string) -TaskName 'run-userdata' -Force -User ".\$admin_username" } } <% end %> If (!(Test-Path "$pydir\python.exe")){ If (!(Test-Path $tmp\python-$pyv.msi)){ log "Downloading Python installer" $WebClient.DownloadFile("https://www.python.org/ftp/python/$pyv/python-$pyv.msi","$tmp/python-$pyv.msi") } log "Running Python installer" (Start-Process -FilePath msiexec -ArgumentList "/i $tmp\python-$pyv.msi /qn ALLUSERS=1 TARGETDIR=$pydir" -Wait -Passthru).ExitCode } If (!(Test-Path "$pydir\Scripts\aws.cmd")){ If (!(Test-Path $tmp/get-pip.py)){ log "Downloading get-pip.py" $WebClient.DownloadFile("https://bootstrap.pypa.io/get-pip.py","$tmp/get-pip.py") } python $tmp/get-pip.py log "Running pip install awscli" pip install awscli } function removeChef($location){ $install_chef = $false $my_chef = (Get-ItemProperty $location | Where-Object {$_.DisplayName -like "chef client*"}).DisplayName if ($my_chef) { if ($my_chef -match '<%= MU.chefVersion %>'.split('-')[0]) { $install_chef = $false } else{ log "Uninstalling Chef" $uninstall_string = (Get-ItemProperty $location | Where-Object {$_.DisplayName -like "chef client*"}).UninstallString $uninstall_string = ($uninstall_string -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X","").Trim() $($uninstall_string -Replace '[\s\t]+', ' ').Split() | ForEach { log "msiexec.exe /X $_ /gn" start-process "msiexec.exe" -arg "/X $_ /qn" -Wait } $install_chef = $true } } return $install_chef } If (!(Test-Path "c:\opscode\chef\embedded\bin\ruby.exe")){ $install_chef = $true } else { if (removeChef("HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*")){ $install_chef = $true } elseif (removeChef("HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*")) { $install_chef = $true } else { $install_chef = $false } } If ($install_chef){ log "Installing Chef <%= MU.chefVersion %>" If (!(Test-Path $env:Temp/chef-installer-<%= MU.chefVersion %>.msi)){ log "Downloading Chef installer" $WebClient.DownloadFile("https://www.chef.io/chef/download?p=windows&pv=2012&m=x86_64&v=<%= MU.chefVersion %>","$env:Temp/chef-installer-<%= MU.chefVersion %>.msi") } log "Running Chef installer" (Start-Process -FilePath msiexec -ArgumentList "/i $env:Temp\chef-installer-<%= MU.chefVersion %>.msi ALLUSERS=1 /le $env:Temp\chef-client-install.log /qn" -Wait -Passthru).ExitCode Set-Content "c:/mu_installed_chef" "yup" } $region=(New-Object System.Net.WebClient).DownloadString("$awsmeta/meta-data/placement/availability-zone") $region=$region.Substring(0,$region.Length-1) fetchSecret("<%= $mu.muID %>-secret") log "Encrypting Mu deploy secret" $deploy_secret = & "c:\opscode\chef\embedded\bin\ruby" -ropenssl -rbase64 -e "key = OpenSSL::PKey::RSA.new(Base64.urlsafe_decode64('<%= $mu.deployKey %>'))" -e "print Base64.urlsafe_encode64(key.public_encrypt(File.read('$tmp\<%= $mu.muID %>-secret')))" function callMomma([string]$act) { $params = @{mu_id='<%= $mu.muID %>';mu_resource_name='<%= $mu.resourceName %>';mu_resource_type='<%= $mu.resourceType %>';mu_instance_id="$awsid";mu_user='<%= $mu.muUser %>';mu_deploy_secret="$deploy_secret";$act="1"} log "Calling Momma Cat at https://<%= $mu.publicIP %>:2260 with $act" [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} $resp = Invoke-WebRequest -Uri https://<%= $mu.publicIP %>:2260 -Method POST -Body $params return $resp.Content } $awsid=(New-Object System.Net.WebClient).DownloadString("$awsmeta/meta-data/instance-id") $credstr = callMomma "mu_windows_admin_creds" $creds = $false $real_admin_user = $admin_username if($credstr){ $credparts = $credstr.Split(";", 2) $creds = New-Object System.Management.Automation.PSCredential($credparts[0], (ConvertTo-SecureString $credparts[1] -AsPlainText -Force)) if($admin_username -ne $credparts[0]){ if ((Get-WmiObject win32_computersystem).partofdomain -ne $true){ (([adsi]("WinNT://./$admin_username, user")).psbase.invoke("SetPassword", $credparts[1])) log "Changing local admin account from $admin_username to $($credparts[0])" ([adsi]("WinNT://./$admin_username, user")).psbase.rename($credparts[0]) $need_reboot = $TRUE $real_admin_user = $credparts[0] } ElseIf(!$admin_username){ $admin_username = $credparts[0] } } ElseIf($creds){ log "Setting $admin_username password" (([adsi]("WinNT://./$admin_username, user")).psbase.invoke("SetPassword", $credparts[1])) } } else { log "Failed to get credentials from Momma Cat for some reason $($credstr)" } If (!(Test-Path $tmp/PSWindowsUpdate.zip)){ If (!(Test-Path c:/Users/$admin_username/Documents/WindowsPowerShell/Modules)){ mkdir c:/Users/$admin_username/Documents/WindowsPowerShell/Modules } $WebClient.DownloadFile("https://s3.amazonaws.com/cloudamatic/PSWindowsUpdate.zip","$tmp/PSWindowsUpdate.zip") Add-Type -A 'System.IO.Compression.FileSystem' If (!(Test-Path c:/windows/System32/WindowsPowerShell/v1.0/Modules/PSWindowsUpdate)){ log "Extracting PSWindowsUpdate module to c:/windows/System32/WindowsPowerShell/v1.0/Modules" [IO.Compression.ZipFile]::ExtractToDirectory("$tmp/PSWindowsUpdate.zip", "c:/windows/System32/WindowsPowerShell/v1.0/Modules") } If (!(Test-Path c:/Users/$admin_username/Documents/WindowsPowerShell/Modules/PSWindowsUpdate)){ log "Extracting PSWindowsUpdate module to c:/Users/$admin_username/Documents/WindowsPowerShell" [IO.Compression.ZipFile]::ExtractToDirectory("$tmp/PSWindowsUpdate.zip", "c:/Users/$admin_username/Documents/WindowsPowerShell/Modules") } } <% if !$mu.skipApplyUpdates %> Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" -Name AUOptions -Value 3 If (!(Test-Path "c:/mu-installer-ran-updates")){ log "Applying Windows updates" Import-Module PSWindowsUpdate Get-WUInstall -AcceptAll -IgnoreReboot Start-Sleep -s 60 If (Test-Path "HKLM:/SOFTWARE/Microsoft/Windows/CurrentVersion/WindowsUpdate/Auto Update/RebootRequired"){ $need_reboot = $TRUE } } <% end %> if((Get-WURebootStatus -Silent) -eq $true){ log "Get-WURebootStatus says to reboot" $need_reboot = $TRUE } $muca = importCert "Mu_CA.pem" "Root" $myname = "<%= $mu.muID %>-<%= $mu.resourceName.upcase %>" $nodecert = importCert "$myname.pfx" "My" $thumb = $nodecert.Thumbprint # XXX guard this properly winrm delete winrm/config/Listener?Address=*+Transport=HTTPS winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"$myname`";CertificateThumbprint=`"$thumb`"}" $ingroup = net localgroup WinRMRemoteWMIUsers__ | Where-Object {$_ -eq $admin_username} if($ingroup -ne $admin_username){ net localgroup WinRMRemoteWMIUsers__ /add $admin_username } $winrmcert = importCert "$myname-winrm.crt" "TrustedPeople" Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name LocalAccountTokenFilterPolicy -Value 1 if($creds){ log "Enabling WinRM cert auth for $real_admin_user" New-Item -Path WSMan:\localhost\ClientCertificate -Subject "$real_admin_user@localhost" -URI * -Issuer $muca.Thumbprint -Force -Credential $creds } winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="8192"}' winrm set winrm/config '@{MaxTimeoutms="1800000"}' Restart-Service WinRm if ($need_reboot){ log "- REBOOT -" Restart-Computer -Force exit } if (!(Get-NetFirewallRule -DisplayName "Allow SSH" -ErrorAction SilentlyContinue)){ log "Opening port 22 in Windows Firewall" New-NetFirewallRule -DisplayName "Allow SSH" -Direction Inbound -LocalPort 22 -Protocol TCP -Action Allow } if (!(Get-NetFirewallRule -DisplayName "Allow WinRM SSL" -ErrorAction SilentlyContinue)){ New-NetFirewallRule -DisplayName "Allow WinRM SSL" -Direction Inbound -LocalPort 5986 -Protocol TCP -Action Allow } Add-Content c:/mu-installer-ran-updates "$(Get-Date -f MM-dd-yyyy_HH:mm:ss)" callMomma "mu_bootstrap" Set-Content "c:/mu_userdata_complete" "yup" Remove-Item -Recurse $tmp Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Undefined true