Patching of Multiple Windows Servers.

0

I have a developed a CF template to Patch a single instance and i would like to carry out patching for multiple servers by executing one template and i need help in fixing the template by adding ResourceGroup and Schedulars below is my template.

AWSTemplateFormatVersion: '2010-09-09'
Description: AWS CloudFormation template to create a scheduled multi-account and multi-Region patching operation using Amazon EventBridge, AWS Lambda, and AWS Systems Manager Automation.
Parameters:
  EventBridgeRuleSchedule:
    Type: String
    Description: >-
      The cron or rate expression to use for the EventBridge rule. For example: cron(0 02 ? * TUE *). Important: The time zone used is UTC. For more information, see https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduled-events.html.
  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.
  ExistingAutomationAdministrationRole:
    Type: String
    Description: >-
      (Optional) The IAM role ARN of an existing Automation Administration role which has permissions to invoke multi-account and multi-Region Automation workflows.
  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%.
  RunPatchBaselineOperation:
    Type: String
    Default: Scan
    AllowedValues:
      - Scan
      - Install
      - Scan and 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: NoReboot
    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.
  ResourceGroupName:
    Type: String
    Description: >-
      Enter a resource group that includes the resources you want to target. Important: The Resource Group name is case sensitive.
  TargetAccounts:
    Type: String
    Description: >-
      Comma separated list of AWS Account 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.
Conditions:
  CreateAutomationAdministrationRoleCondition:
    Fn::Equals:
    - Ref: ExistingAutomationAdministrationRole
    - ''
Resources:
  EventBridgeRule:
    Type: AWS::Events::Rule
    Properties:
      Description: EventBridge rule created for scheduled multi-account and multi-Region Automation patching using AWS Lambda and AWS Systems Manager Automation
      Name: Schedule-Trigger-for-Lambda-MultiAccountPatchingBlog
      ScheduleExpression:
        Ref: EventBridgeRuleSchedule
      State: ENABLED
      Targets:
      - Arn:
          Fn::GetAtt:
          - MultiAccountPatchingBlogLambdaFunction
          - Arn
        Id: MultiAccountPatchingBlog
  PermissionForEventsToInvokeLambda:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName:
        Ref: MultiAccountPatchingBlogLambdaFunction
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn:
        Fn::GetAtt:
        - EventBridgeRule
        - Arn
  AWSLambdaSSMMultiAccountPatchingBlogRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: AWS-Lambda-SSM-MultiAccountPatchingBlogRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - lambda.amazonaws.com
          Action: sts:AssumeRole
      Path: "/"
      Policies:
      - PolicyName: AWSLambdaSSMMultiAccountPatchingBlogPolicy
        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/MultiAccountPatchingBlog:*
            Effect: Allow
  MultiAccountPatchingBlogLambdaFunction:
    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']
              ResourceGroupName=os.environ['ResourceGroupName']
              MaximumConcurrency=os.environ['MaximumConcurrency']
              MaximumErrors=os.environ['MaximumErrors']
              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
            ResourceGroupName: !Ref ResourceGroupName
            MaximumConcurrency: !Ref MaximumConcurrency
            MaximumErrors: !Ref MaximumErrors
            TargetLocationMaxConcurrency: !Ref TargetLocationMaxConcurrency
            TargetLocationMaxErrors: !Ref TargetLocationMaxErrors
            ExecutionRoleName: !Ref ExecutionRoleName
            MasterAccountID: !Sub ${AWS::AccountId}
            AutomationDocumentMamrRunPatchBaseline: !Ref AutomationDocumentMamrRunPatchBaseline
      FunctionName: MultiAccountPatchingBlog
      Handler: index.handler
      Role: !GetAtt AWSLambdaSSMMultiAccountPatchingBlogRole.Arn 
      Runtime: python3.7
  AutomationAdministrationServiceRole:
    Type: AWS::IAM::Role
    Condition: CreateAutomationAdministrationRoleCondition
    Properties:
      RoleName: AWS-SystemsManager-AutomationAdministrationRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service: ssm.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
      Policies:
      - PolicyName: AssumeRole-AWSSystemsManagerAutomationExecutionRole
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - sts:AssumeRole
            Resource:
              Fn::Sub: arn:${AWS::Partition}:iam::*:role/AWS-SystemsManager-AutomationExecutionRole
          - Effect: Allow
            Action:
            - organizations:ListAccountsForParent
            Resource:
            - "*"
  AutomationDocumentMamrRunPatchBaseline:
    Type: AWS::SSM::Document
    Properties: 
      Name: Automation-RunPatchBaseline
      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: "runPatchBaselineScan"
            action: "aws:runCommand"
            timeoutSeconds: 7200
            onFailure: "Continue"
            inputs:
              DocumentName: "AWS-RunPatchBaseline"
              Targets:
                - Key: "resource-groups:Name"
                  Values:
                    - !Ref ResourceGroupName
              Parameters:
                Operation: "Scan"
                SnapshotId: "{{ SnapshotId }}"
              MaxConcurrency: !Ref MaximumConcurrency
              MaxErrors: !Ref MaximumErrors
            description: >-
              This step checks if patches specified in the patch baseline are installed on
              the specified instances.

          - name: "runPatchBaselineInstall"
            action: "aws:runCommand"
            timeoutSeconds: 7200
            onFailure: "Abort"
            inputs:
              DocumentName: "AWS-RunPatchBaseline"
              Targets:
            
Manoj
asked 5 months ago197 views
1 Answer
0

I suggest creating resource groups or using the Patch Group tag https://docs.aws.amazon.com/systems-manager/latest/userguide/patch-manager-tag-a-patch-group.html. You can still create your patch baseline via cfn. You can then also create AWS::SSM::MaintenanceWindow and AWS::SSM::MaintenanceWindowTarget

Bobk
answered 5 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions

Relevant content