AWS Lambda create EC2 and associate EIP - amazon-web-services

I am trying to deploy an EC2 instance and associate an EIP to it, but I am getting and error when trying to associate the EIP because the instance is not running. This is my code:
import boto3
from botocore.exceptions import ClientError
AMI = 'ami-0bf84....'
INSTANCE_TYPE = 't2.micro'
KEY_NAME = 'EC2company'
SUBNET_ID = 'subnet-065....'
ec2 = boto3.client('ec2')
def lambda_handler(event, context):
instance = ec2.run_instances(
ImageId=AMI,
InstanceType=INSTANCE_TYPE,
KeyName=KEY_NAME,
SubnetId=SUBNET_ID,
MaxCount=1,
MinCount=1
)
waiter = ec2.get_waiter('instance_running')
try:
response = ec2.associate_address(
AllocationId='eipalloc-0bc.....',
InstanceId=instance['Instances'][0]['InstanceId'],
)
print(response)
except ClientError as e:
print(e)
I suppose that the issue is related to be applying the waiter in the wrong way, and not sure how i should do it.

As per EC2 waiters, you can create a waiter with:
waiter = client.get_waiter('instance_running')
You then activate the waiter with:
waiter.wait(InstanceIds=['i-xxx']
It polls EC2.Client.describe_instances() every 15 seconds until a successful state is reached. An error is returned after 40 failed checks.

Related

Lambda function for creating an EC2 instance

I ran my code to create an EC2 instance but I keep getting this error.
"errorMessage": "'message'",
"errorType": "KeyError",
The full code
import boto3
import os
AMI = os.environ['AMI']
INSTANCE_TYPE = os.environ['INSTANCE_TYPE']
KEY_NAME = os.environ['KEY_NAME']
SUBNET_ID = os.environ['SUBNET_ID']
REGION = os.environ['AWS_REGION']
ec2 = boto3.client('ec2', region_name=REGION)
def lambda_handler(event, context):
message = event['message']
instance = ec2.run_instances(
ImageId=AMI,
InstanceType=INSTANCE_TYPE,
KeyName=KEY_NAME,
SubnetId=SUBNET_ID,
MaxCount=1,
MinCount=1,
InstanceInitiatedShutdownBehavior='terminate',
UserData=init_script
)
instance_id = instance['Instances'][0]['InstanceId']
print instance_id
return instance_id
What could be triggering this key error?
As an environmental variable, am I supposed to use the full key name including its file type.
Ex: "key.pem" instead of "key"
The error indicates that you have an error on this line:
message = event['message']
Most likely the lambda event does not have the 'message' key you are expecting. You should print out the event to CloudWatch Logs and take a look.
I ran the rest of your code and it created an EC2 instance successfully.

How to get the list of Nitro system based EC2 instance type by CLI?

I know this page lists up the instance types which based on Nitro system but I would like to know the list in a dynamic way with CLI. (for example, using aws ec2 describe-instances). Is it possible to get Nitro based instance type other than parsing the static page? If so, could you tell me the how?
You'd have to write a bit of additional code to get that information. aws ec2 describe-instances will give you InstanceType property. You should use a programming language to parse the JSON, extract InstanceType and then call describe-instances like so: https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instance-types.html?highlight=nitro
From the JSON you get back, extract hypervisor. That'll give you Nitro if the instance is Nitro.
Here's a Python code that might work. I have not tested it fully but you can tweak this to get the results you want.
"""List all EC2 instances"""
import boto3
def ec2_connection():
"""Connect to AWS using API"""
region = 'us-east-2'
aws_key = 'xxx'
aws_secret = 'xxx'
session = boto3.Session(
aws_access_key_id = aws_key,
aws_secret_access_key = aws_secret
)
ec2 = session.client('ec2', region_name = region)
return ec2
def get_reservations(ec2):
"""Get a list of instances as a dictionary"""
response = ec2.describe_instances()
return response['Reservations']
def process_instances(reservations, ec2):
"""Print a colorful list of IPs and instances"""
if len(reservations) == 0:
print('No instance found. Quitting')
return
for reservation in reservations:
for instance in reservation['Instances']:
# get friendly name of the server
# only try this for mysql1.local server
friendly_name = get_friendly_name(instance)
if friendly_name.lower() != 'mysql1.local':
continue
# get the hypervisor based on the instance type
instance_type = get_instance_info(instance['InstanceType'], ec2)
# print findings
print(f'{friendly_name} // {instance["InstanceType"]} is {instance_type}')
break
def get_instance_info(instance_type, ec2):
"""Get hypervisor from the instance type"""
response = ec2.describe_instance_types(
InstanceTypes=[instance_type]
)
return response['InstanceTypes'][0]['Hypervisor']
def get_friendly_name(instance):
"""Get friendly name of the instance"""
tags = instance['Tags']
for tag in tags:
if tag['Key'] == 'Name':
return tag['Value']
return 'Unknown'
def run():
"""Main method to call"""
ec2 = ec2_connection()
reservations = get_reservations(ec2)
process_instances(reservations, ec2)
if __name__ == '__main__':
run()
print('Done')
In the above answer , the statement "From the JSON you get back, extract hypervisor. That'll give you Nitro if the instance is Nitro " is not longer accurate.
As per the latest AWS documentation,
hypervisor - The hypervisor type of the instance (ovm | xen ). The value xen is used for both Xen and Nitro hypervisors.
Cleaned up, verified working code below:
# Get all instance types that run on Nitro hypervisor
import boto3
def get_nitro_instance_types():
"""Get all instance types that run on Nitro hypervisor"""
ec2 = boto3.client('ec2', region_name = 'us-east-1')
response = ec2.describe_instance_types(
Filters=[
{
'Name': 'hypervisor',
'Values': [
'nitro',
]
},
],
)
instance_types = []
for instance_type in response['InstanceTypes']:
instance_types.append(instance_type['InstanceType'])
return instance_types
get_nitro_instance_types()
Example output as of 12/06/2022 below:
['r5dn.8xlarge', 'x2iedn.xlarge', 'r6id.2xlarge', 'r6gd.medium',
'm5zn.2xlarge', 'r6idn.16xlarge', 'c6a.48xlarge', 'm5a.16xlarge',
'im4gn.2xlarge', 'c6gn.16xlarge', 'c6in.24xlarge', 'r5ad.24xlarge',
'r6i.xlarge', 'c6i.32xlarge', 'x2iedn.2xlarge', 'r6id.xlarge',
'i3en.24xlarge', 'i3en.12xlarge', 'm5d.8xlarge', 'c6i.8xlarge',
'r6g.large', 'm6gd.4xlarge', 'r6a.2xlarge', 'x2iezn.4xlarge',
'c6i.large', 'r6in.24xlarge', 'm6gd.xlarge', 'm5dn.2xlarge',
'd3en.2xlarge', 'c6id.8xlarge', 'm6a.large', 'is4gen.xlarge',
'r6g.8xlarge', 'm6idn.large', 'm6a.2xlarge', 'c6i.4xlarge',
'i4i.16xlarge', 'm5zn.6xlarge', 'm5.8xlarge', 'm6id.xlarge',
'm5n.16xlarge', 'c6g.16xlarge', 'r5n.12xlarge', 't4g.nano',
'm5ad.12xlarge', 'r6in.12xlarge', 'm6idn.12xlarge', 'g5.2xlarge',
'trn1.32xlarge', 'x2gd.8xlarge', 'is4gen.4xlarge', 'r6gd.xlarge',
'r5a.xlarge', 'r5a.2xlarge', 'c5ad.24xlarge', 'r6a.xlarge',
'r6g.medium', 'm6id.12xlarge', 'r6idn.2xlarge', 'c5n.2xlarge',
'g5.4xlarge', 'm5d.xlarge', 'i3en.3xlarge', 'r5.24xlarge',
'r6gd.2xlarge', 'c5d.large', 'm6gd.12xlarge', 'm6id.2xlarge',
'm6i.large', 'z1d.2xlarge', 'm5a.4xlarge', 'm5a.2xlarge',
'c6in.xlarge', 'r6id.16xlarge', 'c7g.8xlarge', 'm5dn.12xlarge',
'm6gd.medium', 'im4gn.8xlarge', 'm5dn.large', 'c5ad.4xlarge',
'r6g.16xlarge', 'c6a.24xlarge', 'c6a.16xlarge']
"""List all EC2 instances"""
import boto3
def ec2_connection():
"""Connect to AWS using API"""
region = 'us-east-2'
aws_key = 'xxx'
aws_secret = 'xxx'
session = boto3.Session(
aws_access_key_id = aws_key,
aws_secret_access_key = aws_secret
)
ec2 = session.client('ec2', region_name = region)
return ec2
def get_reservations(ec2):
"""Get a list of instances as a dictionary"""
response = ec2.describe_instances()
return response['Reservations']
def process_instances(reservations, ec2):
"""Print a colorful list of IPs and instances"""
if len(reservations) == 0:
print('No instance found. Quitting')
return
for reservation in reservations:
for instance in reservation['Instances']:
# get friendly name of the server
# only try this for mysql1.local server
friendly_name = get_friendly_name(instance)
if friendly_name.lower() != 'mysql1.local':
continue
# get the hypervisor based on the instance type
instance_type = get_instance_info(instance['InstanceType'], ec2)
# print findings
print(f'{friendly_name} // {instance["InstanceType"]} is {instance_type}')
break
def get_instance_info(instance_type, ec2):
"""Get hypervisor from the instance type"""
response = ec2.describe_instance_types(
InstanceTypes=[instance_type]
)
return response['InstanceTypes'][0]['Hypervisor']
def get_friendly_name(instance):
"""Get friendly name of the instance"""
tags = instance['Tags']
for tag in tags:
if tag['Key'] == 'Name':
return tag['Value']
return 'Unknown'
def run():
"""Main method to call"""
ec2 = ec2_connection()
reservations = get_reservations(ec2)
process_instances(reservations, ec2)
if name == 'main':
run()
print('Done')

AWS lambda change instance type with boto3

when i try this code to change EC2 instance type, it gives me this error
----------------------------------error Response---------------------------------------------------------------
"errorMessage": "Syntax error in module 'lambda_function': expected an indented block
"Runtime.UserCodeSyntaxError",
--------------------------Lambda code--------------------------
import boto3
def lambda_handler(event, context):
client = boto3.client('ec2')
# Insert your Instance ID here
my_instance = 'i-0cd1cecsdcdodid'
# Stop the instance
client.stop_instances(InstanceIds=[my_instance])
waiter=client.get_waiter('instance_stopped')
waiter.wait(InstanceIds=[my_instance])
# Change the instance type
client.modify_instance_attribute(InstanceId=my_instance, Attribute='instanceType', Value='t2.medium')
# Start the instance
client.start_instances(InstanceIds=[my_instance])
Looks like you have some indentation error in your code. Here is a reformatted version:
import boto3
def lambda_handler(event, context):
client = boto3.client('ec2')
# Insert your Instance ID here
my_instance = 'i-0cd1cecsdcdodid' # Stop the instance
client.stop_instances(InstanceIds=[my_instance])
waiter = client.get_waiter('instance_stopped')
waiter.wait(InstanceIds=[my_instance]) # Change the instance type
client.modify_instance_attribute(InstanceId=my_instance,
Attribute='instanceType', Value='t2.medium') # Start the instance
client.start_instances(InstanceIds=[my_instance])

Stopping multiple AWS RDS instances using lambda function

I would like to stop multiple RDS instances using the lambda function. The code I am using is:
import sys
import botocore
import boto3
from botocore.exceptions import ClientError
def lambda_handler(event, context):
rds = boto3.client('rds')
lambdaFunc = boto3.client('lambda')
print('Trying to get Environment variable')
try:
funcResponse = lambdaFunc.get_function_configuration(
FunctionName='lambdaStopRDS'
)
DBinstance = funcResponse['Environment']['Variables']['DBInstanceName']
print('Stopping RDS service for DBInstance : ' + DBinstance)
except ClientError as e:
print(e)
try:
response = rds.stop_db_instance(
DBInstanceIdentifier=DBinstance
)
print('Success :: ')
return response
except ClientError as e:
print(e)
return
{
'message' : "Script execution completed. See Cloudwatch logs for complete output"
}
I am adding the following environment variables:
Key: DBInstanceName
Value: database-1, database-2
and getting the following error:
Trying to get Environment variable
Stopping RDS service for DBInstance : database-1, database-2
An error occurred (InvalidParameterValue) when calling the StopDBInstance operation: Invalid database identifier: database-1, database-2
Here, Keys must be unique, so I can not add another key with same name and add another RDS.
Is there any way to stop multiple RDS instances within same VPC/region without tags?
stop_db_instance takes only one db id, not multiple ones. However, you are trying to pass two of them database-1, database-2. So you have to do in a loop. For example:
try:
db_ids = [v.strip() for v in DBinstance.split(',')]
for db_id in db_ids:
response = rds.stop_db_instance(
DBInstanceIdentifier=db_id
)
print('Success :: ')
return response
except ClientError as e:
print(e)

Getting NULL output when running python job

I am running a python job in AWS Lambda to stop ec2 instances based on tags.
The script is running properly, but even if the script completes successfully, I am getting output "null" in the result returned by function execution.
Attached herewith is the python script. I am new to Python scripting. I am from ops side.
import boto3
import logging
#setup simple logging for INFO
logger = logging.getLogger()
logger.setLevel(logging.INFO)
#define the connection
ec2 = boto3.resource('ec2')
def lambda_handler(event, context):
# Use the filter() method of the instances collection to retrieve
# all running EC2 instances.
filters = [{
'Name': 'tag:AutoOff',
'Values': ['True']
},
{
'Name': 'instance-state-name',
'Values': ['running']
}
]
#filter the instances
instances = ec2.instances.filter(Filters=filters)
#locate all running instances
RunningInstances = [instance.id for instance in instances]
#print the instances for logging purposes
print RunningInstances
#make sure there are actually instances to shut down.
if len(RunningInstances) > 0:
#perform the shutdown
shuttingDown = ec2.instances.filter(InstanceIds=RunningInstances).stop()
print shuttingDown
else:
print "NOTHING"
In order to get response from the lambda you need to return something (usually a dictionary) from lambda_handler method. By default all Python methods return None type, that is why you do not receive any valuable response.
def lambda_handler(event, context):
... your code here ...
return {"turned_off": RunningInstances}
PS. it is preferred to use logging.debug|info|... method instead of print(). You can find more info in the documentation: https://docs.python.org/2.7/library/logging.html
Anyway all the output is saved to CloudWatch Logs. The Log Stream is created automatically when you create a Lambda function. You can find all your prints there for debugging.