DEV Community

Frederic
Frederic

Posted on

Deploy, update, and destroy AWS EC2 Instance automatically

This tutorial explains the deployment automation of a simple AWS EC2 instance with the GruCloud AWS provider.

Instead of manually creating, updating, and destroying EC2 instances, the infrastructure will be described as Javascript code. The GruCloud CLI then reads this code, retrieves the lives resources through the AWS API, and decides what needs to be created, updated, or destroyed.

graph-diagram-ec2.svg

Requirements

AWS Account

Ensure access to the Amazon Console and create an account if necessary.

AWS CLI

Ensure the AWS CLI is installed and configured:

aws --version
Enter fullscreen mode Exit fullscreen mode

If not, visit https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html

Access and Secret Key

Visit the security credentials

  • Click on Access key (access key ID and secret access key).
  • Click on the button Create New Access Key.

Write down the AWSAccessKeyId and AWSSecretKey

In a further episode, the access and secret key will be obtained from a dedicated IAM user with the correct role and policy.

Configure AWS CLI

Configure the account with the previously obtained AWSAccessKeyId and AWSSecretKey, as well as the region, for instance us-east-1

aws configure
Enter fullscreen mode Exit fullscreen mode

Getting the GruCloud CLI

GruCloud is written in Javascript running on Node.js. Check if node is present on your system:

node --version
Enter fullscreen mode Exit fullscreen mode

The version must be greater than 14

Install the GrucCloud command-line utility gc with npm

npm i -g @grucloud/core
Enter fullscreen mode Exit fullscreen mode

Check the current version of gc:

gc --version
Enter fullscreen mode Exit fullscreen mode

Code Architecture

In this section, we'll create the files needed to describe an infrastructure with GruCloud:

  • package.json: specifies the npm dependencies and other information.
  • config.js: the config function.
  • iac.js: exports createStack with provider and resources associated
  • hooks.js: optionally provides hook functions called after deployment or destruction.

The source code for this example in on GitHub.

Create a new project

Create a new directory, for instance ec2-example:

mkdir ec2-example
cd ec2-example
Enter fullscreen mode Exit fullscreen mode

package.json

Let's create a new package.json with the npm init command:

npm init
Enter fullscreen mode Exit fullscreen mode

Let's install the GruCloud AWS provider called @grucloud/provider-aws, as well as the GruCloud core library @grucloud/core.

npm install @grucloud/core @grucloud/provider-aws
Enter fullscreen mode Exit fullscreen mode

config.js

The configuration is a Javascript function located in config.js

