Starting/stopping Amazon EC2 instances using CLI and Python SDK

It’s a very good practice to scan your perimeter from the outside of your network, simulating an attacker. However, you will need to deploy the scanners somewhere to do this. Hosting on Amazon EC2 can be a good and cost-effective option, especially if you start instances with vulnerability scanners only when it’s necessary and keep them stopped at other time.

Amazon AWS python

So, in this post I will give some examples of how to manage Amazon instances automatically using the AWS CLI or Python SDK (boto3): start/stop the instance and get public ip address.

Let’s say we have a corporate account at aws.amazon.com

We login at https://[corporation].signin.aws.amazon.com/console

Amazon AWS login

And at https://eu-west-2.console.aws.amazon.com/ec2/v2/home?region=eu-west-2#Instances:sort=instanceId page (where eu-west-2 is our region), we see some of our instances:

Amazon instances

To automate the work with Amazon from CLI and python, we need:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

You can generate them here: https://console.aws.amazon.com/iam/home?#security_credential

Let’s say you got AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Next, we can try to turning on and off instances with CLI.

AWS CLI

Installing AWS CLI:

$ pip install awscli --upgrade --user

Check that it works:

$ aws --version
aws-cli/1.11.167 Python/2.7.5 Linux/3.10.0-693.2.2.el7.x86_64 botocore/1.7.XX

Configuring:

$ aws configure
AWS Access Key ID [None]: DERNRG34WHROIW$HT41
AWS Secret Access Key [None]: CCUz01piG+xRx532fhM+7vfEFr86UCQoGDq/fHY3
Default region name [None]: eu-west-2
Default output format [None]: json

Verify that authentication works. If you have a problem with permissions you will get:

$ aws ec2 describe-instances

An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.

If everything is ok, this command will return a long json describing instances. You can see the instance id here:

$ aws ec2 describe-instances

