0

This code does not throw any errors, just shows me an empty answer:

import os
import boto3

ec2 = boto3.resource('ec2',region)

for ins in ec2.instances.all():
        ip = ins.private_ip_address
        state_name = ins.state['Name']
        print("ip:{}, state:{}".format(ip, state_name))

Cloudwatch says nothing too. I tried to add try/catch, but no luck...nothing.

VPC is common for EC2 instances and lambda, lambda is binded to private subnets and has the same security group as EC2 instance.

https://policysim.aws.amazon.com/ says me that the assigned role is working with EC2 well. Probably i missed smth in configuration but how to find out whats wrong?

Update:

to avoid confusion, I'm providing here the full code:

import os
import boto3

def get_ec2_instances(region, project, state):
    ec2_instances = []
    ec2 = boto3.resource('ec2', region_name=region)
    
    filters = [
        {
            'Name': 'tag:project',
            'Values': [project]
        },
        {
            'Name': 'instance-state-name',
            'Values': [state]
        }
    ]
    
    for ins in ec2.instances.all():
        ip = ins.private_ip_address
        state_name = ins.state['Name']
        print("ip:{}, state:{}".format(ip, state_name)) // is not printing already
        
    for instance in ec2.instances.filter(Filters=filters):
        ip = instance.private_ip_address
        state_name = instance.state['Name']
        print("ip:{}, state:{}".format(ip, state_name))
        ec2_instances.append(instance)
    
    return ec2_instances
    
def start_ec2_instance(region, project):
    instances_to_start = get_ec2_instances(region, project, 'stopped')
    instance_state_changed = 0
    for instance in instances_to_start:
        instance.start()
        instance_state_changed += 1
    return instance_state_changed
    
def stop_ec2_instance(region, project):
    instances_to_stop = get_ec2_instances(region, project, "running")
    instance_state_changed = 0
    for instance in instances_to_stop:
        instance.stop()
        instance_state_changed += 1
    return instance_state_changed

def describe_ec2_instances(region, project):
    instances = get_ec2_instances(region, project, "running")
    for instance in instances:
        print("Instance ID: {}, IP: {}, State: {}".format(instance.id, instance.private_ip_address, instance.state['Name']))
        
def lambda_handler(event, context):
    region = os.getenv('REGION', 'eu-central-1')
    project = os.getenv('PROJECT', 'ec2-instance')
    instance_state_changed = 0

    if event.get('action') == 'start':
        instance_state_changed = start_ec2_instance(region, project)
    elif event.get('action') == 'stop':
        instance_state_changed = stop_ec2_instance(region, project)
    elif event.get('action') == 'describe':
        describe_ec2_instances(region, project)
        
    return instance_state_changed

And this is the output that i have on the function running:

Test Event Name
Start

Response
{
  "errorMessage": "2023-06-16T16:17:37.510Z 75118795-2ffd-46ab-84e6-.... Task timed out after 3.02 seconds"
}

Function Logs
START RequestId: 75118795-2ffd-46ab-84e6-... Version: $LATEST
2023-06-16T16:17:37.510Z 75118795-2ffd-46ab-84e6-... Task timed out after 3.02 seconds

END RequestId: 75118795-2ffd-46ab-84e6-...
REPORT RequestId: 75118795-2ffd-46ab-84e6-...   Duration: 3018.80 ms    Billed Duration: 3000 ms    Memory Size: 128 MB Max Memory Used: 83 MB  Init Duration: 293.49 ms

Request ID
75118795-2ffd-46ab-84e6-...

update 2 (for the @jarmods comment: ) enter image description here

