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.
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
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
Getting the GruCloud CLI
GruCloud is written in Javascript running on Node.js. Check if node
is present on your system:
node --version
The version must be greater than 14
Install the GrucCloud command-line utility gc with npm
npm i -g @grucloud/core
Check the current version of gc:
gc --version
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
package.json
Let's create a new package.json
with the npm init
command:
npm init
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
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
},
},
});
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"
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 },
};
};
GruCloud CLI
Info
As a way to verify that gc can connect to the AWS API, one can use the info
command:
gc info
- 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
Deploy
We are ready to deploy with apply
command:
gc apply
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)
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
When gc apply
is executed a second time, resources should not be created or destroyed.
gc a
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
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
This will include the default AWS resources such as VPC, subnet, internet gateway, and security group.
To list only the resources created by GruCloud:
gc list --our
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
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
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
Let's apply the change:
gc apply
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
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
t3.micro
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'
18.130.34.212
The temptation to ping is high:
ping `gc output -t EC2 --name web-server -f 'NetworkInterfaces[0].Association.PublicIp'`
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
Destroy
Time to destroy the resources allocated and therefore save a lot of £$€.
gc destroy
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)
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
Let's run the gc list
command with the E2
filter to verify the EC2 is gone:
gc l -t EC2
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
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
Here is the graph of a typical web application managed by Kubernetes running on AWS where the master node is managed by EKS.
Top comments (0)