Automatically remove automatic IPv4 address assignment to Amazon EC2 instances

nishikawaakira - Jun 5 - - Dev Community

It is now possible to dynamically remove and add public IPv4 addresses that were recently automatically assigned to an EC2 instance.

Accordingly, this article will try to automatically delete the public IPv4 address that was assigned to EC2 by default.

Configuration

This time, I used the configuration AWS Config -> Amazon EventBridge -> AWS Lambda. Actually, AWS Config -> AWS Systems Manager can also be configured using Automation runbook, which is smarter, but this time I would like to introduce the method using AWS Lambda.

Configure AWS Config

First, configure AWS Config." ec2-instance-no-public-ip". This managed rule, however, does not enable evaluation of provisioned resources. However, the evaluation mode of this managed rule enables evaluation of provisioned resources, and evaluation is performed only after the instance is started. Therefore, if you want to modify it at startup, you need to prepare your own rules. On the other hand, if you want to target EC2 after startup, this rule may be suitable.

That's all there is to AWS Config. It is very easy.

Create AWS Lambda

In fact, here is where I run into a bit of a snag. In this case, I have chosen Python, but in general, if you want to use Python in Lambda to execute APIs, you will need to use a library called Boto3. Lambda comes standard with this library, but if the API specification has just been changed, as in this case, Boto3 is not supported, or the version of Boto3 in Lambda itself is a bit old, and an error will occur.

API Specification and send a request as follows,

Actual error
Unknown parameter in input: "AssociatePublicIpAddress"
You may see that you have been told, "Unknown parameter in input: "AssociatePublicIpAddress".

So, let's first find out how many versions of Boto3 Lambda has.
Let's run the following in Lambda.
print Boto3 version

When I ran this, it showed 1.34.42 (as of 5/14/2024).
However, the latest version of Boto3 at this time is 1.34.103. And I see that the ability to dynamically remove and add auto-assigned public IPv4 addresses for this EC2 instance was added in 1.34.91.

Boto3 version

I found that it is not possible to run the application as it is, so we need to create a Lambda Layer.

Incidentally, as a side note, the same results were obtained when making API calls with YAML in Systems Manager, so it seems that Systems Manager also takes some time to catch up with the latest API.
So, keep in mind that you will need to wait a few days after a new specification is implemented.

I will not discuss how to create the Lambda Layer, but once it has been created, implement it as follows.

import json
import boto3

def modify_network_interface_attribute(network_interface_id):
    ec2 = boto3.client('ec2')

    try:
        response = ec2.modify_network_interface_attribute(
            NetworkInterfaceId=network_interface_id, # Specify the target Network Interface ID.
            AssociatePublicIpAddress=False # False deletes the public IPv4 address
        )
        print("Network Interface Modified Successfully:")
        print(response)
    except Exception as e:
        print("An error occurred:", e)



def lambda_handler(event, context):

    detail = event['detail']
    # network_interface_id = detail['configurationItem']['configuration']['networkInterfaceId']

    network_interface_ids = []

    # Get a list of resources that have changed
    result = detail.get('newEvaluationResult')
    identifier = result.get('evaluationResultIdentifier')
    configuration_item = identifier.get('evaluationResultQualifier')

    # Check if the resource type is an EC2 instance
    if configuration_item and configuration_item['resourceType'] == 'AWS::EC2::Instance':
        instance_id = configuration_item['resourceId']

        ec2_client = boto3.client('ec2')

        # Get instance details
        response = ec2_client.describe_instances(InstanceIds=[instance_id])
        reservations = response['Reservations']

        # Stop auto-assign of all network interface IDs
        for reservation in reservations:
            for instance in reservation['Instances']:
                for interface in instance['NetworkInterfaces']:
                    network_interface_ids.append(interface['NetworkInterfaceId'])
                    modify_network_interface_attribute(interface['NetworkInterfaceId'])

    print("Network Interface IDs: ", network_interface_ids)

    return {
        'statusCode': 200,
        'body': json.dumps('Success!!')
    }

Enter fullscreen mode Exit fullscreen mode

It is written in a rather crude manner and returns, "Success! but if you just want to remove the public IP address automatically, you can do it with this.

Testing AWS Lambda

The following can be used for testing. Please enter the instance ID you have set up appropriately in resourceId, and then run the test.

{
  "version": "0",
  "id": "ID",
  "detail-type": "Config Rules Compliance Change",
  "source": "aws.config",
  "account": "[AWS Account ID]",
  "time": "2024-05-19T12:19:54Z",
  "region": "ap-northeast-1",
  "resources": [],
  "detail": {
    "resourceId": "[Instance ID]",
    "awsRegion": "ap-northeast-1",
    "awsAccountId": "[AWS Account ID]",
    "configRuleName": "ec2-instance-no-public-ip",
    "recordVersion": "1.0",
    "configRuleARN": "arn:aws:config:ap-northeast-1:[AWS Account ID]:config-rule/config-rule-qx4cpe",
    "messageType": "ComplianceChangeNotification",
    "newEvaluationResult": {
      "evaluationResultIdentifier": {
        "evaluationResultQualifier": {
          "configRuleName": "ec2-instance-no-public-ip",
          "resourceType": "AWS::EC2::Instance",
          "resourceId": "[Instance ID]",
          "evaluationMode": "DETECTIVE"
        },
        "orderingTimestamp": "2024-05-19T00:03:43.749Z"
      },
      "complianceType": "NON_COMPLIANT",
      "resultRecordedTime": "2024-05-19T12:19:53.922Z",
      "configRuleInvokedTime": "2024-05-19T12:19:53.696Z",
      "annotation": "This Amazon EC2 Instance uses a public IP."
    },
    "notificationCreationTime": "2024-05-19T12:19:54.417Z",
    "resourceType": "AWS::EC2::Instance"
  }
}
Enter fullscreen mode Exit fullscreen mode

If it works correctly, the configuration of the instance in question should change as follows.
Auto-assign public IP disabled

Configure Amazon Event Bridge

Finally, let's create the rules for EventBridge.

The event pattern is as follows AWS Config is the source and calls Lambda based on Compliance Change for ec2-instance-no-public-ip.

{
  "source": ["aws.config"],
  "detail-type": ["Config Rules Compliance Change"],
  "detail": {
    "configRuleName": ["ec2-instance-no-public-ip"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Select AWS Lambda function in Target and select AWS Lambda function you just created.

All that remains is to create another instance and confirm that the modifications have been made automatically.
You may want to monitor it to make sure it is working properly.
If it is not working properly, try logging in AWS Lambda function and debugging to see if the Lambda itself is being called properly.

Conclusion

I was able to remove the public IP from EC2 more easily than I expected, but it is not as simple as using AWS Systems Manager Automation runbook (my own subjective opinion), and requires a fair amount of coding knowledge. I'm writing roughly again this time, but we should also pick up error cases more thoroughly, and we need to pay attention to such things.
Next time, I would like to do the same assignment to see how it can be written using AWS Systems Manager Automation runbook.

. .