Anthony
  • 3,218
  • 3
  • 43
  • 73
  • 1
    It would appear that `ec2.instances.all()` is not returning a list of instances. Are you sure there are instances in that AWS Account in the requested Region? You could test it by using the AWS CLI `aws ec2 describe-instances --region YOUR_REGION` command with the same credentials. What does that command display? – John Rotenstein Jun 16 '23 at 13:22
  • 1
    The code you've shown doesn't appear to be a Lambda function. Where is the function handler, for example? Where is `region` defined? Also you mentioned that the Lambda function "has the same security group as EC2 instance" but that's largely irrelevant - the API request to retrieve the [list of EC2 instances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html) goes to a regional EC2 service endpoint, not to an EC2 instance. – jarmod Jun 16 '23 at 13:39
  • @JohnRotenstein yes, i'm sure here. region is the same. i can see that in the web console. And aws ec2 describe-instances shows all instances that i have there. – Anthony Jun 16 '23 at 16:12
  • @jarmod, i provided a simplified part of the code. There is a function with region parameter. I did not remove that, but i debugged that and it has the correct region. Logs show that, but everything stops after ec2.instances.all(). No logs tracking after that. – Anthony Jun 16 '23 at 16:14
  • 2
    "This code does not throw any errors" - no, actually it does: it times out. Which typically means that you don't have network connectivity. Which typically happens with Lambda because they're in a subnet that does not have a NAT or VPC Endpoint that lets them connect to AWS. – kdgregory Jun 16 '23 at 18:04
  • 1
    If the Lambda function doesn't need to access private endpoints in your VPC (e.g. an RDS MySQL database or an Elasticsearch cluster) and you don't have a compliance need for it to run in VPC, then don't configure a VPC subnet at all for the Lambda function. It will then be able to route requests to EC2. – jarmod Jun 16 '23 at 18:46
  • @jarmod i must pick at least one subnet for the lambda function. It's required field in configuration of lambda. – Anthony Jun 16 '23 at 18:52
  • It's not required. If you want it to be in your VPC then that's OK but that should be an affirmative decision that you make, because there are performance downsides. And if you do, [choose](https://stackoverflow.com/questions/52992085/why-cant-an-aws-lambda-function-inside-a-public-subnet-in-a-vpc-connect-to-the) a private subnet, not a public subnet, and make sure you have IGW and NAT configured for your VPC. – jarmod Jun 16 '23 at 19:15
  • @jarmod please see update2 – Anthony Jun 16 '23 at 19:21
  • That dialog is requiring a subnet ID because you chose to connect the Lambda function to your VPC. You don't need to enable VPC. – jarmod Jun 16 '23 at 19:22
  • @jarmod without VPC the result is still the same... – Anthony Jun 16 '23 at 19:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/254108/discussion-between-jarmod-and-anthony). – jarmod Jun 16 '23 at 19:27

2 Answers2

1

Your Lambda function's request to the EC2 service to retrieve the list of EC2 instances is timing out.

The typical reasons are either:

  1. your network connectivity is incorrect and your Lambda function has no network route to the EC2 service, or
  2. you have a lot of EC2 instances and the API request genuinely took longer to complete than your Lambda function timeout

The typical cause of issue #1 is that you connected the Lambda function to your VPC unnecessarily and your network configuration is incorrect, either:

  1. you selected a public subnet but you have no VPC Endpoint to the EC2 service, or

  2. you selected a private subnet but your VPC has no internet connectivity (it has no IGW/NAT) and it has no VPC Endpoint to the EC2 service

In your case, I believe the fix is to disconnect the Lambda function from your VPC (it's not required) and increase your Lambda function timeout.

jarmod
  • 71,565
  • 16
  • 115
  • 122
0

@jarmod is correct. A function handler is required is required for Lambda which makes sense because Lambdas are event-driven. You should be seeing an error that looks similar to this:

{
  "errorMessage": "Handler 'lambda_handler' missing on module 'lambda_function'",
  "errorType": "Runtime.HandlerNotFound",
  "requestId": "14d7be4f-b8fd-4a42-b370-e0fe99309a94",
  "stackTrace": []
}

Your code was able to run properly after adding a function handler and granting the Lambda access to EC2:

import os
import boto3

ec2 = boto3.resource('ec2')

def lambda_handler(event, context):
        for ins in ec2.instances.all():
                ip = ins.private_ip_address
                state_name = ins.state['Name']
                print("ip:{}, state:{}".format(ip, state_name))
John
  • 31
  • 3
  • i updated my full code and response in the text of the question – Anthony Jun 16 '23 at 16:19
  • What is your timeout configured to? The default is 3 seconds which can be changed under Configuration > General Configuration. – John Jun 16 '23 at 18:04