Create SSM Automation runbook to share AMI with another account using CloudFormation

TOC

Create SSM Automation runbook to share AMI with another account using CloudFormation

One of the AWS SOA topics is related to deployment, provisioning, and automation.

The created AMI can be shared with another account.

Two methods are introduced on the official AWS page.

  • AWS Management Console
  • AWS CLI

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharingamis-explicit.html

This time, we will create a custom SSM Automation runbook to achieve the same action as above.

Environment

Diagram of creating SSM Automation runbook to share AMI with another account using CloudFormation

Create an EC2 instance in a private subnet.
The instance will be the latest Amazon Linux 2.

Create an SSM Automation runbook.
The contents of this runbook are as follows

  1. Create an AMI for the instance.
  2. Share the created AMI with another account.

It also creates an association between this runbook and the instance.

CloudFormation template files

The above configuration is built with CloudFormation.
The CloudFormation templates are placed at the following URL

https://github.com/awstut-an-r/awstut-soa/tree/main/03/005

Explanation of key points of template files

Automation Runbook

Resources:
  CreateAndShareAmiRunbook:
    Type: AWS::SSM::Document
    Properties:
      Content:
        assumeRole: "{{AutomationAssumeRole}}"
        description: Create and Share AMI with another account.
        schemaVersion: "0.3"
        parameters:
          AccountId:
            type: String
            description: (Required) The Account ID you want to share AMI.
            default: ""
          AutomationAssumeRole:
            type: String
            description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
            default: ""
          InstanceId:
            type: String
            description: (Required) The instance ID you want to run commands on.
            default: ""
        mainSteps:
          - name: createImage
            action: aws:executeAutomation
            maxAttempts: 1
            timeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
            onFailure: Abort
            inputs:
              DocumentName: AWS-CreateImage
              RuntimeParameters:
                AutomationAssumeRole:
                  - "{{AutomationAssumeRole}}"
                InstanceId:
                  - "{{InstanceId}}"
            outputs:
              - Name: AmiStringList
                Selector: "$"
                Type: String
          - name: convertStringListToString
            action: "aws:executeScript"
            inputs:
              Runtime: python3.8
              Handler: handler
              InputPayload:
                StringList:
                  - "{{createImage.Output}}"
              Script: |-
                def handler(events, context):
                  list = events['StringList']
                  return {'First': list[0]}
            outputs:
              - Name: Ami
                Selector: $.Payload.First
                Type: String
          - name: shareAmiWithAnotherAccount
            action: aws:executeAwsApi
            maxAttempts: 1
            onFailure: Abort
            inputs:
              Service: ec2
              Api: modify-image-attribute
              ImageId: "{{convertStringListToString.Ami}}"
              LaunchPermission:
                Add:
                  - UserId: "{{AccountId}}"
      DocumentFormat: YAML
      DocumentType: Automation
      Name: !Sub "${Prefix}-CreateAndShareAmiRunbook"
      TargetType: /AWS::EC2::Instance
Code language: YAML (yaml)

Create a custom runbook.
For more information on creating runbooks, please see the following pages.

あわせて読みたい
Introduction to Creating SSM Automation Runbooks Using CloudFormation 【Introduction to Creating SSM Automation Runbooks Using CloudFormation】 Many SSM Automation runbooks are currently provided by default. https://docs.aws.am...

The Content property defines the contents of the runbook.

In the parameters data element, specify the parameters for executing this runbook.
In this case, we receive the following three parameters

  • AccountId: ID of the account that shares the AMI
  • AutomationAssumeRole: ARN of the service role that Autometion will use to run this runbook
  • InstanceId: ID of the instance that creates the AMI

The mainSteps data element describes the process to be performed in the runbook.
The book consists of three steps.

Step 1

The first step is to create an AMI from the instance.

AMI creation is done using the SSM Automation runbook AWS-CreateImage.
You will be executing another Automation runbook within Automation, which can be accomplished by executing the aws:executeAutomation action.

https://docs.aws.amazon.com/systems-manager/latest/userguide/automation-action-executeAutomation.html

Specify the parameters required to run AWS-CreateImage on inputs.
In this case, specify the service role for SSM and the ID of the instance to create the AMI.

Specify the data returned after running AWS-CreateImage with outputs.
After this runbook is executed, the ID of the created AMI will be returned, so we will take this as is.

Step 2

The second step converts the type of data received in the first step.

The type of data returned after executing the aws:executeAutomation action is a StringList.

Output

The output generated by the secondary automation. You can reference the output by using the following format: Secondary_Automation_Step_Name.Output

Type: StringList

aws:executeAutomation – Run another automation

In other words, the data returned after executing AWS-CreateImage will be in the form “[‘ami-abcdefg’]”, meaning that the AMI ID cannot be received as a string.

In the three steps described below, this AMI ID is used for processing, and the data type sought is a String.
Therefore, cast the StringList type to a String type by referring to the following page.

https://qiita.com/r18j21/items/6aae052e67455cce2e4e

The way to cast it is to execute a Python script with an aws:executeScript action.
The content of this script is to refer to the data output in the first step and return the first data.
This will convert the data that was of type StringList to a String.

