Created
September 4, 2017 00:21
-
-
Save aidansteele/4ff4995ebbb8174714090b3e2251fc00 to your computer and use it in GitHub Desktop.
Revisions
-
aidansteele created this gist
Sep 4, 2017 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,477 @@ --- schemaVersion: '0.3' description: Updates a Microsoft Windows AMI. By default it will install all Windows updates, Amazon software, and Amazon drivers. It will then sysprep and create a new AMI. Supports Windows Server 2008 R2 and greater. assumeRole: "{{ AutomationAssumeRole }}" parameters: SourceAmiId: type: String description: "(Required) The source Amazon Machine Image ID." IamInstanceProfileName: type: String description: "(Required) The name of the role that enables Systems Manager to manage the instance." default: ManagedInstanceProfile AutomationAssumeRole: type: String description: "(Required) The ARN of the role that allows Automation to perform the actions on your behalf." default: arn:aws:iam::{{global:ACCOUNT_ID}}:role/AutomationServiceRole TargetAmiName: type: String description: "(Optional) The name of the new AMI that will be created. Default is a system-generated string including the source AMI id, and the creation time and date." default: UpdateWindowsAmi_from_{{SourceAmiId}}_on_{{global:DATE_TIME}} InstanceType: type: String description: "(Optional) Type of instance to launch as the workspace host. Instance types vary by region. Default is t2.medium." default: t2.medium IncludeKbs: type: String description: "(Optional) Specify one or more Microsoft Knowledge Base (KB) article IDs to include. You can install multiple IDs using comma-separated values. Valid formats: KB9876543 or 9876543." default: '' ExcludeKbs: type: String description: "(Optional) Specify one or more Microsoft Knowledge Base (KB) article IDs to exclude. You can exclude multiple IDs using comma-separated values. Valid formats: KB9876543 or 9876543." default: '' Categories: type: String description: "(Optional) Specify one or more update categories. You can filter categories using comma-separated values. Options: Application, Connectors, CriticalUpdates, DefinitionUpdates, DeveloperKits, Drivers, FeaturePacks, Guidance, Microsoft, SecurityUpdates, ServicePacks, Tools, UpdateRollups, Updates. Valid formats include a single entry, for example: CriticalUpdates. Or you can specify a comma separated list: CriticalUpdates,SecurityUpdates. NOTE: There cannot be any spaces around the commas." default: '' SeverityLevels: type: String description: "(Optional) Specify one or more MSRC severity levels associated with an update. You can filter severity levels using comma-separated values. By default patches for all security levels are selected. If value supplied, the update list is filtered by those values. Options: Critical, Important, Low, Moderate or Unspecified. Valid formats include a single entry, for example: Critical. Or, you can specify a comma separated list: Critical,Important,Low." default: '' PublishedDaysOld: type: String default: '' description: "(Optional) Specify the amount of days old the updates must be from the published date. For example, if 10 is specified, any updates that were found during the Windows Update search that have been published 10 or more days ago will be returned." PublishedDateAfter: type: String default: '' description: "(Optional) Specify the date that the updates should be published after. For example, if 01/01/2017 is specified, any updates that were found during the Windows Update search that have been published on or after 01/01/2017 will be returned." PublishedDateBefore: type: String default: '' description: "(Optional) Specify the date that the updates should be published before. For example, if 01/01/2017 is specified, any updates that were found during the Windows Update search that have been published on or before 01/01/2017 will be returned." PreUpdateScript: type: String description: "(Optional) A script provided as a string. It will execute prior to installing OS updates." default: '' PostUpdateScript: type: String description: "(Optional) A script provided as a string. It will execute after installing OS updates." default: '' mainSteps: - name: LaunchInstance action: aws:runInstances timeoutSeconds: 1800 maxAttempts: 3 onFailure: Abort inputs: ImageId: "{{ SourceAmiId }}" InstanceType: "{{ InstanceType }}" MinInstanceCount: 1 MaxInstanceCount: 1 IamInstanceProfileName: "{{ IamInstanceProfileName }}" - name: OSCompatibilityCheck action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 7200 inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{LaunchInstance.InstanceIds}}" Parameters: executionTimeout: '7200' commands: [System.Version]$osversion = [System.Environment]::OSVersion.Version if(($osversion.Major -eq 6 -and $osversion.Minor -ge 1) -or ($osversion.Major -ge 10)) { Write-Host 'This OS is supported for use with this automation document.' } else { Write-Host 'This OS is not supported for use with this automation document.' exit -1 } - name: RunPreUpdateScript action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 1800 inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{ LaunchInstance.InstanceIds }}" Parameters: commands: "{{ PreUpdateScript }}" - name: UpdateEC2Config action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 7200 inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{ LaunchInstance.InstanceIds }}" Parameters: commands: $moduleName = 'AWSUpdateWindowsInstance' $zipFilename = 'AWSUpdateWindowsInstance.zip' $tempPath = $env:TEMP $moduleDirectory = Join-Path $tempPath -ChildPath $moduleName $moduleZipFilePath = Join-Path $tempPath -ChildPath $zipFilename $moduleManifestPath = Join-Path $moduleDirectory -ChildPath ('{0}.psd1' -f $moduleName) $id = '{{automation:EXECUTION_ID}}' $regionUrl = 'http://169.254.169.254/latest/dynamic/instance-identity/document/region' $metadata = (New-Object Net.WebClient).DownloadString($regionUrl) $region = (ConvertFrom-JSON $metadata).region function Main { Clear-WindowsUpdateModule Get-WindowsUpdateModule Expand-WindowsUpdateModule if ([Environment]::OSVersion.Version.Major -ge 10) { Invoke-UpdateEC2Launch } else { Invoke-UpdateEC2Config } } function Clear-WindowsUpdateModule { try { if (Test-Path $moduleDirectory) { Remove-Item $moduleDirectory -Force -Recurse } if (Test-Path $moduleZipFilePath) { Remove-Item $moduleZipFilePath -Force } } catch { Write-Host "Cleaning Windows update module resulted in error: $($_)" } } function Get-WindowsUpdateModule { try { if ($region.StartsWith('cn-')) { $s3Location = 'https://s3.{0}.amazonaws.com.cn/aws-windows-downloads-{0}/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } elseif($region.StartsWith('us-gov-')) { $s3Location = 'https://s3-fips-{0}.amazonaws.com/aws-windows-downloads-{0}/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } elseif($region -eq 'us-east-1') { $s3Location = 'https://s3.amazonaws.com/aws-windows-downloads-{0}/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } else { $s3Location = 'https://aws-windows-downloads-{0}.s3.amazonaws.com/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } $moduleS3Location = $s3Location -f $region, $zipFilename $moduleLocalPath = Join-Path $tempPath -ChildPath $zipFilename (New-Object Net.WebClient).DownloadFile($moduleS3Location, $moduleLocalPath) $fileStream = New-Object System.IO.FileStream($moduleLocalPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) $sha256 = [System.Security.Cryptography.HashAlgorithm]::Create('System.Security.Cryptography.SHA256CryptoServiceProvider') $currentHash = [System.BitConverter]::ToString($sha256.ComputeHash($fileStream), 0).Replace('-', '').ToLowerInvariant() $sha256.Dispose() $fileStream.Dispose() if ($currentHash -ne 'B32D397AAA312E5EB0B2E0E0BC7146FB716AED3867A73FA650E0F222DF1079AE') { Write-Host 'The SHA hash of the module does not match the expected value.' Exit -1 } } catch { Write-Host ('Error encountered while getting the module: {0}.' -f $_.Exception.Message) Exit -1 } } function Expand-WindowsUpdateModule { try { [System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null $zip = [System.IO.Compression.ZipFile]::OpenRead($moduleZipFilePath) foreach ($item in $zip.Entries) { $extractPath = Join-Path $tempPath -ChildPath $item.FullName if ($item.Length -eq 0) { if (-not (Test-Path $extractPath)) { New-Item $extractPath -ItemType Directory | Out-Null } } else { $parentPath = Split-Path $extractPath if (-not (Test-Path $parentPath)) { New-Item $parentPath -ItemType Directory | Out-Null } [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item, $extractPath, $true) } } } catch { Write-Host ('Error encountered when extracting module file: {0}.' -f $_.Exception.Message) Exit -1 } finally { $zip.Dispose() } } function Invoke-UpdateEC2Config { Import-Module $moduleManifestPath $command = "Install-AwsUwiEC2Config -Region $region" if($id) { $command += " -Id $($id)"} Invoke-Expression $command } function Invoke-UpdateEC2Launch { Import-Module $moduleManifestPath $command = 'Install-AwsUwiEC2Launch' if($id) { $command += " -Id $($id)" } Invoke-Expression $command } Main - name: UpdateSSMAgent action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 600 inputs: DocumentName: AWS-UpdateSSMAgent InstanceIds: - "{{ LaunchInstance.InstanceIds }}" - name: UpdateAWSPVDriver action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 600 inputs: DocumentName: AWS-ConfigureAWSPackage InstanceIds: - "{{LaunchInstance.InstanceIds}}" Parameters: name: AWSPVDriver action: Install - name: UpdateAWSEnaNetworkDriver action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 600 inputs: DocumentName: AWS-ConfigureAWSPackage InstanceIds: - "{{LaunchInstance.InstanceIds}}" Parameters: name: AwsEnaNetworkDriver action: Install - name: InstallWindowsUpdates action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 14400 inputs: DocumentName: AWS-InstallWindowsUpdates InstanceIds: - "{{ LaunchInstance.InstanceIds }}" Parameters: Action: Install IncludeKbs: "{{ IncludeKbs }}" ExcludeKbs: "{{ ExcludeKbs }}" Categories: "{{ Categories }}" SeverityLevels: "{{ SeverityLevels }}" PublishedDaysOld: "{{ PublishedDaysOld }}" PublishedDateAfter: "{{ PublishedDateAfter }}" PublishedDateBefore: "{{ PublishedDateBefore }}" - name: RunPostUpdateScript action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 1800 inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{ LaunchInstance.InstanceIds }}" Parameters: commands: "{{ PostUpdateScript }}" - name: RunSysprepGeneralize action: aws:runCommand maxAttempts: 3 onFailure: Abort timeoutSeconds: 7200 inputs: DocumentName: AWS-RunPowerShellScript InstanceIds: - "{{ LaunchInstance.InstanceIds }}" Parameters: commands: | $moduleName = 'AWSUpdateWindowsInstance' $zipFilename = 'AWSUpdateWindowsInstance.zip' $tempPath = $env:TEMP $moduleDirectory = Join-Path $tempPath -ChildPath $moduleName $moduleZipFilePath = Join-Path $tempPath -ChildPath $zipFilename $moduleManifestPath = Join-Path $moduleDirectory -ChildPath ('{0}.psd1' -f $moduleName) $id = '{{automation:EXECUTION_ID}}' function Main { Test-PreCondition Clear-WindowsUpdateModule Get-WindowsUpdateModule Expand-WindowsUpdateModule Invoke-RunSysprep } function Test-PreCondition { $osversion = [Environment]::OSVersion.Version if ($osversion.Major -le 5) { Write-Host 'This document is not supported on Windows Server 2003 or earlier.' Exit -1 } if ($osversion.Version -ge '10.0') { $sku = (Get-CimInstance -ClassName Win32_OperatingSystem).OperatingSystemSKU if ($sku -eq 143 -or $sku -eq 144) { Write-Host 'This document is not supported on Windows 2016 Nano Server.' Exit -1 } } $ssmAgentService = Get-ItemProperty 'HKLM:SYSTEM\\CurrentControlSet\\Services\\AmazonSSMAgent\\' -ErrorAction SilentlyContinue if (-not $ssmAgentService -or $ssmAgentService.Version -lt '2.0.533.0') { Write-Host 'This document is not supported with SSM Agent version less than 2.0.533.0.' exit -1 } } function Clear-WindowsUpdateModule { try { if (Test-Path $moduleDirectory) { Remove-Item $moduleDirectory -Force -Recurse } if (Test-Path $moduleZipFilePath) { Remove-Item $moduleZipFilePath -Force } } catch { Write-Host "Cleaning Windows update module resulted in error: $($_)" } } function Get-WindowsUpdateModule { try { $region = $env:AWS_SSM_REGION_NAME if ($region.StartsWith('cn-')) { $s3Location = 'https://s3.{0}.amazonaws.com.cn/aws-windows-downloads-{0}/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } elseif($region.StartsWith('us-gov-')) { $s3Location = 'https://s3-fips-{0}.amazonaws.com/aws-windows-downloads-{0}/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } elseif($region -eq 'us-east-1') { $s3Location = 'https://s3.amazonaws.com/aws-windows-downloads-{0}/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } else { $s3Location = 'https://aws-windows-downloads-{0}.s3.amazonaws.com/PSModules/AWSUpdateWindowsInstance/Latest/{1}' } $moduleS3Location = $s3Location -f $region, $zipFilename $moduleLocalPath = Join-Path $tempPath -ChildPath $zipFilename (New-Object Net.WebClient).DownloadFile($moduleS3Location, $moduleLocalPath) $fileStream = New-Object System.IO.FileStream($moduleLocalPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) $sha256 = [System.Security.Cryptography.HashAlgorithm]::Create('System.Security.Cryptography.SHA256CryptoServiceProvider') $currentHash = [System.BitConverter]::ToString($sha256.ComputeHash($fileStream), 0).Replace('-', '').ToLowerInvariant() $sha256.Dispose() $fileStream.Dispose() if ($currentHash -ne 'B32D397AAA312E5EB0B2E0E0BC7146FB716AED3867A73FA650E0F222DF1079AE') { Write-Host 'The SHA hash of the module does not match the expected value.' Exit -1 } } catch { Write-Host ('Error encountered while getting the module: {0}.' -f $_.Exception.Message) Exit -1 } } function Expand-WindowsUpdateModule { try { [System.Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null $zip = [System.IO.Compression.ZipFile]::OpenRead($moduleZipFilePath) foreach ($item in $zip.Entries) { $extractPath = Join-Path $tempPath -ChildPath $item.FullName if ($item.Length -eq 0) { if (-not (Test-Path $extractPath)) { New-Item $extractPath -ItemType Directory | Out-Null } } else { $parentPath = Split-Path $extractPath if (-not (Test-Path $parentPath)) { New-Item $parentPath -ItemType Directory | Out-Null } [System.IO.Compression.ZipFileExtensions]::ExtractToFile($item, $extractPath, $true) } } } catch { Write-Host ('Error encountered when extracting module file: {0}.' -f $_.Exception.Message) Exit -1 } finally { $zip.Dispose() } } function Invoke-RunSysprep { Import-Module $moduleManifestPath $command = 'Start-AwsUwiSysprep' if($id) { $command += " -Id $($id)"} Invoke-Expression $command } Main - name: StopInstance action: aws:changeInstanceState maxAttempts: 3 timeoutSeconds: 7200 onFailure: Abort inputs: InstanceIds: - "{{ LaunchInstance.InstanceIds }}" CheckStateOnly: false DesiredState: stopped - name: CreateImage action: aws:createImage maxAttempts: 3 onFailure: Abort inputs: InstanceId: "{{ LaunchInstance.InstanceIds }}" ImageName: "{{ TargetAmiName }}" NoReboot: true ImageDescription: Test CreateImage Description - name: TerminateInstance action: aws:changeInstanceState maxAttempts: 3 onFailure: Abort inputs: InstanceIds: - "{{ LaunchInstance.InstanceIds }}" DesiredState: terminated outputs: - CreateImage.ImageId