Options for installing applications on EC2 and when you might use them
With the advent of containers and serverless you’d be forgiven for thinking no-one uses good ‘old fashioned’ servers any more, however based on most customer’s cloud bills I’d suggest there’s still a fair amount of VMs in use.
With that in mind and a recent question on the best way to install applications onto EC2 instances crossing my desk from some less experienced colleagues I thought I’d write-up the options, along with more importantly the when, why and how you might go about using them ahead of a session I intend to deliver to our Atos AWS Coaching Hub on the topic, complete with demo and open source repo here.

As you can see from the above diagram the options available are: -
- AMI Baking
- Userdata
- Post deployment using configuration management tools
I’ll cover them in more detail below as I go through the demo diagrams and installation of a simple NGINX web server with customised welcome page to prove each one used the correct process. I’m most experienced with Hashicorp Terraform from an Infrastructure as Code perspective so that’s what I’ve used for the demo, I’d welcome someone picking up and doing a CloudFormation equivalent and would happily link to the repo they create here if they get in touch :-).
Firstly I need a base environment I can use to create the AMIs and demo EC2 instances, so I created a simple VPC with only public subnets to avoid NAT Gateway costs (wouldn’t recommend for real world use), along with IAM role, instance profile and policy attachment. The code also dynamically creates the files used later, pre-populating fields like subnet ID, security group ID etc.: -

Now we’re ready to get started with the different options….
AMI Baking
AMI baking is a sensible approach in a number of circumstances: -
- You have base software you want on every instance (e.g. anti-virus, monitoring, logging agents etc.)
- You require a quick start-up time from starting to build an instance to that instance’s application being able to serve traffic (e.g. autoscaling)
The two main approaches to AMI baking in AWS are Hashicorp Packer and AWS EC2 Image Builder.
Packer is a multi-cloud tool, so you can build images consistently across numerous different platforms. It works in AWS by building a temporary EC2 instance from a base AMI (e.g. a vanilla Ubuntu image) and then connects to that instance via SSH (or WINRM for Windows) and runs scripts to install the defined software. Once the software is all installed Packer creates an AMI from the temporary EC2 instance before terminating it. The AMI can be encrypted, shared across multiple accounts and replicated around multiple regions depending on requirements. In my basic demo of an AMI in a single region the AMI was available within a couple of minutes. Having used Packer in a customer environment the only drawback I’ve come across is that if there’s an issue and Packer doesn’t exit gracefully it can leave behind orphaned resources that would need cleaning up (a tagging strategy and Lambda function to tidy-up would be a sensible idea)
The below diagram provides an overview of the approach using Packer: -

In my demo I’ve ran Packer from my local machine, in a production context you’d run it via a CI/CD pipeline: -

Once the AMI has been created and an EC2 instance with public IP is created from the AMI you can see the customised webpage via http: -

In recent years AWS have release EC2 Image Builder. Due to the prevalence in the market of Packer, having been available a number of years earlier and having multi-cloud capability I’ve not personally used EC2 Image Builder in a customer environment yet, however for the purposes of demonstration I’ve set one up in the demo environment.
It works in the same way as Packer in terms of creating a temp instance, installing the software via code and them creating an AMI from it. There are some more advanced options I’ve not tried to do with testing, which would no doubts be useful in a customer environment. My only observation is that the simple pipeline that installed NGINX via Packer in a couple of minute took around 20 minutes using EC2 Image Builder, but at least you wouldn’t need to worry about your own Packer pipeline or clean-up routines if anything went wrong: -

Progress within the console whilst waiting for the image to build: -

Once the AMI has been created and an EC2 instance with public IP is created from the AMI you can see the customised webpage via http: -

Userdata
EC2 userdata is a script that can be passed to an EC2 Instance at build time to perform tasks, for example to install software. This is an appropriate option when you want the software to be installed immediately at build time, but not on enough instances that it warrants baking a separate AMI and you want to ‘fire and forget’ the install.
Anything you want to install needs passing in as a script, so any external files (e.g. application binaries) need hosing elsewhere (e.g. in an accessible S3 bucket) and calling via the script. Userdata is great to set certain things at build time that can’t be baked into an AMI (e.g. hostname), but given it’s a one time use per EC2 Instance it’s then less useful than ‘post deployment’ options for management through life: -

Once the EC2 instance with public IP is created you can see the customised webpage via http: -

Post Deployment via Configuration Management Tools
Post deployment options all tasks to be ran on existing instances, or fleets of instances (e.g. targeting many instances with the same tag key / value), so is extremely flexible. It can be used to target an instance immediately after creation, but it can also be used to manage instances that could be running for months / years.
Tools supporting this approach include Chef, Puppet and Ansible, as well as AWS options like Systems Manager and Opsworks. The demo I’ve done uses Systems Manager, which if you’re unaware can be used to manage EC2 instances as well as on premise or other cloud provider VMs. I prefer to use Systems Manager within AWS for configuration management, as it allows management of instances without a central server needing IP connectivity to every instance (like Chef, Puppet or Ansible). The demo creates a simple SSM Document, Ec2 instance using a vanilla Ubuntu image and then associates the SSM document with the Instance, which runs the document on the instance and installs the software: -

The created SSM document as seen in the console: -

Systems Manager also has the benefit of retaining logs, even after the instance is terminated: -

Once the EC2 instance with public IP is created and the SSM document has rang against the server you can see the customised webpage via http: -
