Skip to content

aws sysops 03 cli ec2 vpc

ebarault edited this page May 18, 2017 · 5 revisions

Using the AWS CLI to manage EC2 VPCs and subnets

AWS CLI EC2 reference

Step 1: Create a VPC and Subnets

Create a VPC with a 10.0.0.0/16 CIDR block.

$ aws ec2 create-vpc --cidr-block 10.0.0.0/16
{
    "Vpc": {
        "VpcId": "vpc-e8099680",
        "InstanceTenancy": "default",
        "Tags": [],
        "Ipv6CidrBlockAssociationSet": [],
        "State": "pending",
        "DhcpOptionsId": "dopt-a9548fc1",
        "CidrBlock": "10.0.0.0/16",
        "IsDefault": false
    }
}

Note the VpcId: vpc-e8099680

Create a Subnet with a 10.0.0.0/24 CIDR block.

$ aws ec2 create-subnet --vpc-id vpc-e8099680 --cidr-block 10.0.0.0/24
{
    "Subnet": {
        "VpcId": "vpc-e8099680",
        "AvailableIpAddressCount": 251,
        "MapPublicIpOnLaunch": false,
        "DefaultForAz": false,
        "Ipv6CidrBlockAssociationSet": [],
        "State": "pending",
        "AvailabilityZone": "eu-central-1a",
        "SubnetId": "subnet-7b114a13",
        "CidrBlock": "10.0.0.0/24",
        "AssignIpv6AddressOnCreation": false
    }
}

Create a second subnet with a 10.0.1.0/24 CIDR block.

$ aws ec2 create-subnet --vpc-id vpc-e8099680 --cidr-block 10.0.1.0/24
{
    "Subnet": {
        "SubnetId": "subnet-d01249b8",
		...
    }
}

Step 2: Make one of the Subnets Public

To make one of the subnets public and give it access to Internet, we need to:

  • attach an Internet gateway to the VPC
  • create a custom route table
    • configure a default route to the Internet gateway
  • associate the subnet to this routing table

Create an Internet gateway and attach the VPC to it

$ aws ec2 create-internet-gateway
{
    "InternetGateway": {
        "Tags": [],
        "InternetGatewayId": "igw-54988f3d",
        "Attachments": []
    }
}

Note the InternetGatewayId: igw-54988f3d

$ aws ec2 attach-internet-gateway \
  --vpc-id vpc-e8099680 --internet-gateway-id igw-54988f3d
# The command does not return anything

Create a routing table and a default route to the Internet gateway

$ aws ec2 create-route-table --vpc-id vpc-e8099680
{
    "RouteTable": {
        "Associations": [],
        "RouteTableId": "rtb-5a095d32",
        "VpcId": "vpc-e8099680",
        "PropagatingVgws": [],
        "Tags": [],
        "Routes": [
            {
                "GatewayId": "local",
                "DestinationCidrBlock": "10.0.0.0/16",
                "State": "active",
                "Origin": "CreateRouteTable"
            }
        ]
    }
}

NOTE:

  • The first Routing Table created for a VPC is elected the VPC's Main Routing Table
  • Any subnet which is not specifically associated with a Routing Table uses the Main Routing Table of the VPC
  • Our 2 new subnets will hence use the Routing Table we just created as their default

Note the RouteTableId: rtb-5a095d32

Next we create the default route to the Internet gateway with the create-route command:

$ aws ec2 create-route \
  --route-table-id rtb-5a095d32 \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id igw-54988f3d
{
    "Return": true
}

Confirm the new route has been created using the describe-route-tables command:

$ aws ec2 describe-route-tables --route-table-id rtb-5a095d32
{
    "RouteTables": [
        {
            "Associations": [],
            "RouteTableId": "rtb-5a095d32",
            "VpcId": "vpc-e8099680",
            "PropagatingVgws": [],
            "Tags": [],
            "Routes": [
                {
                    "GatewayId": "local",
                    "DestinationCidrBlock": "10.0.0.0/16",
                    "State": "active",
                    "Origin": "CreateRouteTable"
                },
                {
                    "GatewayId": "igw-54988f3d",
                    "DestinationCidrBlock": "0.0.0.0/0",
                    "State": "active",
                    "Origin": "CreateRoute"
                }
            ]
        }
    ]
}

