# Unleashing Automation: Spinning Up EC2 Instances with the Dynamic Duo of Terraform and Ansible 🚀

### 📔 Introduction

In this example we are going to create an EC2 instance and install docker inside, using Terraform to deploy the instance and ansible to install docker and configure everything

### **📚 Prerequisites**

* Terraform - [https://developer.hashicorp.com/terraform/downloads](https://developer.hashicorp.com/terraform/downloads)
    
* Ansible - [https://docs.ansible.com/ansible/latest/installation\_guide/intro\_installation.html](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html)
    
* AWS Free tier account - [https://aws.amazon.com/free/](https://aws.amazon.com/free/)
    
* Unix based OS - [https://linuxmint.com/](https://linuxmint.com/)
    

### 📓 Steps

**📝 Step 0 — Create a repository from this one** [https://github.com/jd-apprentice/base-web-server](https://github.com/jd-apprentice/base-web-server)

With this step, we are going to get the repository and start working locally

**📝 Step 1 — Create a ssh-key**

```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```

**REMEMBER TO NOT SHARE YOUR PRIVATE KEY**

The program will ask you a few things like a name for the key, if you leave it blank with this algorithm it will be called something like `id_rsa` and `id_rsa.pub`.

This will create a ssh-key to when we have our instance we associate that key and connect to the server with it.

In case you want to read a more detailed example of how to create and what are ssh-key I'll leave you this post - [https://www.atlassian.com/git/tutorials/git-ssh](https://www.atlassian.com/git/tutorials/git-ssh)

**📝 Step 2 — Obtain your variables from AWS**

In this step is up to you how are you going to handle your sensitive variables from AWS since you can just use root and that's all but it is not a recommended practice since is insecure, you should rather use IAM and create a user with the necessary permissions to create the resources in your account.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1692678835026/003fab64-bc8f-410e-91ae-6de4b0776f70.png align="center")

If you are going for the root route you go into `Security Credentials` then `Access keys` and create a new one, then copy the values.

If you want the secure way or at least not the worst one you can create a new user assign permissions and go into `Access keys` again to retrieve your values

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1692678988042/4dc85f08-0df0-42b9-b03b-aeaa1d917948.png align="center")

Here is the detailed example they give - [https://docs.aws.amazon.com/IAM/latest/UserGuide/id\_credentials\_access-keys.html?icmpid=docs\_iam\_console#Using\_CreateAccessKey](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html?icmpid=docs_iam_console#Using_CreateAccessKey)

Now at this point, we should have our ssh-key and the AWS environment variables let's go into our project.

**📝 Step 3 — Populate the fields**

In our local project, we are going to need to create a `secret.tfvars` inside the `terraform` folder is used to store sensitive information which in this case is going to be the AWS keys.

Inside is going to look something like this

```bash
access_key = "your-key"
secret_key = "your-key"
ssh_key    = "path/to/public/ssh-key"
```

Also, we are going to populate the `Makefile` with the path to our private ssh-key

```makefile
## Build and deploy the infrastructure
## Usage: make

deploy: apply all playbook

## For development and testing purposes

BOOK2USE = prepare.yml # name of the playbook to use
PRIVATE_KEY_PATH = ~/.ssh/id_rsa # path to your private key
```

The line where it says `PRIVATE_KEY_PATH` I'm going to write `~/.ssh/id_rsa` since that is the key I've created with the example above.

**📝 Step 4 — Run the project**

At this point, we can initialize our infrastructure with `make init` then if everything is okay we can run `make`.

And what will `make` do?

* It will run the first command at the `Makefile` which is `deploy: apply all playbook`
    
* This will at first run
    
    ```makefile
    apply:
    	cd terraform && terraform plan -var-file="secret.tfvars"
    ```
    
    It will apply the infrastructure to AWS (check `resources.tf` to see what is going to do.
    
* Then is going to run `all`
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1692680447899/05709a6a-0cbd-4fe7-8892-3fc99d6946f7.png align="center")
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1692680495111/a209434f-7022-4651-9764-f369b0f77691.png align="center")
    

```makefile
all: terraform_output update_hosts clean_temp

terraform_output:
	cd terraform && terraform output instance_public_ip > $(TEMP_FILE)

update_hosts:
	echo "[aws_server]" > $(OUTPUT_FILE)
	cat ./terraform/$(TEMP_FILE) >> $(OUTPUT_FILE)
	echo "" >> $(OUTPUT_FILE)
	echo "[aws_server:vars]" >> $(OUTPUT_FILE)
	echo "ansible_ssh_user=ec2-user" >> $(OUTPUT_FILE)
	echo "ansible_ssh_private_key_file=$(PRIVATE_KEY_PATH)" >> $(OUTPUT_FILE)

clean_temp:
	rm ./terraform/$(TEMP_FILE)
```

At first it will output the instance public ip of the EC2 instance and save it into a temporary file called `temp_ip.txt`

```nginx
output "instance_public_ip" {
  value = aws_instance.example_webserver.public_ip
}
```

Then we will populate the `ansible/inventory/host` file which contains information for ansible to connect to the instance and run the playbook

At the end, we will execute the playbook to access the instance and perform updates. Finally, we will install Docker.

### 🍎 **Troubleshooting**

If for some reason the ansible operation fails it means we have to connect manually for the first time. To solve this you can run

```bash
make connect
```

then we can close the connection typing `exit` and run `make playbook` to do the ansible part alone.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1692680879945/0145186b-2bd5-4016-a093-d712a334362b.png align="center")

There we can see that everything went okay! At this point, we have our EC2 instance running with Amazon Linux and with docker installed.

### 🏁 Conclusion

At the end we have a running instance with some initial configuration if we maybe need 30 machines with the same configuration it would be much easier to do with Terraform + Ansible rather than doing everything manually even if we do bash scripts to automate the process.
