Periodically delete old AMIs – SSM Automation runbook version

Periodically delete old AMIs - SSM Automation runbook version

Periodically delete old AMIs – SSM Automation runbook version

In the following pages, we have shown how to use DLM to periodically delete old AMIs.

https://awstut.com/en/2023/03/19/introduction-to-data-lifecycle-manager-create-ebs-snapshot-ami-periodically-en

We also showed how Step Functions can be used to periodically delete old AMIs.

https://awstut.com/en/2023/03/20/periodically-delete-old-amis-step-functions-version-en

This time, the same content as above is performed using the SSM Automation runbook.

Envrionment

Diagram of periodically delete old AMIs - SSM Automation runbook version

Create a SSM Automation runbook.
The runbook executes the following

  • Search for AMIs that have been created for more than one hour and tag them.
  • Run AWS-DeleteImage, a runbook owned by AWS, on AMIs that have been tagged.

Create an EventBridge rule.
Set the runbook to run periodically (once an hour).

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/02/005

Explanation of key points of template files

SSM Automation Runbook

Resources:
  DeleteImageRunbook:
    Type: AWS::SSM::Document
    Properties:
      Content:
        assumeRole: "{{AutomationAssumeRole}}"
        description: Create an AMI for an EC2 instance with a specific tag.
        schemaVersion: "0.3"
        parameters:
          AutomationAssumeRole:
            type: String
            description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
            default: !GetAtt DeleteImageRunbookRole.Arn
          AccountId:
            type: String
            description: (Required) The ID of Account.
            default: !Ref AWS::AccountId
          Region:
            type: String
            description: (Required) The Region.
            default: !Ref AWS::Region
          TagKey:
            type: String
            description: (Required) The tag key of Image.
            default: !Ref TagKey
          TagValue:
            type: String
            description: (Required) The tag value of Image.
            default: !Ref TagValue
          ValidHours:
            type: Integer
            description: (Required) The expired hours.
            default: !Ref ValidHours
        mainSteps:
          - name: setTagToExpiredImages
            action: aws:executeScript
            inputs:
              Runtime: python3.8
              Handler: handler
              InputPayload:
                AccountId: "{{AccountId}}"
                Region: "{{Region}}"
                TagKey: "{{TagKey}}"
                TagValue: "{{TagValue}}"
                ValidHours: "{{ValidHours}}"
              Script: |-
                import boto3
                import datetime
                import json

                def handler(events, context):
                  account_id = events['AccountId']
                  region = events['Region']
                  tag_key = events['TagKey']
                  tag_value = events['TagValue']
                  valid_hours = int(events['ValidHours'])

                  client = boto3.client('ec2', region_name=region)

                  now = datetime.datetime.now(datetime.timezone.utc)

                  response = client.describe_images(
                    Owners=[account_id])
                  create_tags_results = []
                  for image in response['Images']:
                    creation_date_str = image['CreationDate']
                    creation_date_dt = datetime.datetime.fromisoformat(creation_date_str.replace('Z', '+00:00'))

                    diff = now - creation_date_dt
                    diff_hour = diff.seconds / (60 * 60)

                    if diff_hour > valid_hours:
                      result = client.create_tags(
                        Resources=[
                          image['ImageId']],
                        Tags=[
                          {
                            'Key': tag_key,
                            'Value': tag_value
                          }],
                        )
                      create_tags_results.append(result)

                  return {'createTagResults': json.dumps(create_tags_results)}
            outputs:
              - Name: createTagResults
                Selector: $.Payload.creataTagResults
                Type: String
          - name: deleteTagedImages
            action: aws:executeAutomation
            maxAttempts: 1
            timeoutSeconds: !Ref WaitForSuccessTimeoutSeconds
            onFailure: Abort
            inputs:
              DocumentName: AWS-DeleteImage
              TargetParameterName: ImageId
              Targets:
                - Key: "tag:{{TagKey}}"
                  Values:
                    - "{{TagValue}}"
            outputs:
              - Name: deleteImage
                Selector: "$"
                Type: String
      DocumentFormat: YAML
      DocumentType: Automation
      Name: !Sub "${Prefix}-DeleteImageRunbook"
Code language: YAML (yaml)

Create a SSM Automation runbook to remove the old AMI.

For more information on how to create your own runbook, please see the following pages.

https://awstut.com/2023/02/23/introduction-to-creating-ssm-automation-runbooks-using-cloudformation

The point is the setting regarding steps.

First step

This step sets a tag on the old AMI.
You can execute any code by setting the actions property to “aws:executeScript”.

Runs the Python or PowerShell script provided using the specified runtime and handler.

aws:executeScript – Run a script

The code to be executed is generally similar to the Step Functions version.

https://awstut.com/en/2023/03/20/periodically-delete-old-amis-step-functions-version-en