// config.js
const pkg = require("./package.json");
module.exports = ({ stage }) => ({
  projectName: pkg.name,
  ec2Instance: {
    name: "web-server",
    properties: {
      InstanceType: "t2.micro",
      ImageId: "ami-00f6a0c18edb19300", // Ubuntu 18.04
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

You will have to find out the ImageId for your specific region. One way to retrieve to list of images is with the aws cli:

aws ec2 describe-images --filters "Name=description,Values=Ubuntu Server 20.04 LTS" "Name=architecture,Values=x86_64"
Enter fullscreen mode Exit fullscreen mode

This step will be automated in a future episode with the help of the Amazon Managed Image resource.

iac.js

Create a file called iac.js which stands for infrastructure as code.
We'll first import AwsProvider from @grucloud/provider-aws

iac.js must export the createStack function which returns the provider and the resources.

Then, instantiate AwsProvider by providing the config function.

In the case, an EC2 Instance is defined with provider.makeEC2.

// iac.js
const { AwsProvider } = require("@grucloud/provider-aws");

exports.createStack = async ({ stage }) => {
  const provider = AwsProvider({ config: require("./config"), stage });
  const { config } = provider;
  const ec2Instance = await provider.makeEC2({
    name: config.ec2Instance.name,
    properties: () => config.ec2Instance.properties,
  });

  return {
    provider,
    resources: { ec2Instance },
  };
};
Enter fullscreen mode Exit fullscreen mode

GruCloud CLI

Info

As a way to verify that gc can connect to the AWS API, one can use the info command:

gc info
Enter fullscreen mode Exit fullscreen mode
  - provider:
      name: aws
      type: aws
    stage: dev
    accountId: 840541460064
    zone: eu-west-2a
    config:
      projectName: ec2-simple
      ec2Instance:
        name: web-server
        properties:
          InstanceType: t2.micro
          ImageId: ami-00f6a0c18edb19300
      stage: dev
      zone: [object Function]
      accountId: [object Function]
      region: eu-west-2

Command "gc info" executed in 1s
Enter fullscreen mode Exit fullscreen mode

Deploy

We are ready to deploy with apply command:

gc apply
Enter fullscreen mode Exit fullscreen mode
Querying resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Listing 6/6
  ✓ Querying
    ✓ EC2 1/1
┌──────────────────────────────────────────────────────────────────────────┐
│ 1 EC2 from aws                                                           │
├────────────┬──────────┬──────────────────────────────────────────────────┤
│ Name       │ Action   │ Data                                             │
├────────────┼──────────┼──────────────────────────────────────────────────┤
│ web-server │ CREATE   │ InstanceType: t2.micro                           │
│            │          │ ImageId: ami-00f6a0c18edb19300                   │
│            │          │ MaxCount: 1                                      │
│            │          │ MinCount: 1                                      │
│            │          │ TagSpecifications:                               │
│            │          │   - ResourceType: instance                       │
│            │          │     Tags:                                        │
│            │          │       - Key: Name                                │
│            │          │         Value: web-server                        │
│            │          │       - Key: ManagedBy                           │
│            │          │         Value: GruCloud                          │
│            │          │       - Key: CreatedByProvider                   │
│            │          │         Value: aws                               │
│            │          │       - Key: stage                               │
│            │          │         Value: dev                               │
│            │          │       - Key: projectName                         │
│            │          │         Value: ec2-simple                        │
│            │          │                                                  │
└────────────┴──────────┴──────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────────┐
│ Plan summary for provider aws                                           │
├─────────────────────────────────────────────────────────────────────────┤
│ DEPLOY RESOURCES                                                        │
├────────────────────┬────────────────────────────────────────────────────┤
│ EC2                │ web-server                                         │
└────────────────────┴────────────────────────────────────────────────────┘
? Are you sure to deploy 1 resource, 1 type on 1 provider? › (y/N)
Enter fullscreen mode Exit fullscreen mode

At this point, you are given the opportunity to look at what is going to be deployed.
Type y to accept:

Deploying resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Deploying
    ✓ EC2 1/1
1 resource deployed of 1 type and 1 provider
Running OnDeployedGlobal resources on 1 provider: aws
Command "gc a" executed in 1m 29s
Enter fullscreen mode Exit fullscreen mode

When gc apply is executed a second time, resources should not be created or destroyed.

gc a
Enter fullscreen mode Exit fullscreen mode
Querying resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Listing 13/13
  ✓ Querying
    ✓ EC2 1/1
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ Plan summary for provider aws                                                           │
└─────────────────────────────────────────────────────────────────────────────────────────┘
Nothing to deploy
Running OnDeployedGlobal resources on 1 provider: aws
Command "gc a" executed in 9s
Enter fullscreen mode Exit fullscreen mode

As expected, nothing to deploy, the target resources defined in the code match the live resources.

List

To list all the resources, and generate a diagram:

gc list --graph --all
Enter fullscreen mode Exit fullscreen mode

This will include the default AWS resources such as VPC, subnet, internet gateway, and security group.

graph-live

To list only the resources created by GruCloud:

gc list --our
Enter fullscreen mode Exit fullscreen mode
Listing resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Listing 13/13
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 1 EC2 from aws                                                                                          │
├────────────┬─────────────────────────────────────────────────────────────────────────────────────┬──────┤
│ Name       │ Data                                                                                │ Our  │
├────────────┼─────────────────────────────────────────────────────────────────────────────────────┼──────┤
│ web-server │ AmiLaunchIndex: 0                                                                   │ Yes  │
│            │ ImageId: ami-00f6a0c18edb19300                                                      │      │
│            │ InstanceId: i-016628a1b6a6a2f91                                                     │      │
│            │ InstanceType: t3.micro                                                              │      │
│            │ LaunchTime: 2021-04-29T23:10:44.000Z                                                │      │
│            │ Monitoring:                                                                         │      │
│            │   State: disabled                                                                   │      │
│            │ Placement:                                                                          │      │
│            │   AvailabilityZone: eu-west-2a                                                      │      │
│            │   GroupName:                                                                        │      │
│            │   Tenancy: default                                                                  │      │
│            │ PrivateDnsName: ip-172-31-2-146.eu-west-2.compute.internal                          │      │
│            │ PrivateIpAddress: 172.31.2.146                                                      │      │
│            │ ProductCodes: []                                                                    │      │
│            │ PublicDnsName: ec2-18-132-98-216.eu-west-2.compute.amazonaws.com                    │      │
│            │ PublicIpAddress: 18.132.98.216                                                      │      │
│            │ State:                                                                              │      │
│            │   Code: 16                                                                          │      │
│            │   Name: running                                                                     │      │
│            │ StateTransitionReason:                                                              │      │
│            │ SubnetId: subnet-0f6f085fc384bf8ce                                                  │      │
│            │ VpcId: vpc-bbbafcd3                                                                 │      │
│            │ Architecture: x86_64                                                                │      │
│            │ BlockDeviceMappings:                                                                │      │
│            │   - DeviceName: /dev/sda1                                                           │      │
│            │     Ebs:                                                                            │      │
│            │       AttachTime: 2021-04-29T23:10:45.000Z                                          │      │
│            │       DeleteOnTermination: true                                                     │      │
│            │       Status: attached                                                              │      │
│            │       VolumeId: vol-0a88a34248fe18740                                               │      │
│            │ ClientToken: 7ef5fd60-e962-400f-a2b9-37719d8329ed                                   │      │
│            │ EbsOptimized: false                                                                 │      │
│            │ EnaSupport: true                                                                    │      │
│            │ Hypervisor: xen                                                                     │      │
│            │ ElasticGpuAssociations: []                                                          │      │
│            │ ElasticInferenceAcceleratorAssociations: []                                         │      │
│            │ NetworkInterfaces:                                                                  │      │
│            │   - Association:                                                                    │      │
│            │       IpOwnerId: amazon                                                             │      │
│            │       PublicDnsName: ec2-18-132-98-216.eu-west-2.compute.amazonaws.com              │      │
│            │       PublicIp: 18.132.98.216                                                       │      │
│            │     Attachment:                                                                     │      │
│            │       AttachTime: 2021-04-29T23:10:44.000Z                                          │      │
│            │       AttachmentId: eni-attach-02744addcff43ecbb                                    │      │
│            │       DeleteOnTermination: true                                                     │      │
│            │       DeviceIndex: 0                                                                │      │
│            │       Status: attached                                                              │      │
│            │       NetworkCardIndex: 0                                                           │      │
│            │     Description:                                                                    │      │
│            │     Groups:                                                                         │      │
│            │       - GroupName: default                                                          │      │
│            │         GroupId: sg-f4139a96                                                        │      │
│            │     Ipv6Addresses: []                                                               │      │
│            │     MacAddress: 06:ee:ef:d7:1c:48                                                   │      │
│            │     NetworkInterfaceId: eni-06e9d87ff776cf40f                                       │      │
│            │     OwnerId: 840541460064                                                           │      │
│            │     PrivateDnsName: ip-172-31-2-146.eu-west-2.compute.internal                      │      │
│            │     PrivateIpAddress: 172.31.2.146                                                  │      │
│            │     PrivateIpAddresses:                                                             │      │
│            │       - Association:                                                                │      │
│            │           IpOwnerId: amazon                                                         │      │
│            │           PublicDnsName: ec2-18-132-98-216.eu-west-2.compute.amazonaws.com          │      │
│            │           PublicIp: 18.132.98.216                                                   │      │
│            │         Primary: true                                                               │      │
│            │         PrivateDnsName: ip-172-31-2-146.eu-west-2.compute.internal                  │      │
│            │         PrivateIpAddress: 172.31.2.146                                              │      │
│            │     SourceDestCheck: true                                                           │      │
│            │     Status: in-use                                                                  │      │
│            │     SubnetId: subnet-0f6f085fc384bf8ce                                              │      │
│            │     VpcId: vpc-bbbafcd3                                                             │      │
│            │     InterfaceType: interface                                                        │      │
│            │ RootDeviceName: /dev/sda1                                                           │      │
│            │ RootDeviceType: ebs                                                                 │      │
│            │ SecurityGroups:                                                                     │      │
│            │   - GroupName: default                                                              │      │
│            │     GroupId: sg-f4139a96                                                            │      │
│            │ SourceDestCheck: true                                                               │      │
│            │ Tags:                                                                               │      │
│            │   - Key: Name                                                                       │      │
│            │     Value: web-server                                                               │      │
│            │   - Key: stage                                                                      │      │
│            │     Value: dev                                                                      │      │
│            │   - Key: CreatedByProvider                                                          │      │
│            │     Value: aws                                                                      │      │
│            │   - Key: ManagedBy                                                                  │      │
│            │     Value: GruCloud                                                                 │      │
│            │   - Key: projectName                                                                │      │
│            │     Value: ec2-simple                                                               │      │
│            │ VirtualizationType: hvm                                                             │      │
│            │ CpuOptions:                                                                         │      │
│            │   CoreCount: 1                                                                      │      │
│            │   ThreadsPerCore: 2                                                                 │      │
│            │ CapacityReservationSpecification:                                                   │      │
│            │   CapacityReservationPreference: open                                               │      │
│            │ HibernationOptions:                                                                 │      │
│            │   Configured: false                                                                 │      │
│            │ Licenses: []                                                                        │      │
│            │ MetadataOptions:                                                                    │      │
│            │   State: applied                                                                    │      │
│            │   HttpTokens: optional                                                              │      │
│            │   HttpPutResponseHopLimit: 1                                                        │      │
│            │   HttpEndpoint: enabled                                                             │      │
│            │ EnclaveOptions:                                                                     │      │
│            │   Enabled: false                                                                    │      │
│            │                                                                                     │      │
└────────────┴─────────────────────────────────────────────────────────────────────────────────────┴──────┘


List Summary:
Provider: aws
┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ aws                                                                                                    │
├────────────────────┬───────────────────────────────────────────────────────────────────────────────────┤
│ EC2                │ web-server                                                                        │
└────────────────────┴───────────────────────────────────────────────────────────────────────────────────┘
1 resource, 7 types, 1 provider
Command "gc l -o" executed in 7s
Enter fullscreen mode Exit fullscreen mode

Note that tags have been added to the EC2 Instance, it gives GruCloud a way to identify the resources under its control. Unlike other infrastructure as code tools such as Terraform and Pulumi, GruCloud does not need a state file. Hence removing a lot of complexity and issues.

Update

The ec2 instance configuration might change, for instance, let's modify the machine type to t3.micro located in config.js

The plan command is a read-only command which fetches the live resources and compares them with the target resources defined in the code.

gc plan
Enter fullscreen mode Exit fullscreen mode
Querying resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Listing 13/13
  ✓ Querying
    ✓ EC2 1/1
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│ 1 EC2 from aws                                                                           │
├────────────┬──────────┬──────────────────────────────────────────────────────────────────┤
│ Name       │ Action   │ Data                                                             │
├────────────┼──────────┼──────────────────────────────────────────────────────────────────┤
│ web-server │ UPDATE   │ added:                                                           │
│            │          │ updated:                                                         │
│            │          │   InstanceType: t3.micro                                         │
│            │          │                                                                  │
└────────────┴──────────┴──────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ Plan summary for provider aws                                                           │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ DEPLOY RESOURCES                                                                        │
├────────────────────┬────────────────────────────────────────────────────────────────────┤
│ EC2                │ web-server                                                         │
└────────────────────┴────────────────────────────────────────────────────────────────────┘
1 resource to deploy on 1 provider
Command "gc plan" executed in 5s
Enter fullscreen mode Exit fullscreen mode

Let's apply the change:

gc apply
Enter fullscreen mode Exit fullscreen mode

Bear in mind that the machine requires to be stopped and restarted.

Trust but verify, hence, list the EC2 instances and check the InstanceType has been changed.

gc l -t EC2
Enter fullscreen mode Exit fullscreen mode

Output

Another useful command is gc output, which extract information for a specific field of a given resource

gc output -t EC2 --name web-server -f InstanceType
Enter fullscreen mode Exit fullscreen mode
t3.micro
Enter fullscreen mode Exit fullscreen mode

Nested field can be accessed too, for example, let's retrieve the public IP address attached to the EC2 instance:

gc output -t EC2 --name web-server -f 'NetworkInterfaces[0].Association.PublicIp'
Enter fullscreen mode Exit fullscreen mode
18.130.34.212
Enter fullscreen mode Exit fullscreen mode

The temptation to ping is high:

ping `gc output -t EC2 --name web-server -f 'NetworkInterfaces[0].Association.PublicIp'`
Enter fullscreen mode Exit fullscreen mode
PING 18.130.34.212 (18.130.34.212): 56 data bytes
64 bytes from 18.130.34.212: icmp_seq=0 ttl=38 time=153.799 ms
64 bytes from 18.130.34.212: icmp_seq=1 ttl=38 time=153.583 ms
64 bytes from 18.130.34.212: icmp_seq=2 ttl=38 time=140.622 ms
Enter fullscreen mode Exit fullscreen mode

Destroy

Time to destroy the resources allocated and therefore save a lot of £$€.

gc destroy
Enter fullscreen mode Exit fullscreen mode
Find Deletable resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Listing 6/6
? Are you sure to destroy 1 resource, 1 type on 1 provider? › (y/N)

Enter fullscreen mode Exit fullscreen mode

Once again, you are given the opportunity to look at what is going to be destroyed.
Type 'y' to confirm the destruction:

Destroying resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Destroying
    ✓ EC2 1/1
1 resource destroyed, 1 type on 1 provider
Running OnDestroyedGlobal resources on 1 provider: aws
Command "gc destroy" executed in 1m 29s
Enter fullscreen mode Exit fullscreen mode

Let's run the gc list command with the E2 filter to verify the EC2 is gone:

gc l -t EC2
Enter fullscreen mode Exit fullscreen mode
Listing resources on 1 provider: aws
✓ aws
  ✓ Initialising
  ✓ Listing 6/6
List Summary:
Provider: aws
┌─────────────────────────────────────────────────────────────────────────┐
│ aws                                                                     │
└─────────────────────────────────────────────────────────────────────────┘
0 resources, 0 types, 1 provider
Command "gc list -t EC2" executed in 3s
Enter fullscreen mode Exit fullscreen mode

This example demonstrates how to code a very basic infrastructure with one EC2 instance, and how can we use the gc apply, gc list, gc destroy and gc graph to manage the infrastructure.

It paves the way for more AWS examples

Graph

A picture is worth a thousand words, GruCloud generates an SVG file describing the resources and their relationship.

gc graph
Enter fullscreen mode Exit fullscreen mode

Here is the graph of a typical web application managed by Kubernetes running on AWS where the master node is managed by EKS.

kubernetes eks

Links

Top comments (0)