#* #* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. #* SPDX-License-Identifier: MIT-0 #* #* 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. #* #* 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. #* #------------------------------------------------------------------------------ # # Template: opsmgmt-operations-central-account.yaml # Purpose: Configures the patching operation in the Systems Manager central account # # #------------------------------------------------------------------------------ AWSTemplateFormatVersion: '2010-09-09' Description: AWS CloudFormation template to create a scheduled multi-account and multi-region Automation patching operation. #----------------------------------------------------------- # Parameters #----------------------------------------------------------- Parameters : CloudWatchEventRuleSchedule: Type: String Description: 'The cron or rate expression to use for the CloudWatch Event rule. For example: cron(0 09 ? * FRI *). Important: The time zone used is UTC.' ExecutionLogsBucket: Type: String Description: >- Enter the name of the execution logs bucket created using opsmgmt-central-account.yaml. ExecutionRoleName: Type: String Default: AWS-SystemsManager-AutomationExecutionRole Description: >- The Automation execution role to be assumed in target accounts during multi-account and multi-Region Automation patching operations. MaximumConcurrency: Type: String Default: 10% Description: >- Specify the number or percentage of targets on which to execute the task at the same time. You can specify a number, such as 10, or a percentage, such as 10%. The default value is 10%. MaximumErrors: Type: String Default: 10% Description: >- The number of errors that are allowed before the system stops initiating the automation on additional targets. You can specify either an absolute number of errors, for example 10, or a percentage of the target set, for example 10%. The default value is 10%. ResourceGroupName: Type: String Description: >- Enter a resource group that includes the resources you want to target. Important: The Resource Group name is case sensitive. Default: WebServers RunPatchBaselineOperation: Type: String Default: Scan AllowedValues: - Scan - Install Description: >- (Required) The update or configuration to perform on the instance. The Scan operation checks if patches specified in the patch baseline are installed on the instance. The Install operation installs patches missing from the baseline. RunPatchBaselineRebootOption: Type: String Default: RebootIfNeeded AllowedValues: - RebootIfNeeded - NoReboot Description: >- (Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan. RunPatchBaselineInstallOverrideList: Type: String Default: '' AllowedPattern: '(^$)|^https://.+$|^s3://([^/]+)/(.*?([^/]+))$' Description: >- (Optional) An https URL or an Amazon S3 path-style URL to the list of patches to be installed. This patch installation list overrides the patches specified by the default patch baseline. TargetAccounts: Type: String Description: >- Comma separated list of AWS Account Ids or AWS Organization OU Ids for the target account(s). TargetLocationMaxConcurrency: Type: String Default: '1' Description: >- Specify the number or percentage of locations (account-Region pairs) on which to execute the task at the same time. You can specify a number, such as 10, or a percentage, such as 10%. The default value is 1. TargetLocationMaxErrors: Type: String Default: '1' Description: >- Specify an error threshold which will stop the task after the task fails on a specific number or percentage of locations. You can specify either an absolute number of errors, for example 10, or a percentage of the locations, for example 10%. The default value is 1. TargetRegionIds: Type: String Description: >- Comma separated list of AWS Regions to target. For example: us-east-1,ap-south-1. Resources: #------------------------------------------------- # CloudWatch Event and permissions to invoke the Lambda function for multi-account/multi-Region Automation #------------------------------------------------- CWScheduleEventCFN: Type: AWS::Events::Rule Properties: Description: Cloudwatch Schedule Event Rule Created for Scheduled SSM MultiAccount Patching using lambda Name: Schedule-Trigger-for-Lambda-MultiAccountPatching ScheduleExpression: Ref: CloudWatchEventRuleSchedule State: ENABLED Targets: - Arn: Fn::GetAtt: - MultiAccountPatchingLambdaFunction - Arn Id: MultiAccountPatching PermissionForEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: Ref: MultiAccountPatchingLambdaFunction Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: Fn::GetAtt: - CWScheduleEventCFN - Arn #------------------------------------------------- # Lambda function and IAM role to make the multi-account/multi-Region Automation API call #------------------------------------------------- AWSLambdaSSMMultiAccountRole: Type: AWS::IAM::Role Properties: RoleName: AWS-Lambda-SSM-MultiAccountRole AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ssm.amazonaws.com - lambda.amazonaws.com Action: sts:AssumeRole Path: "/" Policies: - PolicyName: AWSLambdaSSMMultiAccountPolicy PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - ssm:StartAutomationExecution Resource: - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${AutomationDocumentMamrRunPatchBaseline}:$DEFAULT - Action: iam:PassRole Resource: Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:role/AWS-SystemsManager-AutomationAdministrationRole Effect: Allow - Action: logs:CreateLogGroup Resource: Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:* Effect: Allow - Action: - logs:CreateLogStream - logs:PutLogEvents Resource: Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/MultiAccountPatching:* Effect: Allow MultiAccountPatchingLambdaFunction: Type: AWS::Lambda::Function Properties: Code: ZipFile: !Sub | import boto3 import os import string import uuid client = boto3.client('ssm') def handler(event,context): TargetAccounts=os.environ['TargetAccounts'] b = str(TargetAccounts) TargetAccountsArray = b.split(",") TargetRegionIds=os.environ['TargetRegionIds'] b = str(TargetRegionIds) TargetRegionIdsArray = b.split(",") RunPatchBaselineOperation=os.environ['RunPatchBaselineOperation'] RunPatchBaselineRebootOption=os.environ['RunPatchBaselineRebootOption'] RunPatchBaselineInstallOverrideList=os.environ['RunPatchBaselineInstallOverrideList'] TargetLocationMaxConcurrency=os.environ['TargetLocationMaxConcurrency'] TargetLocationMaxErrors=os.environ['TargetLocationMaxErrors'] ExecutionRoleName=os.environ['ExecutionRoleName'] MasterAccountID=os.environ['MasterAccountID'] AutomationDocumentMamrRunPatchBaseline=os.environ['AutomationDocumentMamrRunPatchBaseline'] if len(RunPatchBaselineInstallOverrideList) > 0: response = client.start_automation_execution( DocumentName=f'{AutomationDocumentMamrRunPatchBaseline}', Parameters={ 'AutomationAssumeRole':[f'arn:aws:iam::{MasterAccountID}:role/AWS-SystemsManager-AutomationAdministrationRole'] , 'Operation' : [f'{RunPatchBaselineOperation}'] , 'RebootOption' : [f'{RunPatchBaselineRebootOption}'] , 'InstallOverrideList' : [f'{RunPatchBaselineInstallOverrideList}'] , 'SnapshotId' : [str(uuid.uuid4())] }, TargetLocations=[ { 'Accounts': TargetAccountsArray, 'Regions': TargetRegionIdsArray, 'TargetLocationMaxConcurrency': f'{TargetLocationMaxConcurrency}', 'TargetLocationMaxErrors': f'{TargetLocationMaxErrors}', 'ExecutionRoleName': f'{ExecutionRoleName}' } ] ) else: response = client.start_automation_execution( DocumentName=f'{AutomationDocumentMamrRunPatchBaseline}', Parameters={ 'AutomationAssumeRole':[f'arn:aws:iam::{MasterAccountID}:role/AWS-SystemsManager-AutomationAdministrationRole'] , 'Operation' : [f'{RunPatchBaselineOperation}'] , 'RebootOption' : [f'{RunPatchBaselineRebootOption}'] , 'SnapshotId' : [str(uuid.uuid4())] }, TargetLocations=[ { 'Accounts': TargetAccountsArray, 'Regions': TargetRegionIdsArray, 'TargetLocationMaxConcurrency': f'{TargetLocationMaxConcurrency}', 'TargetLocationMaxErrors': f'{TargetLocationMaxErrors}', 'ExecutionRoleName': f'{ExecutionRoleName}' } ] ) print(response) Environment: Variables: TargetAccounts: !Ref TargetAccounts TargetRegionIds: !Ref TargetRegionIds RunPatchBaselineOperation: !Ref RunPatchBaselineOperation RunPatchBaselineRebootOption: !Ref RunPatchBaselineRebootOption RunPatchBaselineInstallOverrideList: !Ref RunPatchBaselineInstallOverrideList TargetLocationMaxConcurrency: !Ref TargetLocationMaxConcurrency TargetLocationMaxErrors: !Ref TargetLocationMaxErrors ExecutionRoleName: !Ref ExecutionRoleName MasterAccountID: !Sub ${AWS::AccountId} AutomationDocumentMamrRunPatchBaseline: !Ref AutomationDocumentMamrRunPatchBaseline FunctionName: MultiAccountPatching Handler: index.handler Role: !GetAtt AWSLambdaSSMMultiAccountRole.Arn Runtime: python3.7 #------------------------------------------------- # Automation document to run AWS-RunPatchBaseline on target resources #------------------------------------------------- AutomationDocumentMamrRunPatchBaseline: Type: AWS::SSM::Document Properties: DocumentType: Automation Content: description: >- **Description** This document runs the Command document ```AWS-RunPatchBaseline``` on the specified instances. schemaVersion: '0.3' assumeRole: '{{ AutomationAssumeRole }}' parameters: AutomationAssumeRole: type: String description: The ARN of the Automation service role to assume. Operation: type: String default: Scan description: >- (Required) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline. RebootOption: type: String default: RebootIfNeeded description: >- (Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan. InstallOverrideList: type: String default: "" description: >- (Optional) An https URL or an Amazon S3 path-style URL to the list of patches to be installed. This patch installation list overrides the patches specified by the default patch baseline. SnapshotId: type: String default: "" description: >- (Optional) The snapshot ID to use to retrieve a patch baseline snapshot. mainSteps: - name: runPatchBaseline action: 'aws:runCommand' timeoutSeconds: 7200 onFailure: Abort inputs: DocumentName: AWS-RunPatchBaseline Targets: - Key: 'resource-groups:Name' Values: - !Ref ResourceGroupName Parameters: Operation: '{{ Operation }}' RebootOption: '{{ RebootOption }}' SnapshotId: '{{ SnapshotId }}' InstallOverrideList: '{{ InstallOverrideList }}' OutputS3BucketName: !Ref ExecutionLogsBucket OutputS3KeyPrefix: 'patching/accountid={{global:ACCOUNT_ID}}/region={{global:REGION}}/executionid={{automation:EXECUTION_ID}}' MaxConcurrency: !Ref MaximumConcurrency MaxErrors: !Ref MaximumErrors description: >- This command runs the Command document ```AWS-RunPatchBaseline``` on the specified instances.