1
votes

I have an AWS Lambda function that resizes EC2 instances but the instance id is hard-coded - I would prefer to use tags but can't get it to work

Here's the code that works:

# lambda function
import boto3
import botocore
import os

# define environment variables
Instance_Type = os.environ['InstanceType']

#define the connections
client = boto3.client('ec2')

RunningInstances = 'i-02a1130833c928708'

def lambda_handler(event, context):
    # Stop the instance
    client.stop_instances(InstanceIds=[RunningInstances])
    waiter=client.get_waiter('instance_stopped')
    waiter.wait(InstanceIds=[RunningInstances])
    print "Stopped", RunningInstances

    # Change the instance type
    client.modify_instance_attribute(InstanceId=RunningInstances, 
    Attribute='instanceType', Value=Instance_Type)
    print RunningInstances, "resized down to", Instance_Type

    # Start the instance
    client.start_instances(InstanceIds=[RunningInstances])
    print "Started", RunningInstances

Here's what I'd like to work:

import boto3
import botocore
import os

# define environment variables
Instance_Type = os.environ['InstanceType']

#define the connections
client = boto3.client('ec2')

#RunningInstances = 'i-02a1130833c928708'

def lambda_handler(event, context):
    filters=[
            {'Name': 'tag-key', 'Values': ['ResizeDown']},
            {'Name': 'tag-value', 'Values': ['True']},
            {'Name': 'instance-state-name','Values': ['running']}
    ]

    #filter the instances
    instances = client.instances.filter(Filters=filters)

    #locate all running instances
    RunningInstances = [instance.id for instance in instances]

    # Stop the instance
    client.stop_instances(InstanceIds=[RunningInstances])
    waiter=client.get_waiter('instance_stopped')
    waiter.wait(InstanceIds=[RunningInstances])
    print "Stopped", RunningInstances

    # Change the instance type
    client.modify_instance_attribute(InstanceId=RunningInstances, 
    Attribute='instanceType', Value=Instance_Type)
    print RunningInstances, "resized down to", Instance_Type

    # Start the instance
    client.start_instances(InstanceIds=[RunningInstances])
    print "Started", RunningInstances

the error I get is:

'EC2' object has no attribute 'instances': AttributeError Traceback (most recent call last): File "/var/task/lambda_function.py", line 21, in lambda_handler instances = client.instances.filter(Filters=filters) File "/var/runtime/botocore/client.py", line 555, in getattr self.class.name, item) AttributeError: 'EC2' object has no attribute 'instances'

2

2 Answers

2
votes

As your error says, your EC2 object has no attribute instances, and if you look in the docs you won't find it there. You should just be able to use the describe_instances function on your boto client with the filter you already set up, and that will return you the relevant instances as before, ie:

instances = client.describe_instances(Filters=filters)
1
votes

This line:

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

is using the format for an EC2 resource rather than an EC2 client.

You could use:

ec2_resource = boto3.resource('ec2')

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

Basically, it's two different ways of referencing AWS. The client approach maps to the AWS API, while the resource approach is more Pythonic.