Sample single host environment:

Overview:

As noted in the Sample MySQL Environment document, I’m having discussions with cloud engineers about the wisdom and process for granting developer access to secrets in the development environment.

That document outlines cli, terraform, and cloudformation configurations for a complete set up. I created a similar set up in aws. I didn’t take it quite as far - didn’t set up an s3 bucket and create a mysql db, for instance, but I do create a vpc, s3 and secrets manager endpoints, an internet gateway, and lastly the ec2 instance.

I tend to prefer aws over azure; however, having come later, azure does do some things that make life easier. The existance of resource groups makes deleting an entire environment a single command. AWS tends to be more in the weeds. That gives you a lot of flexibility; but, it sure does make the cli scripts longer.

Approach differences:

Creating the scripts for cli was very involved. I also had to do a fair bit of sequential build - ensuring subnets are created before route tables, etc. Those kinds of things terraform handles for you. I also had to create the remove script in sync with the creation script. Unlike Azure, I can’t simply remove the resource group to remove all created resources

All in all, there was significantly less difference in development time for Azure cli over Azure terraform. The AWS cli took several days. I was working out process and procedure which accounts for a fair bit; but, even with that, the terraform development process took about 3 hours soup to nuts.

Run time differences:

One would expect the cli to take longer to generate an environment since it’s doing everything sequentially. To get to the point where I could access the ec2 instance took about 10 minutes. Removing the environment also took longer at ~ 2 minutes.

Terraform, as you’d expect, was quit a bit faster. Just shy of 2 minutes to create the enviroment where I could access the ec2 instance. Just over 1m to delete the env.

Surprisingingly, at least to me, cloudformation was even faster. 1m6 to get to where I could access the ec2 instance. Deleting, the prompt came back in 8 seconds; however, the env was still getting deleted in the background.

This doesn’t take into account any user scripts or other host configs. Still, the fact that cloudformation took ~ half the time for config that terraform did was surprising.

I’m probably going to continue using terraform more often as I need to support multiple clouds; however, if you need your resources really fast, cloudformation might be worth exploring.

Terraform:

To reiterate: terraform code was easier to develop and much shorter - also much easier to maintain since the cli is close to 600 lines while the same terraform code is roughly a third of that (223 lines)

Similar to the idami cli function, terraform provides a way to get the latest image:

data "aws_ami" "lami" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "description"
    values = ["Amazon Linux 2023*x86_64 HVM*"]
  }

  filter {
    name   = "state"
    values = ["available"]
  }
}

I also like being able to obtain and display the ec2’s public IP without jumping through hoops:

output "instance_IP" {
  description = "The public IP address of the EC2 instance, prefixed with host and hostname"
  value = <<EOT
host ${var.iname}
  hostname ${aws_instance.dolec2.public_ip}
EOT
}

Files:

locals.tf:

Defines local variables/data resources such as myip and lami

main.tf:

Main resource deffinition of the terraform config

outputs.tf:

Outputs ami id, myip, and the public ip of the ec2 instance.

providers.tf:

Establishes aws as the provider and provides the default region to use.

variables.tf:

All the variables used in main and the outputs.

Cloudformation:

In the interest of transparency, I admit I asked perplexity, a genAI tool similar to chatgpt, to convert the terraform code to cloudformation. It did a reasonable job; but, there were still some things to troubleshoot. I had to create the route from subnet2 to the igw and update the ssm parameter to obtain the latest al2023 ami id.

One limiting factor: both terraform and the cli used external processes to obtain my local IP which is used in the security group. External processes apparently aren’t possible with cloudformation so that had to be hard coded. Another option is to add it as a cli parameter.

One bonus factor: In the interet of emphasis: cloudformation was bloody fast. Slightly over a minute to create this environment soup to nuts. Terraform was ~ twice that.

Files:

dbaccess.yaml:

Cloudformation template

CLI creation:

The functions defined in vars are needed in both scripts; however, the idami function could have some use outside of the script. It identifies the lastest ami for the specific amazon linux 2:

idami()
{
  aws ec2 describe-images --region ${region} \
    --owners amazon \
    --filters "Name=description,Values=Amazon Linux 2023*x86_64 HVM*" "Name=state,Values=available" \
    --query 'Images | sort_by(@, &CreationDate) | [-1].{ID:ImageId}' \
    --output text

}

I’ve had problems doing that from the command line as well as the web so that was a useful lesson.

Files:

vars:

Contains the variables and common functions used by both mkenv and rmenv

mkenv:

Creates the environment from the ground up. Functions listed later.

rmenv:

Deletes the aws environment created by mkenv.

Common functions:

idami():

Lists the the latest ami for amazon linux 2023.

idigwid():

Lists the interget gateway id

idinstanceid():

Lists the instance id of a running ec2 instance named ${inane}

idistate():

Lists the state of the instance id.

idpubip():

Lists the public IP of the ec2 instance.

idrtable():

Lists the routing table id

idrtableassocid():

Lists the route table ID associated with a subnet.

idsgid():

Lists the security group identifier.

idsubnetid():

Lists the subnet id:

idvpcid():

Lists the vpc ID

subassoc():

Lists the subnet id that’s associated with a route.

subnetids():

Lists subnet ids of a cidr.

mkenv:

The mkenv script calls functions in order to create the environment:

mkvpc     || exit 3 # creates the vpc
mksubnets || exit 4 # creates the subnets
mkigw     || exit 5 # creates the iternet gateway
attachigw || exit 6 # Attaches the igw to the vpc
mkroutes  || exit 7 # creates routes, including s3 and secretsmanager endpoints
mksg      || exit 8 # creates the security group
mkec2     || exit 9 # creates the ec2 instance.

rmenv:

Teh rmenv script, basically, does the same thing in reverse:

rmec2     || exit # terminate the ec2 instance
rmsg      || exit # delete the security group.
rmrtable  || exit # delete the routing table including s3/secrets manager endpoint
rmattach  || exit # detach the route table from the vp
rmigw     || exit # delete the internet gateway
rmsubnets || exit # delete subnets
rmvpc     || exit # delete the vpc