5

To follow up on this question: Filter CloudWatch Logs to extract Instance ID

I think it leaves the question incomplete because it does not say how to access the event object with python.

My goal is to:

  • read the instance that was triggered by a change in running state
  • get a tag value associated with the instance
  • start all other instances that have the same tag

The Cloudwatch trigger event is:

{
  "source": [
    "aws.ec2"
  ],
  "detail-type": [
    "EC2 Instance State-change Notification"
  ],
  "detail": {
    "state": [
      "running"
    ]
  }
}

I can see examples like this:

def lambda_handler(event, context):

    # here I want to get the instance tag value
    # and set the tag filter based on the instance that 
    # triggered the event

    filters = [{
            'Name': 'tag:StartGroup',
            'Values': ['startgroup1'] 
        },
        {
            'Name': 'instance-state-name', 
            'Values': ['running']
        }
    ]

    instances = ec2.instances.filter(Filters=filters)

I can see the event object but I don't see how to drill down into the tag of the instance that had it's state changed to running.

Please, what is the object attribute through which I can get a tag from the triggered instance?

I suspect it is something like:

myTag = event.details.instance-id.tags["startgroup1"]

4 Answers 4

1

The event data passed to Lambda contains the Instance ID.

You then need to call describe_tags() to retrieve a dictionary of the tags.

import boto3
client = boto3.client('ec2')

client.describe_tags(Filters=[
        {
            'Name': 'resource-id',
            'Values': [
                event['detail']['instance-id']
            ]
        }
    ]
)
Sign up to request clarification or add additional context in comments.

1 Comment

John, I saw references to the client object in the boto3 docs but did not see the describe_tags method. I've posted my working (maybe not best) code. It works quickly although it throws an error when I test with a non-existent instance-id. I think that's OK, because I likely won't launch an Instance from the instance list that does not exist. We'll actually, I suppose it could be terminated, still in the list, and I accidentally select it. Oh, well.
0

In the Details Section of the Event, you will get the instance Id's. Using the instance id and AWS SDK you can query the tags. The following is the sample event

{
  "version": "0",
  "id": "ee376907-2647-4179-9203-343cfb3017a4",
  "detail-type": "EC2 Instance State-change Notification",
  "source": "aws.ec2",
  "account": "123456789012",
  "time": "2015-11-11T21:30:34Z",
  "region": "us-east-1",
  "resources": [
    "arn:aws:ec2:us-east-1:123456789012:instance/i-abcd1111"
  ],
  "detail": {
    "instance-id": "i-abcd1111",
    "state": "running"
  }
}

1 Comment

Thanks, Vaisakh. That actually helps a lot. I see the event object and the detail section, but please, how does one get to a specific tag, using python, for this instance-id. Is this a json parsing exercise? Or can this be done with object attributes? The tag value for the instance does not appear to be in the event object. Thanks, Chris.
0

This is what I came up with...

Please let me know how it can be done better. Thanks for the help.

# StartMeUp_Instances_byOne
#
# This lambda script is triggered by a CloudWatch Event, startGroupByInstance.
# Every evening a separate lambda script is launched on a schedule to stop
# all non-essential instances.
# 
# This script will turn on all instances with a LaunchGroup tag that matches 
# a single instance which has been changed to the running state.
#
# To start all instances in a LaunchGroup, 
# start one of the instances in the LaunchGroup and wait about 5 minutes.
# 
# Costs to run: approx. $0.02/month
# https://s3.amazonaws.com/lambda-tools/pricing-calculator.html
# 150 executions per month * 128 MB Memory * 60000 ms Execution Time
# 
# Problems: talk to chrisj
# ======================================

# test system
# this is what the event object looks like (see below)
# it is configured in the test event object with a specific instance-id
# change that to test a different instance-id with a different LaunchGroup

# {  "version": "0",
#   "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
#   "detail-type": "EC2 Instance State-change Notification",
#   "source": "aws.ec2",
#   "account": "999999999999999",
#   "time": "2015-11-11T21:30:34Z",
#   "region": "us-east-1",
#   "resources": [
#     "arn:aws:ec2:us-east-1:123456789012:instance/i-abcd1111"
#   ],
#   "detail": {
#     "instance-id": "i-0aad9474",  # <---------- chg this
#     "state": "running"
#   }
# }
# ======================================

import boto3
import logging
import json

ec2 = boto3.resource('ec2')

def get_instance_LaunchGroup(iid):
    # When given an instance ID as str e.g. 'i-1234567', 
    # return the instance LaunchGroup.
    ec2 = boto3.resource('ec2')
    ec2instance = ec2.Instance(iid)
    thisTag = ''
    for tags in ec2instance.tags:
        if tags["Key"] == 'LaunchGroup':
            thisTag = tags["Value"]
    return thisTag

# this is the entry point for the cloudwatch trigger
def lambda_handler(event, context):

    # get the instance id that triggered the event
    thisInstanceID = event['detail']['instance-id']
    print("instance-id: " + thisInstanceID)

    # get the LaunchGroup tag value of the thisInstanceID
    thisLaunchGroup = get_instance_LaunchGroup(thisInstanceID)
    print("LaunchGroup: " + thisLaunchGroup)
    if thisLaunchGroup == '':
        print("No LaunchGroup associated with this InstanceID - ending lambda function")
        return

    # set the filters
    filters = [{
            'Name': 'tag:LaunchGroup',
            'Values': [thisLaunchGroup] 
        },
        {
            'Name': 'instance-state-name', 
            'Values': ['stopped']
        }
    ]

    # get the instances based on the filter, thisLaunchGroup and stopped
    instances = ec2.instances.filter(Filters=filters)

    # get the stopped instance IDs
    stoppedInstances = [instance.id for instance in instances]

    # make sure there are some instances not already started
    if len(stoppedInstances) > 0:
        startingUp = ec2.instances.filter(InstanceIds=stoppedInstances).start()

    print ("Finished launching all instances for tag: " + thisLaunchGroup)

Comments

0

So, here's how I got the tags in my Python code for my Lambda function.

  ec2 = boto3.resource('ec2')
  instance = ec2.Instance(instanceId)

# get image_id from instance-id
  imageId = instance.image_id
  print(imageId)

  for tags in instance.tags:
    if tags["Key"] == 'Name':
      newName = tags["Value"] + ".mydomain.com"
  print(newName)

So, using instance.tags and then checking the "Key" matching my Name tags and pulling out the "Value" for creating the FQDN (Fully Qualified Domain Name).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.