Now list the VPC's Subnets to get their SubnetIds

$ aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-e8099680"

Go further!

  • This command returns a lot of information, how would you use the --query` parameter to only fetch SubnetId and CidrBlock for each subnet and show the result as a table?
$ aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-e8099680" \
  --query 'Subnets[*].{SubnetId:SubnetId, CidrBlock:CidrBlock}' \
  --output table
------------------------------------
|          DescribeSubnets         |
+--------------+-------------------+
|   CidrBlock  |     SubnetId      |
+--------------+-------------------+
|  10.0.1.0/24 |  subnet-d01249b8  |
|  10.0.0.0/24 |  subnet-7b114a13  |
+--------------+-------------------+

Choose one subnet and note the SubnetId: subnet-7b114a13

Associate the route table with the subnet

$ aws ec2 associate-route-table \
  --subnet-id subnet-7b114a13 \
  --route-table-id rtb-5a095d32
{
    "AssociationId": "rtbassoc-ec4e8c87"
}

NOTE:

  • We now modify the public IP addressing behavior of the public subnet with the --map-public-ip-on-launch command so that an instance launched into the subnet automatically receives a public IP address, otherwise we should associate an Elastic IP address to the instance.
$ aws ec2 modify-subnet-attribute --subnet-id subnet-7b114a13 \
  --map-public-ip-on-launch
# The command does not return anything

Launch an instance in the public subnet and connect to it

First we create a security group in the VPC with a rule that allows SSH access from anywhere:

$ aws ec2 create-security-group --group-name SSHAccess \
  --vpc-id vpc-e8099680 \
  --description "Security group for SSH access"
{
    "GroupId": "sg-bdc64dd6"
}

Note the Security GroupId: sg-bdc64dd6

Then we create the rule for SSH access from anywhere:

$ aws ec2 authorize-security-group-ingress --group-id sg-bdc64dd6 \
  --protocol tcp --port 22 --cidr 0.0.0.0/0
# The command does not return anything

Next, we launch an instance into the public subnet:

$ aws ec2 run-instances --image-id ami-060cde69 --count 1 \
  --instance-type t2.micro \
  --key-name sysops \
  --security-group-ids sg-bdc64dd6 \
  --subnet-id subnet-7b114a13
# ami-060cde69 is AWS image for Ubuntu 16.04

Note the InstanceId: i-0ca725b4fbe6af75e

Before connecting to the newly created instance, let's confirm it is up and running.

$ aws ec2 describe-instances --instance-id i-0ca725b4fbe6af75e --query \
  'Reservations[*].Instances[*].{
    id:InstanceId,
    zone: Placement.AvailabilityZone,
    status: State.Name,
    publicIpv4: PublicIpAddress
  }'
[
    [
        {
            "status": "running",
            "publicIpv4": "52.59.17.117",
            "id": "i-0ca725b4fbe6af75e",
            "zone": "eu-central-1a"
        }
    ]
]

Note the publicIpv4: 52.59.17.117

Finally connect to the instance in the public network to confirm your setup is OK and the instance has access to Internet:

$ ssh -i "sysops.pem" [email protected]
# NOTE: login as user ec2-user if you launched an Amazon Linux AMI instance

πŸŽ‰πŸŽ‰πŸŽ‰ tada!

Step 3: Make the other Subnet private and implement a NAT gateway

To make one the other subnet private and give it access to Internet through a NAT gateway, we need:

  • create a NAT gateway inside the public* subnet
  • create a custom route table
    • configure a defaut route to the NAT gateway
  • associate the subnet to this routing table

Create a NAT gateway

The NAT gateway needs to have a route to the VPC's Internet gateway. Given our set-up the easiest way is to place it in the public subnet.

First we need to allocate a new Elastic IP for the NAT gateway with the allocate-address command. It will be the exit point the gateway to Internet.

$ aws ec2 allocate-address
{
    "PublicIp": "52.28.150.124", 
    "Domain": "vpc", 
    "AllocationId": "eipalloc-6187d908"
}

Note the AllocationId: eipalloc-6187d908

Next we create the NAT gateway with the create-nat-gateway command which associates it the allocated EIP:

$ create-nat-gateway \
  --subnet-id subnet-d01249b8 \
  --allocation-id eipalloc-6187d908
{
    "NatGateway": {
        "NatGatewayAddresses": [
            {
                "AllocationId": "eipalloc-6187d908"
            }
        ], 
        "VpcId": "vpc-315bc359", 
        "State": "pending", 
        "NatGatewayId": "nat-0ba16025223779534", 
        "SubnetId": "subnet-d01249b8", 
        "CreateTime": "2017-05-17T19:09:51.097Z"
    }
}

Note the NatGatewayId: nat-0ba16025223779534

Create another routing table and a default route to the NAT gateway

$ aws ec2 create-route-table --vpc-id vpc-e8099680
{
    "RouteTable": {
        "Associations": [], 
        "RouteTableId": "rtb-5add8c32", 
        // ...
    }
}

Note the RouteTableId: rtb-5add8c32

Next we create the default route to the Internet gateway with the create-route command:

$ aws ec2 create-route \
  --route-table-id rtb-5add8c32 \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id nat-0ba16025223779534
{
    "Return": true
}

Associate the route table with the subnet

$ aws ec2 associate-route-table \
  --subnet-id subnet-d01249b8 \
  --route-table-id rtb-5add8c32
{
    "AssociationId": "rtbassoc-bc4e4cd7"
}

Our second subnet is now defacto private has it has no direct route to the Internet gateway.

Launch an instance in the private subnet and connect to it

$ aws ec2 run-instances --image-id ami-060cde69 --count 1 \
  --instance-type t2.micro \
  --key-name sysops \
  --security-group-ids sg-bdc64dd6 \
  --subnet-id subnet-d01249b8
# ami-060cde69 is AWS image for Ubuntu 16.04

Note the InstanceId: i-0404ddc91d6a6145c

Let's get the private IP address of this new instance:

$ aws ec2 describe-instances --instance-id i-0404ddc91d6a6145c --query \
  'Reservations[*].Instances[*].NetworkInterfaces[*].PrivateIpAddresses[*].{
    privateIP: PrivateIpAddress
  }'
[
    [
        {
            "privateIP": "10.0.1.114"
        }
    ]
]

Note the privateIP: 10.0.1.114

As the new instance has no public IP address, we cannot connect to it directly. We will use the other instance, connected inside the public subnet, to bounce to our private instance.

$ ssh -i "sysops.pem" [email protected]

# once logged in we need first to import our private key, then we ssh to the private instance:

$ ssh -i "sysops.pem" [email protected]

# now we confirm we have access to the internet by pinging google.command

$ ping google.com
# PING google.com (216.58.207.78): 56 data bytes
# 64 bytes from 216.58.207.78: icmp_seq=0 ttl=55 time=14.632 ms
# ...

πŸ‘ Congratulations ! πŸŽ‰πŸŽ‰πŸŽ‰

Clean Up (Optional)

# instances
$ aws ec2 terminate-instances --instance-ids i-0ca725b4fbe6af75e i-0404ddc91d6a6145c

# security group
$ aws ec2 delete-security-group --group-id sg-bdc64dd6

# subnets
$ aws ec2 delete-subnet --subnet-id subnet-7b114a13
$ aws ec2 delete-subnet --subnet-id subnet-d01249b8

# route table
$ aws ec2 delete-route-table --route-table-id rtb-5a095d32
$ aws ec2 delete-route-table --route-table-id rtb-5add8c32

# internet gateway
$ aws ec2 detach-internet-gateway --internet-gateway-id igw-54988f3d --vpc-id vpc-e8099680
$ aws ec2 delete-internet-gateway --internet-gateway-id igw-54988f3d

# nat gateway
$ aws ec2 delete-nat-gateway --nat-gateway-id nat-0ba16025223779534

# vpc
$ aws ec2 delete-vpc --vpc-id vpc-e8099680