├── .editorconfig ├── .gitignore ├── 0000-setup └── main.tf ├── README.md ├── docker-compose.yaml └── setup.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 2 9 | indent_style = space 10 | insert_final_newline = true 11 | max_line_length = 80 12 | trim_trailing_whitespace = true 13 | 14 | [*.{tf,tfvars}] 15 | indent_size = 2 16 | indent_style = space 17 | 18 | [*.md] 19 | max_line_length = 0 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | creds/* 2 | 3 | .terraform 4 | .idea 5 | .vscode 6 | 7 | # .tfstate files 8 | *.tfstate 9 | *.tfstate.* 10 | 11 | # Crash log files 12 | crash.log 13 | 14 | # Ignore override files as they are used to override resources locally and should not be checked in 15 | override.tf 16 | override.tf.json 17 | *_override.tf 18 | *_override.tf.json 19 | 20 | # Ignore plan output files 21 | *tfplan* 22 | 23 | # Ignore secrets files 24 | terraform.tfvars 25 | 26 | # Ignore lock files 27 | .terraform.lock.hcl 28 | 29 | volume/ 30 | -------------------------------------------------------------------------------- /0000-setup/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.6.0" 3 | required_providers { 4 | aws = { 5 | source = "hashicorp/aws" 6 | version = "5.68.0" 7 | } 8 | } 9 | } 10 | 11 | provider "aws" { 12 | access_key = "test" 13 | secret_key = "test" 14 | region = "us-east-1" 15 | profile = "localstack" 16 | 17 | endpoints { 18 | ec2 = "http://localhost:4566" 19 | s3 = "http://s3.localhost.localstack.cloud:4566" 20 | dynamodb = "http://localhost:4566" 21 | route53 = "http://localhost:4566" 22 | route53resolver = "http://localhost:4566" 23 | } 24 | } 25 | 26 | resource "aws_instance" "exercise_0000" { 27 | ami = "ami-edbfe74c41f8" 28 | instance_type = "t2.micro" 29 | 30 | tags = { 31 | Name = "exercise_0000" 32 | Terraform = true 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform Workshop 2 | 3 | This is the code for my Terraform Workshop. 4 | 5 | **Please note** that I routinely make destructive changes (a.k.a `git push -f`) to this repository. 6 | If you wish to keep a copy around, I highly recommend you fork this, and `git pull upstream` judiciously. 7 | 8 | ## Agenda 9 | 10 | - The place for, and benefits of "Everything as Code" alongside GitOps 11 | - Terraform's architecture 12 | - Terraform 101 13 | - Introduction to HCL 14 | - What are providers? 15 | - Initializing terraform and providers 16 | - Dive right in! Creating your first resource in AWS using Terraform 17 | - Understanding references, dependencies 18 | - `apply`-ing terraform 19 | - Using `output` and `data` in your terraform scripts 20 | - Variables and the HCL type-system 21 | - DRY with Terraform modules 22 | - Understanding how Terraform manages state 23 | - Using S3 as a backend 24 | - Collaboration using Terraform 25 | - Terraform ecosystem, testing, and GitOps 26 | - Closing arguments, final Q/A, discussion 27 | 28 | ## Setup 29 | 30 | While this repository is a tutorial aimed at teaching Terraform, AWS setup can be arduous, and non-deterministic across audiences, especially if anyone chooses to use their _organizational_ credentials (Hello IAM!) and setup. 31 | 32 | To avoid this, this tutorial uses the absolutely fantastic [LocalStack](https://www.localstack.cloud/) project to emulate the AWS API locally. 33 | Not only does this make it easier to set up for the workshop, it a zero-cost solution for attendees, and eliminates the risk that they accidentally leave resources running in the cloud. 34 | 35 | It also makes the setup trivial. 36 | Please see the following sections: 37 | 38 | ## Installation 39 | 40 | You will need the following installed 41 | 42 | - [Terraform](https://learn.hashicorp.com/terraform/getting-started/install.html) 43 | - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 44 | - [Docker](https://www.docker.com/get-started/) 45 | - A good text editor. 46 | I highly recommend [VS Code](https://code.visualstudio.com/). 47 | - Clone this repository 48 | 49 | ## Set up 50 | 51 | ### Create a specific profile for LocalStack 52 | 53 | 1. Open a terminal window 54 | 2. Run the following command: 55 | ```bash 56 | aws configure --profile localstack 57 | ``` 58 | 3. Enter the following details for the `localstack` profile: 59 | - AWS Access Key ID: Enter `test` 60 | - AWS Secret Access Key: Enter `test` 61 | - Default region name: Enter `us-east-1` 62 | - Default output format: Enter `json` 63 | 64 | 4. The AWS CLI will create a new profile named `localstack` in the AWS credentials file (located at `~/.aws/credentials` on Linux/Mac or `%USERPROFILE%\.aws\credentials` on Windows) as well as storing your profile preferences in `~/.aws/config`. 65 | 66 | **This is VERY IMPORTANT**. 67 | Edit the `localstack` profile in `~/.aws/config`, and the add the following line at the bottom of the `localstack` profile: 68 | 69 | ```ini 70 | [profile localstack] 71 | region=us-east-1 72 | output=json 73 | endpoint_url = http://localhost:4566 # <- ADD THIS LINE 74 | ``` 75 | 5. Run the following: 76 | 77 | ```bash 78 | docker pull ubuntu:24.04 79 | docker pull ubuntu:23.10 80 | docker tag ubuntu:24.04 localstack-ec2/ubuntu-24-04-ami:ami-edbfe74c41f8 81 | docker tag ubuntu:23.10 localstack-ec2/ubuntu-23-10-ami:ami-77081d4f1e72 82 | ``` 83 | 84 | 85 | ## Testing your setup 86 | 87 | In the directory where you cloned this repository: 88 | 89 | ```bash 90 | # cd /path/to/terraform-workshop 91 | ❯ docker compose up --build 92 | # You should see ... 93 | [+] Running 1/0 94 | ✔ Container localstack-main Created 0.0s 95 | Attaching to localstack-main 96 | localstack-main | 97 | localstack-main | LocalStack version: 3.7.1 98 | localstack-main | LocalStack build git hash: d4f2409a7 99 | localstack-main | 100 | localstack-main | Ready. 101 | ``` 102 | 103 | **In another terminal**, navigate, again, to the location where you cloned this repository: 104 | 105 | ```bash 106 | # initialize terraform 107 | terraform init 108 | # see if terraform can perform it's duties 109 | terraform apply -auto-approve 110 | 111 | # if all goes well, you should something like 112 | Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 113 | ``` 114 | 115 | If all that works, you are golden. 116 | Go back to the first terminal 117 | 118 | ```bash 119 | # Use `Ctrl-c` to stop compose 120 | # Clean up afterwards 121 | docker compose down -v 122 | ``` 123 | 124 | Woot! 125 | You are all set. 126 | See you soon. 127 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | localstack: 3 | container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}" 4 | image: localstack/localstack:3.7.1 5 | hostname: localstack 6 | ports: 7 | - "127.0.0.1:4566:4566" # LocalStack Gateway 8 | - "127.0.0.1:4510-4559:4510-4559" # external services port range 9 | environment: 10 | # LocalStack configuration: https://docs.localstack.cloud/references/configuration/ 11 | - DEBUG=${DEBUG:-1} 12 | - HOSTNAME_EXTERNAL=localstack 13 | volumes: 14 | - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" 15 | - "/var/run/docker.sock:/var/run/docker.sock" 16 | networks: 17 | - localstack 18 | 19 | networks: 20 | localstack: 21 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then 4 | echo "Loading Aliases" 5 | export AWS_PROFILE=localstack \ 6 | && alias d="docker compose down -v" \ 7 | && alias u="docker compose up" \ 8 | && alias up="docker compose up --build" \ 9 | && alias t=terraform \ 10 | && alias tp='terraform plan' \ 11 | && alias tc='terraform console' \ 12 | && alias ta='terraform apply -auto-approve' \ 13 | && alias td='terraform destroy' \ 14 | && alias tda='terraform destroy -auto-approve' \ 15 | && code . 16 | else 17 | echo "You need to source this script ... Please try 'source setup.sh'." 18 | fi 19 | 20 | --------------------------------------------------------------------------------