{
    "Reservations": [
        {
            "Instances": [
                {
                    "Monitoring": {
                        "State": "disabled"
                    }, 
                    "PublicDnsName": "", 
                    "StateReason": {
                        "Message": "Client.UserInitiatedShutdown: User initiated shutdown", 
                        "Code": "Client.UserInitiatedShutdown"
                    }, 
                    "State": {
                        "Code": 80, 
                        "Name": "stopped"
                    }, 
                    "EbsOptimized": false, 
                    "LaunchTime": "2017-XX-XXXXX:XX:XX.XXXX", 
                    "PrivateIpAddress": "XXX.XX.XX.XX", 
                    "ProductCodes": [], 
                    "VpcId": "vpc-XXXX", 
                    "StateTransitionReason": "User initiated (XXXX-XX-XX XX:XX:XX GMT)", 
                    "InstanceId": "i-wieituqp938tyqp94", 
                    "EnaSupport": true, 
                    "ImageId": "ami-XXXX", 
                    "PrivateDnsName": "ip-XXX-XX-XX-XX.eu-west-2.compute.internal", 
                    "KeyName": "EntryPoint", 
                    "SecurityGroups": [
                        {
                            "GroupName": "launch-wizard-2", 
                            "GroupId": "sg-XXXX"
                        }
                    ], 
                    "ClientToken": "XXXX", 
                    "SubnetId": "subnet-XXXX", 
                    "InstanceType": "XXXX", 
                    "NetworkInterfaces": [
                        {
                            "Status": "in-use", 
                            "MacAddress": "0a:eb:33:4d:aa:f1", 
                            "SourceDestCheck": true, 
                            "VpcId": "vpc-XXXX", 
                            "Description": "", 
                            "NetworkInterfaceId": "eni-XXXX", 
                            "PrivateIpAddresses": [
                                {
                                    "PrivateDnsName": "ip-XXX-XX-XX-XX.eu-west-2.compute.internal", 
                                    "Primary": true, 
                                    "PrivateIpAddress": "XXX.XX.XX.XX"
                                }
                            ], 
                            "PrivateDnsName": "ip-XXX-XX-XX-XX.eu-west-2.compute.internal", 
                            "Attachment": {
                                "Status": "attached", 
                                "DeviceIndex": 0, 
                                "DeleteOnTermination": true, 
                                "AttachmentId": "eni-attach-XXXX", 
                                "AttachTime": "XXXX-XX-XXXXX:XX:XX.XXXX"
                            }, 
                            "Groups": [
                                {
                                    "GroupName": "launch-wizard-2", 
                                    "GroupId": "sg-XXXXXXX"
                                }
                            ], 
                            "Ipv6Addresses": [], 
                            "OwnerId": "XXXXXXX", 
                            "SubnetId": "subnet-XXXXXXX", 
                            "PrivateIpAddress": "XXX.XX.XX.XX"
                        }
                    ], 
                    "SourceDestCheck": true, 
                    "Placement": {
                        "Tenancy": "default", 
                        "GroupName": "", 
                        "AvailabilityZone": "eu-west-2b"
                    }, 
                    "Hypervisor": "xen", 
                    "BlockDeviceMappings": [
                        {
                            "DeviceName": "/dev/sda1", 
                            "Ebs": {
                                "Status": "attached", 
                                "DeleteOnTermination": true, 
                                "VolumeId": "vol-XXXXXXX", 
                                "AttachTime": "XXXX-XX-XXXXX:XX:XX.XXXX"
                            }
                        }
                    ], 
                    "Architecture": "x86_64", 
                    "RootDeviceType": "ebs", 
                    "RootDeviceName": "/dev/sda1", 
                    "VirtualizationType": "hvm", 
                    "Tags": [
                        {
                            "Value": "linuxXX", 
                            "Key": "Name"
                        }
                    ], 
                    "AmiLaunchIndex": 0
                }
            ], 
            "ReservationId": "r-XXXXXXX", 
            "Groups": [], 
            "OwnerId": "XXXXXXX"
        }, 
        ...

Starting instance using this id:

$ aws ec2 start-instances --instance-ids i-wieituqp938tyqp94

{
    "StartingInstances": [
        {
            "InstanceId": "i-wieituqp938tyqp94", 
            "CurrentState": {
                "Code": 0, 
                "Name": "pending"
            }, 
            "PreviousState": {
                "Code": 80, 
                "Name": "stopped"
            }
        }
    ]
}

Checking the status:

$ aws ec2 describe-instance-status --instance-id i-wieituqp938tyqp94

{
    "InstanceStatuses": [
        {
            "InstanceId": "i-wieituqp938tyqp94", 
            "InstanceState": {
                "Code": 16, 
                "Name": "running"
            }, 
            "AvailabilityZone": "eu-west-2b", 
            "SystemStatus": {
                "Status": "initializing", 
                "Details": [
                    {
                        "Status": "initializing", 
                        "Name": "reachability"
                    }
                ]
            }, 
            "InstanceStatus": {
                "Status": "initializing", 
                "Details": [
                    {
                        "Status": "initializing", 
                        "Name": "reachability"
                    }
                ]
            }
        }
    ]
}

Great! It’s running.

Amazon instance run

We look at all the instances once again and see that an external IP address has appeared:

$ aws ec2 describe-instances

                    "NetworkInterfaces": [
                        {
                            "Status": "in-use", 
                            "MacAddress": "0a:eb:33:4d:aa:f1", 
                            "SourceDestCheck": true, 
                            "VpcId": "vpc-XXXX", 
                            "Description": "", 
                            "NetworkInterfaceId": "eni-XXXX", 
                            "PrivateIpAddresses": [
                                {
                                    "PrivateDnsName": "ip-XXX-XX-XX-XX.eu-west-2.compute.internal", 
                                    "PrivateIpAddress": "XXX.XX.XX.XX"
                                    "Primary": true, 
                                    "Association": {
                                        "PublicIp": "35.176.43.150", 
                                        "PublicDnsName": "ec2-XXX-XXX-XXX-XXX.eu-west-2.compute.amazonaws.com", 
                                        "IpOwnerId": "amazon"
                                    }
                                }
                            ], 
...
                    ], 

And now, when the work with instance is finnished, we are stopping the instance by id:

$ aws ec2 stop-instances --instance-ids i-wieituqp938tyqp94

{
    "StoppingInstances": [
        {
            "InstanceId": "i-wieituqp938tyqp94", 
            "CurrentState": {
                "Code": 64, 
                "Name": "stopping"
            }, 
            "PreviousState": {
                "Code": 16, 
                "Name": "running"
            }
        }
    ]
}

Amazon AWS Python SDK

Now let’s try the same thing using Python

Installing SDK:

sudo pip install -U boto

Looking for stopped instances:

import boto3
ec2 = boto3.resource(
    'ec2',
    aws_access_key_id="DERNRG34WHROIW$HT41",
    aws_secret_access_key="CCUz01piG+xRx532fhM+7vfEFr86UCQoGDq/fHY3",
    region_name='eu-west-2'
)

instances = ec2.instances.filter(
    Filters=[{'Name': 'instance-state-name', 'Values': ['stopped']}])

for instance in instances:
    print(instance.id)

Here they are:

i-quae4itvyqn738ty3
i-wieituqp938tyqp94

Ok, we see our instance i-wieituqp938tyqp94 let’s try to start it:

ids = ["i-wieituqp938tyqp94"]
status = ec2.instances.filter(InstanceIds=ids).start()
print(status)

Output:

[{u'StartingInstances': [{u'InstanceId': 'i-wieituqp938tyqp94', u'CurrentState': {u'Code': 0, u'Name': 'pending'}, u'PreviousState': {u'Code': 80, u'Name': 'stopped'}}], 'ResponseMetadata': {'RetryAttempts': 0, 'HTTPStatusCode': 200, 'RequestId': 'XXXX', 'HTTPHeaders': {'transfer-encoding': 'chunked', 'vary': 'Accept-Encoding', 'server': 'AmazonEC2', 'content-type': 'text/xml;charset=UTF-8', 'date': 'XXXX'}}}]

Ok, looks like it’s started. Let’s try to find it among the running instances and get out the external IP-address:

instances = ec2.instances.filter(
    Filters=[{'Name': 'instance-state-name', 'Values': ['running']}])

for instance in instances:
    print(instance.id)
    print(instance.public_ip_address)

Output:

i-wieituqp938tyqp94
35.176.43.151

Finally, we stop this instance:

ids = ["i-wieituqp938tyqp94"]
status = ec2.instances.filter(InstanceIds=ids).stop()
print(status)

Output:

[{u'StoppingInstances': [{u'InstanceId': 'i-wieituqp938tyqp94', u'CurrentState': {u'Code': 64, u'Name': 'stopping'}, u'PreviousState': {u'Code': 16, u'Name': 'running'}}], 'ResponseMetadata': {'RetryAttempts': 0, 'HTTPStatusCode': 200, 'RequestId': 'XXXX', 'HTTPHeaders': {'transfer-encoding': 'chunked', 'vary': 'Accept-Encoding', 'server': 'AmazonEC2', 'content-type': 'text/xml;charset=UTF-8', 'date': 'XXXX'}}}]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.