The change is that if an AMI is found that has been created for more than one hour, a tag will be set.
The following tags will be set

  • Tag Name:DeleteImage
  • Tag Value:true

Second step

In this step, the AMI is deleted.

By setting the actions property to “aws:executeAutomation”, you can execute another runbook from within a runbook.
Delete an AMI by specifying “AWS-DeleteImage” for the DocumentName property in inputs.

By specifying the aforementioned tag information in the Targets property and “ImageId” in the TargetParameterName, AWS-DeleteImage is executed for the AMI to which the tag is assigned.

IAM Roles for Runbook

Resources:
  DeleteImageRunbookRole:
    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: SetTagAndDeleteImagePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - tag:GetResources
                Resource:
                  - "*"
Code language: YAML (yaml)

In addition to the AWS management policy AmazonSSMAutomationRole, set an inline policy to allow tag:GetResources.

EventBridge Rule

Resources:
  EventsRule:
    Type: AWS::Events::Rule
    Properties:
      Name: !Sub "${Prefix}-EventsRule"
      ScheduleExpression: rate(1 hour)
      State: ENABLED
      Targets:
        - Arn: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${Runbook}:$DEFAULT"
          Id: !Ref Runbook
          RoleArn: !GetAtt EventsRuleRole.Arn
Code language: YAML (yaml)

Create an EventBridge rule to run the runbook periodically.

By specifying “rate(1 hour)” in the ScheduleExpression property, the runbook can be executed every hour.

Specify the aforementioned runbook ARN, etc. in the Targets property.

Resources:
  EventsRuleRole:
    Type: AWS::IAM::Role
    DeletionPolicy: Delete
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: events.amazonaws.com
      Policies:
        - PolicyName: !Sub "${Prefix}-StartAutomationExecutionPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: ssm:StartAutomationExecution
                Resource: !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${Runbook}:$DEFAULT"
              - Effect: Allow
                Action: iam:PassRole
                Resource: !Ref RunbookRoleArn
Code language: YAML (yaml)

Allow two actions in the inline policy.

The first is ssm:StartAutomationExecution.
Literally, this is what allows the runbook to be executed.

The second is iam:PassRole.
This is to pass the IAM role for the SSM Automation runbook that we just checked to the runbook.

Architecting

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

Create a 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.

https://awstut.com/en/2021/12/11/cloudformations-nested-stack

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

  • SSM Automation runbook:soa-02-005-DeleteImageRunbook
  • EventBridge rule:soa-02-005-EventsRule

Check various resources from the AWS Management Console.

Check the SSM Automaiton runbook.

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

The runbook has been successfully created.

Check the EventBridge rule.

Detail of EventBridge 1.
Detail of EventBridge 2.

The contents of the runbook are to be executed every hour.

Operation Check

AMI Creation

Create an AMI from any EC2 instance.

Detail of EC2 1.
Detail of EC2 2.

A snapshot was created along with the AMI.

1st runbook execution

The first runbook execution begins automatically.

Detail of SSM 4.

Execution details.

Detail of SSM 5.

You can see that the two steps in the run book have been successfully completed.

Check the status of both steps.

The first step.

Detail of SSM 6.

Looking at Outputs, the result of the script execution is empty.
In other words, there were no AMIs that had been created for more than one hour, so the script terminated without performing any tagging or other processing.

Second step.

Detail of SSM 7.

This step executes the SSM Automation runbook AWS-DeleteImage on an AMI with the tag name “DeleteImage” and the value “true”.

Below are the results of this runbook run.

Detail of SSM 8.

Nothing was executed because there were no AMIs with the corresponding tag.

Thus, if there are no AMIs that meet the deletion criteria, the program exits without executing any action.

2nd runbook execution

After one hour, a second runbook execution will automatically begin.

Detail of SSM 9.

Execution details.

Detail of SSM 10.

Again, we see that the two steps in the run book were completed successfully.

Check the status of both steps.

The first step.

Detail of SSM 11.

Looking at Outputs, there was one result of the script execution.
This means that more than an hour has passed since its creation and the AMI was tagged earlier.

Second step.

Detail of SSM 12.

This step executes the SSM Automation runbook AWS-DeleteImage on an AMI with the tag name “DeleteImage” and the value “true”.

Below are the results of this runbook execution.

Detail of SSM 13.

You can see the ID of the AMI you just generated in Step name.

Confirm the execution of the steps.

Detail of SSM 14.

Runbook AWS-DeleteImage is now running.

Check what has been executed in this run book.

Detail of SSM 15.

Deletion of tagged AMI was successfully performed.

Finally, check the AMI and snapshots.

Detail of EC2 3.
Detail of EC2 4.

Indeed, AMI and others have been removed.

Summary

We also showed how to use the SSM Automation runbook to periodically delete old AMIs.