In Part 1 and Part 2 of this AWS quick start series we have walked through the steps on how to use the AWS console to create a Virtual Private Cloud (VPC). Then, we secured it by creating a custom Network Access Control List (NACL) that opens up ports 80 (HTTP) and 22 (SSH) and associated the NACL to our subnet. We also created a Resource Group and tagged our network objects to allow us to keep track of all of our cloud infrastructure.
We then took all of those manual steps and we created a Tuono Blueprint in under 40 lines of “code”. This Blueprint will repeatedly and reliably build that infrastructure for you. It just happens to be declarative as well, so you can edit the Blueprint and re-apply to make any changes you want over time.
In this part we are going to deploy an Ubuntu virtual machine that will be attached to our network and leverage cloud-init to configure NGINX and display a custom message that is accessible on the public interface via port 80.
How do I deploy an EC2 Instance in the AWS Console?
Navigate to the EC2 dashboard in the AWS console and select Instances from the side blade. Click “Launch Instances”.
The first step in deploying an AWS instance is picking an Amazon Machine Image (AMI). There are a variety of ways to search for an AMI. In order to stay consistent with our Azure quick start, we are going to search for the Canonical Ubuntu Community AMI that we have been using. We will search for that specific AMI by id
Let’s select our image under Community AMIs.
This brings us to selecting an Instance Type. For this tutorial we are sticking with the free tier, so select the t2.micro image and click “NEXT: Configure Instance Details”.
The ‘Configuring Instance Details’ page allows us to specify our Network as well as customize our VM with a cloud-init script. Under “Network” and “Subnet” select the VPC and subnet we previously created in Part 1. Since we associated a secure NACL with this subnet in Part 2 we know that we are deploying our Instance into a VPC that is secured to only allow traffic required for the web server.
We are also going to Enable “Auto-assign Public IP”. This is going to get the instance setup with a public IPv4 address that will allow the system to configure NGINX on first boot.
How do I use cloud-init to customize an EC2 Instance in the AWS Console?
Under Advanced Details we are going to focus on the ‘User data’ section which will allow us to configure the Instance at launch. The cloud-init script below will install the NGINX package, set our login user and authorized ssh public key, and configure NGINX to display a custom welcome message. We are electing to use our own generated SSH key rather than generating a new AWS keypair. If you don’t know why you should be using SSH keys or know how to create one we have a blog article to help you with that! (The Dummy SSH key below is a legitimate key, but we want to be sure nobody mistakes it as their own key.)
#cloud-config package_upgrade: false packages: - nginx users: - name: adminuser groups: - sudo sudo: ALL=(ALL) NOPASSWD:ALL ssh_authorized_keys: - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDu= firstname.lastname@example.org runcmd: - sudo su - echo 'Congratulations on configuring an AWS web server! ' > /var/www/html/index.nginx-debian.html
In the ‘User data’ section paste in the cloud-init script above and then click “Next: Add Storage”
The ‘Add Storage’ page allows you to create additional volumes for your Instance. For our purposes we will keep the default and click “Next: Add Tags”
We wish the AWS console was consistent with when they allow you to add tags in their wizards. Instances are a case where there is a dedicated screen to add tags. We will add a tag to Name our web server then add our Resource Group Tag Key
walkthrough and value
webserver so we can keep track of our deployed infrastructure. Click “Next: Configure Security Group”.
How do I configure a security group on an EC2 Instance during deploy?
The ‘Configure Security Group’ page allows us to add security at the Instance level. While our NACL defines the rules for the subnet, the security group is enforced directly on the Instance. We will keep the default SSH rule and add Type HTTP (Port 80). We can remove the range, ::/0, as that is for IPv6 and we will only be using IPv4 in this example. Click “Review and Launch”
After a review of the settings click “Launch”
A screen pops up to configure a key pair that will be used to SSH into the Instance. We previously configured the SSH key in our cloud-init script to use the SSH key we already have. That allows us to launch our Instance without creating a new key pair. Select ‘Proceed without a key pair” and acknowledge then click “Launch Instances”
The ‘Launch Status’ screen lets us know that AWS is turning the gears.
Once our Instance is complete let’s get the Public IPv4 address from the Instance.
Having created a resource group and tagging our infrastructure as a best practice we can see all of our deployed infrastructure under our resource group.
Tuono gets us consistent, secure deployable infrastructure every time
Let’s build on our network infrastructure Blueprint we created in the previous posts. We are now going to add the pieces to define an image and deploy our VM along with configuring NGINX with cloud-init using Tuono. When deploying to AWS with Tuono we automatically create and tag Infrastructure and place it in a resource group so you know exactly what your cloud automation platform has deployed.
Below you can see that we are selecting the same Canonical Ubuntu 18.04 image as our manual deploy.
compute: image: bionic: publisher: Canonical product: UbuntuServer sku: 18.04-LTS venue: aws: image_id: ami-04bb0cc469b2b81cc
vm: webserver: cores: 1 memory: 1 GB image: bionic nics: external: ips: - private: type: dynamic public: type: static firewall: fw-external-access subnet: subnet-walkthrough
Setting up the SSH key and NGINX is accomplished with a configure stanza where we pass in our details and use cloud-init after first boot to customize NGINX.
configure: admin: username: (( admin_username )) public_key: (( admin_public_key )) userdata: type: cloud-init content: | #cloud-config package_upgrade: false packages: - nginx users: - name: (( admin_username )) groups: - sudo sudo: ALL=(ALL) NOPASSWD:ALL ssh_authorized_keys: - (( admin_public_key )) runcmd: - sudo su - echo '(( your_caption ))' > /var/www/html/index.nginx-debian.html
The complete Blueprint with user defined variables for this series is below.
# # This is an example blueprint that demonstrates the creation of a webservice # --- variables: admin_username: description: The username for the administrative user. type: string default: adminuser admin_public_key: description: The OpenSSH Public Key to use for administrative access. type: string default: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDummyDu= email@example.com your_caption: description: Name of webserver type: string default: "Congratulations on configuring an AWS web server!" location: region: my-region: country: USA area: northwest folder: aws-walkthrough: region: my-region networking: network: vnet-walkthrough: range: 10.0.0.0/16 scope: public subnet: subnet-walkthrough: range: 10.0.0.0/24 network: vnet-walkthrough firewall: fw-external-access # Part 2 adds a Firewall to the subnet and marks it public scope: public # Part 2 Protocols protocol: ssh: ports: - port: 22 proto: tcp http: ports: - port: 80 proto: tcp # Part 2 adds a Firewall using a protocol firewall: fw-external-access: rules: - protocols: ssh to: self - protocols: http to: self # Part 3 adds VM and configures NGINX with cloud-init compute: image: bionic: publisher: Canonical product: UbuntuServer sku: 18.04-LTS venue: aws: # if provisioning fails due to image not found, go to: # https://cloud-images.ubuntu.com/locator/ec2/ # and search for "bionic amd64 ebs" and also add your AWS zone name like "us-west-2" image_id: ami-04bb0cc469b2b81cc vm: webserver-var: cores: 1 memory: 1 GB image: bionic nics: external: ips: - private: type: dynamic public: type: static firewall: fw-external-access subnet: subnet-walkthrough configure: admin: username: (( admin_username )) public_key: (( admin_public_key )) userdata: type: cloud-init content: | #cloud-config package_upgrade: false packages: - nginx users: - name: (( admin_username )) groups: - sudo sudo: ALL=(ALL) NOPASSWD:ALL ssh_authorized_keys: - (( admin_public_key )) runcmd: - sudo su - echo '(( your_caption ))' > /var/www/html/index.nginx-debian.html
Don’t miss the bonus tutorial in this series. It is a great walkthrough of how to setup an Application Load Balancer in AWS.
As always, all of the Tuono code samples in this post can be used for free in the Community Edition of the Tuono platform.