Search code examples
amazon-web-servicesamazon-ec2automationyamlaws-ssm

YAML script to tag stopped EC2 instances using automation document in Systems Manager


I am trying to create an automation document in AWS for a maintenance task- which will add a tag to all the stopped EC2 instances in a region.

This is the yaml I'm working with:

schemaVersion: '0.3'
description: Automation to add tags to stopped instances.

parameters:
  - name: Region
    type: String
    description: The AWS region to run the automation in.
    default: us-east-1

  - name: TagKey
    type: String
    description: The key of the tag to be added to the instances.
    default: Status

  - name: TagValue
    type: String
    description: The value of the tag to be added to the instances.
    default: Stopped

mainSteps:
  - action: 'aws:runCommand'
    name: 'FindStoppedInstances'
    inputs:
      DocumentName: 'AWS-RunShellScript'
      Parameters:
        commands:
          - |
            # Get the list of stopped EC2 instance IDs
            instance_ids=$(aws ec2 describe-instances \
              --filters "Name=instance-state-name,Values=stopped" \
              --query "Reservations[*].Instances[*].InstanceId" \
              --output text)
            
            echo "$instance_ids"
        executionTimeout: ['3600']
    outputs:
      - Name: 'instance_ids'
        Selector: '$.Payload'
        Type: 'StringList'

  - action: 'aws:runCommand'
    name: 'TagStoppedInstances'
    inputs:
      DocumentName: 'AWS-RunShellScript'
      Parameters:
        commands:
          - |
            # Loop through each stopped instance and tag it
            for instance_id in {{ instance_ids }}; do
              echo "Adding tag to instance $instance_id"
              aws ec2 create-tags --resources $instance_id --tags Key={{ TagKey }},Value={{ TagValue }}
            done
        executionTimeout: ['3600']

frankly, I have not used YAML before and took help from chatgpt. I don't want to use lambda for this task. This document is giving an error - (parameters) Parameter definition does not match any following types, String, StringList, Boolean, Integer, StringMap, MapList, AWS::EC2::Instance::Id, AWS::IAM::Role::Arn, AWS::S3::Bucket::Name, List<AWS::EC2::Instance::Id>, List<AWS::IAM::Role::Arn>, List<AWS::S3::Bucket::Name>

Can someone please help me on how to correct this script? After putting the tags, I want to start those EC2s. Once my patching task is complete (for which I am using run command), I want to shut down the EC2s which have this tag again and remove this tag.

Script using AWS API actions:

description: Automation to add tags to stopped EC2 instances.
schemaVersion: '0.3'
assumeRole: "{{ AutomationAssumeRole }}"
parameters:
  AutomationAssumeRole:
    type: String
    description: "The ARN of the role that allows Automation to perform the actions on your behalf."
    default: ''
  Region:
    type: String
    description: "The AWS region to run the automation in."
    default: "us-east-1"
  TagKey:
    type: String
    description: "The key of the tag to be added to the instances."
    default: "Status"
  TagValue:
    type: String
    description: "The value of the tag to be added to the instances."
    default: "Stopped"

mainSteps:
  - name: findStoppedInstances
    action: aws:executeAwsApi
    inputs:
      Service: ec2
      Api: DescribeInstances
      Filters:
        - Name: "instance-state-name"
          Values:
            - "stopped"
    outputs:
      - Name: instanceIds
        Selector: "$.Reservations[*].Instances[*].InstanceId"
        Type: "StringList"

  - name: tagStoppedInstances
    action: aws:executeAwsApi
    inputs:
      Service: ec2
      Api: CreateTags
      Resources: "{{ findStoppedInstances.instanceIds }}"
      Tags:
        - Key: "{{ TagKey }}"
          Value: "{{ TagValue }}"

outputs:
  - Name: "instanceIds"
    Selector: "{{ findStoppedInstances.instanceIds }}"
    Type: "StringList"

Solution

  • There are couple of issues with the script. You have to update the parameter section to have keys, not list:

    parameters:
      Region:
        type: String
        description: The AWS region to run the automation in.
        default: us-east-1
    
      TagKey:
        type: String
        description: The key of the tag to be added to the instances.
        default: Status
    
      TagValue:
        type: String
        description: The value of the tag to be added to the instances.
        default: Stopped
    

    Then update the script in action TagStoppedInstances to reference the previous action:

      - action: 'aws:runCommand'
        name: 'TagStoppedInstances'
        inputs:
          DocumentName: 'AWS-RunShellScript'
          Parameters:
            commands:
              - |
                # Loop through each stopped instance and tag it
                for instance_id in {{ FindStoppedInstances.instance_ids }}; do
                  echo "Adding tag to instance $instance_id"
                  aws ec2 create-tags --resources $instance_id --tags Key={{ TagKey }},Value={{ TagValue }}
                done
            executionTimeout: ['3600']
    

    Instead of running scripts that call AWS CLI I would suggest using actions that run AWS API. A sample of similar workflow can be found on AWS Systems Manager User Guide.

    Update: The error you are getting is because the main outputs object is a list. You have to edit the findStoppedInstances selector to get all of the instances. Full automation script should look like:

    schemaVersion: '0.3'
    description: Automation to add tags to stopped instances.
    assumeRole: '{{ AutomationAssumeRole }}'
    parameters:
      AutomationAssumeRole:
        type: String
        description: The ARN of the role that allows Automation to perform the actions on your behalf.
        default: ''
      Region:
        type: String
        description: The AWS region to run the automation in.
        default: us-east-1
      TagKey:
        type: String
        description: The key of the tag to be added to the instances.
        default: Status
      TagValue:
        type: String
        description: The value of the tag to be added to the instances.
        default: Stopped
    mainSteps:
      - name: findStoppedInstances
        action: aws:executeAwsApi
        nextStep: tagStoppedInstances
        isEnd: false
        inputs:
          Service: ec2
          Api: DescribeInstances
          Filters:
            - Name: instance-state-name
              Values:
                - stopped
        outputs:
          - Name: instanceIds
            Selector: $.Reservations..Instances..InstanceId
            Type: StringList
      - name: tagStoppedInstances
        action: aws:executeAwsApi
        isEnd: true
        inputs:
          Service: ec2
          Api: CreateTags
          Resources:
            - '{{ findStoppedInstances.instanceIds }}'
          Tags:
            - Key: '{{ TagKey }}'
              Value: '{{ TagValue }}'
    outputs:
      - findStoppedInstances.instanceIds