Step 3

The third step is to share the AMI with another account.
The AMI is shared by executing an AWS API in the form of an aws:executeAwsApi action.

https://docs.aws.amazon.com/systems-manager/latest/userguide/automation-action-executeAwsApi.html

AMI sharing can be performed with the modify-image-attribute method of the EC2 service.

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.modify_image_attribute

To execute this API, two parameters are set.
The first is the ImageId. This specifies the AMI ID converted to a String type in two steps.
The second is LaunchPermission, which specifies the account with which the AMI will be shared.

Service Roles for Automation

Resources:
  CreateAndShareAmiRunbookRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - ssm.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonSSMAutomationRole
      Policies:
        - PolicyName: CopyAndShareAmiWithAnotherAccountPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - ec2:CreateImage
                  - ec2:DescribeInstances
                  - ec2:ModifyImageAttribute
                  - iam:PassRole
                Resource:
                  - "*"
Code language: YAML (yaml)

Normally, if no service role is specified, the default service link role (SLR), AWSServiceRoleforAmazonSSM, is used.
In this case, create the service role according to the following official AWS page.

https://docs.aws.amazon.com/systems-manager/latest/userguide/automation-setup-iam.html

In addition, grant the necessary permissions for AMI creation and sharing as an inline policy.

SSM Association

Resources:
  CreateAndShareAmiRunbookAssociation:
    Type: AWS::SSM::Association
    Properties:
      AssociationName: !Sub "${Prefix}-CreateAndShareAmiRunbookAssociation"
      AutomationTargetParameterName: InstanceId
      Name: !Ref CreateAndShareAmiRunbook
      Parameters:
        AccountId:
          - !Ref AccountId
        AutomationAssumeRole:
          - !GetAtt CreateAndShareAmiRunbookRole.Arn
        InstanceId:
          - "{{RESOURCE_ID}}"
      Targets:
        - Key: !Sub "tag:${TagKey}"
          Values:
            - !Ref TagValue
      WaitForSuccessTimeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
Code language: YAML (yaml)

Create an association between the runbook and the instance described above.

For information on how to create SSM associations and run SSM Automation runbook on EC2 instances, please see the following page.

あわせて読みたい
Create AMI using SSM Automation (one-time/scheduled) 【Create AMI using SSM Automation (one-time/scheduled)】 There are several ways to create an AMI. For example, the following page shows how to create one fro...

In this case, instances with the following tags are targeted for association.

  • Tag Key: MyDocument
  • Tag Value: Group1

Tag instances with the above conditions to make them eligible for association.

Architecting

Use CloudFormation to build this environment and check its actual behavior.

Create CloudFormation stacks and check the resources in the stacks

Create CloudFormation stacks.
For information on how to create stacks and check each stack, please refer to the following pages.

あわせて読みたい
CloudFormation’s nested stack 【How to build an environment with a nested CloudFormation stack】 Examine nested stacks in CloudFormation. CloudFormation allows you to nest stacks. Nested ...

After reviewing the resources in each stack, information on the main resources created in this case is as follows

  • EC2 instance: i-0f5ba431de00d32a6
  • Custom SSM Automation runbook: soa-03-005-CreateAndShareAmiRunbook
  • SSM association: da0a89d3-bb77-480b-afda-c15d5f8fffec

Action Check

Now that you are ready, check each resource from the AWS Management Console.

Custom SSM Automation Runbook

Check the runbook you have created.

Detail of SSM 1.
Detail of SSM 2.
Detail of SSM 3.

You can see that the runbook has been successfully created.

There are three parameters in this runbook, the contents of which are to be shared with another account after the AMI of the EC2 instance is created.

SSM Association

関連付けを確認します。

Detail of SSM 4.

You can see that an association has been created for the aforementioned runbook.

Detail of SSM 5.

Parameters for running the runbook.
The account specified in AccountId and the AMI to be created in the runbook will be shared.

Detail of SSM 6.

Looking at the target, we can see that the target is the instance whose tag name MyDocument has the value “Group1”.

Check the run history of the runbook.

Detail of SSM 7.

Execution has been completed successfully.

Check the detailed log of each step performed.

Check the first step.

Detail of SSM 8.

The AWS-CreateImage runbook was executed with the service role for SSM and instance ID as parameters.
The Outputs shows that an AMI was created.
The ID of the AMI created this time is “ami-06b79a64a1a2b1447”.

Check the second step.

Detail of SSM 9.

The Python script is now executed.
Looking at Input parameters, the AMI ID mentioned above is stored as a StringList.
The Outputs section outputs this as a String type.

Check the third step.

Detail of SSM 10.

The modify-image-attribute method has been executed and the AMI has been shared to another account.

Finally, check the details of the AMI you have created.

Detail of EC2 1.

If you look at the Shared accounts section, you will indeed see the ID of another account.
This means that by creating a custom SSM Automation runbook and associating it with an EC2 instance, an AMI for this instance was created and then the AMI was shared with another account.

Summary

We have identified a way to share an AMI with another account using a custom SSM Automation runbook.

TOC