I recently did something similar using AWS Config -> Amazon EventBridge -> AWS Lambda in another article. another article. In this case, I would like to use an Automation runbook in AWS Systems Manager to automatically delete the public IPv4 addresses of Amazon EC2 instances.
I would like to tell you that you don't need many programming skills to use an Automation runbook to gain control.
Create an AWS Systems Manager Automation runbook
Now, let's go ahead and create an AWS Systems Manager Automation runbook.
Initially, the following screen is opened, with the design tab now selected.
You can create a runbook using the graphical interface in this way, or you can select the {} Code tab and edit YAML or JSON directly.
Writing an Automation runbook
When you visually add parts to the Automation runbook, you will see YAML being described. You can also write Python within this YAML. Take, for example, AWS-BulkDeleteAssociation in the managed runbook.
The following is written in Python, but this is quite difficult without some programming skills. This is not smart, and for the purpose of this article, I want to show that it can be implemented by people with no programming knowledge, so I want to prove that we can do it without using services like AWS Lambda!
First of all, I will create a runbook with only the minimum required elements. Select the "AWS APIs" tab under the search box and enter "modifynetwork" in the search box to find the ModifyNetworkInterfaceAttribute of AWS API to be used this time. Drag and drop it to the middle of the "ModifyNetworkInterfaceAttribute" between Start and End.
{} Open the Code tab. You will see that "ModifyNetworkInterfaceAttribute" is being hit as an API, as shown below.
We want to put arguments here, as you can see from the AWS Lambda code in the previous article. It seems to be sufficient to enter NetworkInterfaceId.γAPI Reference is here You can also see that you need to set AssociatePublicIpAddress to false to prevent the automatic assignment of Public IPv4 addresses.
Let's describe it as follows. The regular expression is written assuming that NetworkInterfaceId starts with "eni-" and that a combination of alphabets and numbers is entered. Also, entering (Required) in the parameter description will make it a required parameter. It is not mandatory to pass assumeRole as a parameter, but it is always recommended to do so. This is because you may want to use this runbook by assuming it from another AWS account. For example, there are use cases where a runbook is distributed to each account in CloudFormation StackSets, and the runbook can be assumed and executed from an administrative account.
schemaVersion: '0.3'
description: Disable Public IPv4 on EC2.
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
AutomationAssumeRole:
type: String
description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
default: ''
NetworkInterfaceId:
type: String
description: (Required) Network Interface Id
allowedPattern: ^eni-[a-z0-9]+$
mainSteps:
- name: ModifyNetworkInterfaceAttribute
action: aws:executeAwsApi
isEnd: true
inputs:
Service: ec2
Api: ModifyNetworkInterfaceAttribute
NetworkInterfaceId: '{{ NetworkInterfaceId }}'
AssociatePublicIpAddress: false
Now, enter a suitable name in NewRunbook and press "Create runbook".
Test Run Automation runbook
Open the runbook you have created. You can find the runbook you created in the "Owned by me" tab.
Open it and click on Execute automation.
Before you do so, create an EC2 instance. Check the network interface ID of the Amazon EC2 instance.
Try entering a Network Interface ID starting with "eni-" in the "NetworkInterfaceId" field of the Input parameters and click Execute.
Check that the public IP address in the network interface ID on the EC2 instance has been removed. You will see that it has been deleted. We will now see if this is really the end of the process.
AWS Config Settings
As before, I will use the "ec2-instance-no-public-ip" managed rule. Open this Config rule and click on "Manage remediation".
Choose your own AWS Systems Manager Automation runbook.
Notice that only InstanceId can be passed as a Resource ID parameter.
Oops, it seems that the network interface ID cannot be passed here, even though I really want to pass it here. In other words, there seems to be no way to pass the network interface ID dynamically.
Get a List of Network Interface IDs from EC2 Instance ID
We have no choice but to change course. You will find that you need to get the network interface ID from the EC2 instance to pass it on. There are two main methods. The first is to use DescribeInstances to get all EC2 information and extract only the necessary information. The second is to use DescribeNetworkInterfaces to get just the network interface information.
This time, the information on the EC2 instance will hardly be used, so we will hit the DescribeNetworkInterfaces API to get the information on network interfaces only.
So, as shown below, the DescribeNetworkInterfaces API is used to create the part that obtains information on network interfaces.
The code is as follows.
schemaVersion: '0.3'
description: Disable Public IPv4 on EC2.
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
InstanceId:
type: String
description: The ID of EC2 instance
AutomationAssumeRole:
type: String
description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
default: ''
mainSteps:
- name: DescribeNetworkInterfaces
action: aws:executeAwsApi
isEnd: true
inputs:
Service: ec2
Api: DescribeNetworkInterfaces
Filters:
- Name: attachment.instance-id
Values:
- '{{InstanceId}}'
outputs:
- Name: NetworkInterfaceIds
Selector: $.NetworkInterfaces..NetworkInterfaceId
Type: StringList
Network interface IDs can now be retrieved as a StringList. There is a caveat here: if you look at the API specification for DescribeNetworkInterfaces, the Request Parameter is written as Filter.N, but at first, it was not clear how to write it in YAML. The answer I arrived at is below, which is closer to the way CLIγis written.
Filters:
- Name: attachment.instance-id
Values:
- '{{InstanceId}}'
Another is the Selector in outputs.
Selector: $.NetworkInterfaces..NetworkInterfaceId
This Selector allows you to specify which values are output and used in the next step. As an EC2 instance may have multiple network interfaces, it is necessary to retrieve them all, but when using Selector: $.NetworkInterfaces, only a MapList (like an associative array) can be specified for Type. In this case, the loop in the runbook cannot be used. Therefore, you need to get the array as a StringList, which can be done by writing "..". By writing this, the list of specified items can be retrieved as a StringList.
Delete the Public IPv4 Address of the Relevant Network Interface ID
Now that you have reached this point, the rest is easy. Create a runbook like the following.
The final code is as follows.
schemaVersion: '0.3'
description: Disable Public IPv4 on EC2.
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
InstanceId:
type: String
description: The ID of EC2 instance
AutomationAssumeRole:
type: String
description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
default: ''
mainSteps:
- name: DescribeNetworkInterfaces
action: aws:executeAwsApi
nextStep: Loop
isEnd: false
inputs:
Service: ec2
Api: DescribeNetworkInterfaces
Filters:
- Name: attachment.instance-id
Values:
- '{{InstanceId}}'
outputs:
- Name: NetworkInterfaceIds
Selector: $.NetworkInterfaces..NetworkInterfaceId
Type: StringList
- name: Loop
action: aws:loop
isEnd: true
inputs:
Iterators: '{{ DescribeNetworkInterfaces.NetworkInterfaceIds }}'
Steps:
- name: ModifyNetworkInterfaceAttribute
action: aws:executeAwsApi
isEnd: true
inputs:
Service: ec2
Api: ModifyNetworkInterfaceAttribute
NetworkInterfaceId: '{{ Loop.CurrentIteratorValue }}'
AssociatePublicIpAddress: false
"Create new version", then select "Create new default version" to create a new document.
Before you start testing, launch an EC2 instance and attach several Network Interfaces.
Then open the runbook, click on "Execute automation", enter the InstanceId and press Execute.
Check the previous screen to see if it has been disabled properly. If it is disabled as shown in the image below, you have succeeded.
Conclusion
In this way, control can be exercised without programming knowledge. However, when you write it like this, you might think it's easy to do! And while that is the aim, there are some real quirks in the way YAML is written, and I had a lot of trouble with it. At first, I thought that to get the network interface IDs as a list, I would need to write some Python code. And indeed, the first version of this blog did. While researching, I realized that it could be retrieved using "..".
I'm going to accumulate this kind of runbook writing style and work towards a situation where anyone can control the AWS environment!
Thank you for reading to the end.
Top comments (0)