├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── base_web_app └── main.tf ├── commands ├── m3_commands.sh ├── m4_commands.sh ├── m5_commands.sh ├── m6_commands.sh ├── m7_commands.sh └── m8_commands.sh ├── m4_solution ├── locals.tf ├── main.tf ├── outputs.tf ├── terraform.tfvars.example └── variables.tf ├── m5_solution ├── instances.tf ├── loadbalancer.tf ├── locals.tf ├── network.tf ├── outputs.tf ├── terraform.tfvars.example └── variables.tf ├── m6_solution ├── instances.tf ├── loadbalancer.tf ├── locals.tf ├── network.tf ├── outputs.tf ├── providers.tf ├── s3.tf ├── terraform.tf ├── terraform.tfvars.example ├── variables.tf └── website │ ├── Globo_logo_Vert.png │ └── index.html ├── m7_solution ├── instances.tf ├── loadbalancer.tf ├── locals.tf ├── network.tf ├── outputs.tf ├── providers.tf ├── s3.tf ├── templates │ └── startup_script.tpl ├── terraform.tf ├── terraform.tfvars.example ├── variables.tf └── website │ ├── Globo_logo_Vert.png │ └── index.html ├── m8_solution ├── instances.tf ├── loadbalancer.tf ├── locals.tf ├── modules │ └── globo-web-app-s3 │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── terraform.tf │ │ └── variables.tf ├── network.tf ├── outputs.tf ├── providers.tf ├── s3.tf ├── templates │ └── startup_script.tpl ├── terraform.tf ├── terraform.tfvars.example ├── variables.tf └── website │ ├── Globo_logo_Vert.png │ └── index.html └── website ├── Globo_logo_Vert.png └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # .tfvars files 9 | *.tfvars 10 | 11 | # .tfplan files 12 | *.tfplan 13 | 14 | # .pem files 15 | *.pem 16 | 17 | # .hcl files from Terraform v0.14 and newer 18 | *.hcl 19 | 20 | # working directory for web app 21 | /globo_web_app/* 22 | 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | **2023-06-02** 2 | 3 | Partial Course Update 4 | 5 | This is a revision of the previous course using Terraform 1.4. The exercises and content of the course have changed based on feedback and updates to the behavior and features available in Terraform. 6 | 7 | I have removed some content and I am moving it to *Terraform - Deep Dive*. I also added more context around state data and how Terraform decides to create, update, and delete resources based on configuration, state, and the target environment. 8 | 9 | The previous version of the course will remain available under branch `v3`. 10 | 11 | * Updated course to use version 4.X of AWS provider 12 | * Removed module 9 of the course that dealt with workspaces 13 | * Rewrote s3.tf to use new AWS resources 14 | * Changed resource naming to be more descriptive 15 | * Updated variable names to be more accurate 16 | * Moved website content to a local value 17 | * Updated version of VPC module being used 18 | 19 | **2021-10-07** 20 | 21 | Full Course Update 22 | 23 | This is a revision of the entire course using Terraform version 1.x. The exercises and content of the course have changed based on feedback and what I've learned presenting Terraform to user groups and individuals. Here are the major changes: 24 | 25 | * Removed the AzureRM provider from the course 26 | * Removed the need to supply EC2 Key Pairs 27 | * Removed the use of the default VPC 28 | * Switched from Classic ELB to an Application Load Balancer 29 | * Removed the use of file and remote provisioners 30 | 31 | Many of the removals are meant to simplify the learning process by removing common stumbling blocks. The provisioners were removed b/c HashiCorp now strongly discourages them. They will still be discussed in the course, but not used in the demos. 32 | 33 | **2020-12-21** 34 | 35 | Updated for Terraform version 0.14.3: 36 | 37 | * Add detail to README on how to create an Azure service principal 38 | * Added comments to `terraform.tfvars.example` files to improve usability 39 | * Added `.gitignore` exception for `.hcl`files, since Terraform 0.14 now creates these to lock provider versions 40 | * Updated information about the certification guide 41 | * Moved `terraform init` in module 6 due to changes in `terraform console` 42 | * Added environment variable directions for Bash in module 7 43 | * Added `required_providers` block to `terraform` block to set required version of Azure RM provider 44 | * Added `features` block and `skip_provider_registration` to Azure RM provider config -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ned Bellavance 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Getting-Started-Terraform 2 | 3 | Welcome to Terraform - Getting Started. These exercise files are meant to accompany my course on [Pluralsight](http://www.pluralsight.com/courses/terraform-getting-started-2023). The course was developed using version 1.4.2 of Terraform. Since the GA of version 1.0, HashiCorp has committed to no breaking changes until version 2.0. All the exercises in this course should be valid as long as you are using Terraform in the 1.X version family. The modules and providers in this course have no such guarantee, so I have pinned the version where applicable. 4 | 5 | If you're looking for the exercise files for older versions of the course, there are two branches to check out: 6 | 7 | * [v3](https://github.com/ned1313/Getting-Started-Terraform/tree/v3) - Corresponding to the [course published on Novemeber 2, 2021](https://app.pluralsight.com/library/courses/terraform-getting-started-2021/table-of-contents). 8 | * [pre-1.0] - Corresponding to the [course published on October 1, 2019](https://app.pluralsight.com/library/courses/getting-started-terraform/table-of-contents). 9 | 10 | I don't recommend taking the older courses, as they are out of date and do not cover the latest features of Terraform. I have left the branches available since you may be assigned an older course by your employer. 11 | 12 | ## Using the files 13 | 14 | In the course, you are given a Terraform configuration for a basic web application from someone else on the Globomantics team. We are going to make a copy of this configuration and modify it over the length of the course. The `base_web_app` directory has the basic web app configuration, which we will copy over to a new directory called `globo_web_app`, and start making alterations. 15 | 16 | As we progress through the modules, you will be challenged to make updates to the files in `globo_web_app` to meet the evolving needs and requirements of the web application infrastructure. The completed solution for each module is in the `mX_solution` directories. I recommend trying to first write the solution yourself, and then checking your answer against what you see in the solution file. 17 | 18 | At the beginning of module three, we will hardcode our AWS keys into the configuration. **You should not do this in any real world scenario!** I am trying to illustrate a point in the course. *Please do not commit your AWS keys to source control*. Some of us (**me**) have done this in the past and felt very silly. We will move these keys into environment variables in module four, and they shall never again be hardcoded **anywhere**. 19 | 20 | The suggested commands for each module are in the `commands` directory. You can run these commands to get through the exercises, but I also encourage you to experiment and try things out for yourself. 21 | 22 | ## Providers 23 | 24 | This course uses the AWS and Random providers. At the start of the course, we are not specifying a particular version of the AWS provider, meaning Terraform will grab the latest version. The **day** after I finished the course, version `5.0` of the AWS provider was released 😔. So while you will see version `4.X` in the course video, when you initialize your configuration for the first time, you will get version `5.X`. They are no breaking changes in the AWS provider between `4.X` and `5.X` that impact the example code, so it *should* all work as expected, with one minor exception. 25 | 26 | In module 6, we add the Random provider and specify required versions for both providers, setting the AWS provider to use major version 4. In the videos, I am already using major version 4, so Terraform didn't have a problem with adding the version constraint and running `terraform init`. However, you will be using major version 5 of the provider, and the addition of the version constraint will mean Terraform has to download a different version of the provider and update the lock file. When you run `terraform init`, you will get an error message indicating that your version constraints have changed and you need to run `terraform init -upgrade` to update the lock file. You can safely run this command and everything will work as expected. 27 | 28 | I admit the `-upgrade` switch is a little misleading, as it implies that you are moving to a newer version of the provider, when in reality you're moving down a major version. I wish the command was something else, like `terraform init -update` or something. But alas, it is what it is. 29 | 30 | ## AWS Environment 31 | 32 | You will need access to an AWS environment with permissions to create resources in EC2, S3, and IAM. I recommend creating a dedicated account just for this course. The exercises have been tested with AWS region `us-east-1`. The input variable `aws_region` has `us-east-1` set as the default, but you can supply a different region if you prefer. Generally, the exercises should work in any region that has at least three availability zones and an Amazon Linux 2 AMI. 33 | 34 | You will need to generate an AWS access key to run through the exercises. You can do this through the IAM console in a browser (*hint*: it's under security credentials for your user) by following the [official AWS docs](https://aws.amazon.com/premiumsupport/knowledge-center/create-access-key/). I'd recommend assigning the `AdministratorAccess` policy to your user to give you permissions to do everything in the account. Also, enable 2FA for the user account! 35 | 36 | ## Line Endings 37 | 38 | An issue I have discovered from time to time is that Terraform doesn't much like the Windows style of ending a line with both a Carriage Return (CR) and a Line Feed (LF), commonly referred to as CRLF. If you are experiencing strange parsing issues, change the line ending to be Line Feed (LF) only. In VS Code this can be down by clicking on the CRLF in the lower right corner and changing it to LF. This primarily affects template files or scripts being sent to a Linux machine for processing. 39 | 40 | ## MONEY!!! 41 | 42 | A gentle reminder about cost. The course will have you creating resources in AWS. Some of the resources are not going to be 100% free. I have tried to use the [Free-tier](https://aws.amazon.com/free/) when possible, but you many still incur some charges. 43 | 44 | When you complete an exercise in the course, you can easily tear down the deployed infrastructure using `terraform destroy`. Just run that command and approve the destruction to remove all resources from AWS. Before you start the next module, run `terraform apply` again and you should be right where you started. Isn't infrastructure automation amazing?! 45 | 46 | ## Certification 47 | 48 | HashiCorp has released the Terraform Certified Associate certification. You might be wondering if this course fully prepares you for the cert. **It does not.** Taking this course along with the [Deep Dive - Terraform](https://app.pluralsight.com/library/courses/terraform-deep-dive) course on Pluralsight will meet most of the learning objectives for the certification, but there is no substitute for running the software on your own and hacking away. 49 | 50 | I have coauthored a certification guide which you can find on [Leanpub](https://leanpub.com/terraform-certified/). This is an unofficial guide, but I believe in concert with the Pluralsight courses you will be in a good position to sit the exam. And in case you're wondering, yes it is fully updated for version 003 of the exam that was released in May 2023. 51 | 52 | ## Conclusion 53 | 54 | I hope you enjoy taking this course as much as I did creating it. I'd love to hear feedback and suggestions for revisions. Find me on Twitter (@ned1313) or add an issue to this repository. 55 | 56 | Thanks and happy automating! 57 | 58 | Ned 59 | -------------------------------------------------------------------------------- /base_web_app/main.tf: -------------------------------------------------------------------------------- 1 | ################################################################################## 2 | # PROVIDERS 3 | ################################################################################## 4 | 5 | provider "aws" { 6 | access_key = "ACCESS_KEY" 7 | secret_key = "SECRET_KEY" 8 | region = "us-east-1" 9 | } 10 | 11 | ################################################################################## 12 | # DATA 13 | ################################################################################## 14 | 15 | data "aws_ssm_parameter" "amzn2_linux" { 16 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 17 | } 18 | 19 | ################################################################################## 20 | # RESOURCES 21 | ################################################################################## 22 | 23 | # NETWORKING # 24 | resource "aws_vpc" "app" { 25 | cidr_block = "10.0.0.0/16" 26 | enable_dns_hostnames = true 27 | 28 | } 29 | 30 | resource "aws_internet_gateway" "app" { 31 | vpc_id = aws_vpc.app.id 32 | 33 | } 34 | 35 | resource "aws_subnet" "public_subnet1" { 36 | cidr_block = "10.0.0.0/24" 37 | vpc_id = aws_vpc.app.id 38 | map_public_ip_on_launch = true 39 | } 40 | 41 | # ROUTING # 42 | resource "aws_route_table" "app" { 43 | vpc_id = aws_vpc.app.id 44 | 45 | route { 46 | cidr_block = "0.0.0.0/0" 47 | gateway_id = aws_internet_gateway.app.id 48 | } 49 | } 50 | 51 | resource "aws_route_table_association" "app_subnet1" { 52 | subnet_id = aws_subnet.public_subnet1.id 53 | route_table_id = aws_route_table.app.id 54 | } 55 | 56 | # SECURITY GROUPS # 57 | # Nginx security group 58 | resource "aws_security_group" "nginx_sg" { 59 | name = "nginx_sg" 60 | vpc_id = aws_vpc.app.id 61 | 62 | # HTTP access from anywhere 63 | ingress { 64 | from_port = 80 65 | to_port = 80 66 | protocol = "tcp" 67 | cidr_blocks = ["0.0.0.0/0"] 68 | } 69 | 70 | # outbound internet access 71 | egress { 72 | from_port = 0 73 | to_port = 0 74 | protocol = "-1" 75 | cidr_blocks = ["0.0.0.0/0"] 76 | } 77 | } 78 | 79 | # INSTANCES # 80 | resource "aws_instance" "nginx1" { 81 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value) 82 | instance_type = "t3.micro" 83 | subnet_id = aws_subnet.public_subnet1.id 84 | vpc_security_group_ids = [aws_security_group.nginx_sg.id] 85 | 86 | user_data = <Taco Team Server

You did it! Have a 🌮

' | sudo tee /usr/share/nginx/html/index.html 92 | EOF 93 | 94 | } 95 | 96 | -------------------------------------------------------------------------------- /commands/m3_commands.sh: -------------------------------------------------------------------------------- 1 | # You can find the installer info for Terraform here: 2 | https://developer.hashicorp.com/terraform/downloads 3 | 4 | # First we'll start by making sure Terraform is installed and check out the 5 | # help command baked in to the cli. 6 | terraform version 7 | 8 | terraform -help 9 | 10 | # Terraform follows the terraform syntax 11 | # Options use a single dash whether it's a single character option 12 | # or full word. 13 | 14 | # In this module, we are simply trying to get the configuration deployed. 15 | # First we'll copy our file from the base_web_app to a working directory 16 | mkdir globo_web_app 17 | cp ./base_web_app/main.tf ./globo_web_app/main.tf 18 | 19 | # Now we can work with the main.tf file in globo_web_app 20 | cd globo_web_app 21 | 22 | # Open the main.tf file in your code editor and replace the values 23 | # for the AWS keys in the config file 24 | 25 | # !! DO NOT COMMIT THESE TO SOURCE CONTROL !! 26 | 27 | # Now we will follow the standard Terraform workflow 28 | terraform init 29 | terraform plan -out m3.tfplan 30 | terraform apply "m3.tfplan" 31 | 32 | # Got to the Console and get the Public IP address for the EC2 instance 33 | # and browse to port 80. 34 | 35 | # If you are done, you can tear things down to save $$ 36 | terraform destroy 37 | -------------------------------------------------------------------------------- /commands/m4_commands.sh: -------------------------------------------------------------------------------- 1 | # Now we are going to start refactoring by adding some variables 2 | # and local values to our configuration. This makes it more reusable 3 | # and simplifies configuration updates. 4 | terraform init 5 | terraform validate 6 | 7 | # We will pass our variables at the command line 8 | terraform plan -var=billing_code="ACCT8675309" -var=project="web-app" -var=aws_access_key="YOUR_ACCESS_KEY" -var=aws_secret_key="YOUR_SECRET_KEY" -out m4.tfplan 9 | 10 | # Ugh that's unwieldy. Let's store our nonsensitive variables in a file called 11 | # terraform.tfvars 12 | 13 | # And we can store our sensitive data in environment variables like so 14 | # For Linux and MacOS 15 | export TF_VAR_aws_access_key=YOUR_ACCESS_KEY 16 | export TF_VAR_aws_secret_key=YOUR_SECRET_KEY 17 | 18 | # For PowerShell 19 | $env:TF_VAR_aws_access_key="YOUR_ACCESS_KEY" 20 | $env:TF_VAR_aws_secret_key="YOUR_SECRET_KEY" 21 | 22 | # Now we can run plan without all that extra stuff 23 | terraform plan -out m4.tfplan 24 | terraform apply "m4.tfplan" 25 | 26 | terraform show 27 | terraform output 28 | 29 | # You can always tear it down to save $$ 30 | terraform destroy -------------------------------------------------------------------------------- /commands/m5_commands.sh: -------------------------------------------------------------------------------- 1 | # Time to make our deployment a bit more resilient! 2 | # We are going to add a second instance and load balance the 3 | # two instances behind a load balancer. We are going to need 4 | # to discover availability zones, add subnets, and add an 5 | # application load balancer, target group, load balancer listener 6 | # and target group attachment. 7 | terraform state list 8 | terraform state show aws_instance.nginx1 9 | 10 | terraform validate 11 | 12 | # In case you don't have them set anymore don't forget to run the export commands 13 | # For Linux and MacOS 14 | export TF_VAR_aws_access_key=YOUR_ACCESS_KEY 15 | export TF_VAR_aws_secret_key=YOUR_SECRET_KEY 16 | 17 | # For PowerShell 18 | $env:TF_VAR_aws_access_key="YOUR_ACCESS_KEY" 19 | $env:TF_VAR_aws_secret_key="YOUR_SECRET_KEY" 20 | 21 | terraform plan -out m5.tfplan 22 | terraform apply m5.tfplan -------------------------------------------------------------------------------- /commands/m6_commands.sh: -------------------------------------------------------------------------------- 1 | # Instead of storing our AWS keys in input variables, we will instead store 2 | # them in environment variables. The AWS provider will check for values 3 | # stored in AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables. 4 | 5 | # For Linux and MacOS 6 | export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY 7 | export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY 8 | 9 | # For PowerShell 10 | $env:AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY" 11 | $env:AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY" 12 | 13 | # Now we can remove the variables from our configuration 14 | 15 | # Once the updates are complete we'll run the standard workflow 16 | terraform fmt 17 | 18 | terraform init 19 | terraform validate 20 | 21 | terraform plan -out m6.tfplan 22 | terraform apply m6.tfplan 23 | 24 | -------------------------------------------------------------------------------- /commands/m7_commands.sh: -------------------------------------------------------------------------------- 1 | # Let's test out the use of functions! 2 | # You actually need to initialize the config before terraform console will work. 3 | terraform init 4 | 5 | terraform console 6 | 7 | # Now we can try some different functions and syntax 8 | min(42,5,16) 9 | lower("TACOCAT") 10 | cidrsubnet(var.vpc_cidr_block, 8, 0) 11 | cidrhost(cidrsubnet(var.vpc_cidr_block, 8, 0),5) 12 | 13 | local.common_tags 14 | local.common_tags["company"] 15 | local.common_tags["missing"] 16 | lookup(local.common_tags, "company", "Unknown") 17 | lookup(local.common_tags, "missing", "Unknown") 18 | 19 | 20 | # Update the configuration by adding count and for_each loops and 21 | # adding functions where helpful. 22 | 23 | terraform fmt 24 | terraform validate 25 | 26 | # For Linux and MacOS 27 | export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY 28 | export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY 29 | 30 | # For PowerShell 31 | $env:AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY" 32 | $env:AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY" 33 | 34 | terraform plan -out m7.tfplan 35 | terraform apply m7.tfplan -------------------------------------------------------------------------------- /commands/m8_commands.sh: -------------------------------------------------------------------------------- 1 | # Modules are a powerful tool to group data sources and resources together 2 | # into a logical unit that can be reused and distributed. Let's make use 3 | # of the official VPC module on the Terraform registry and a module we 4 | # write ourselves for s3 resources associated with web applications. 5 | 6 | terraform console 7 | 8 | # Test out the range function 9 | range(var.vpc_public_subnet_count) 10 | # Try it in a for expression 11 | [for subnet in range(var.vpc_public_subnet_count): cidrsubnet(var.vpc_cidr_block, 8, subnet)] 12 | 13 | # Prepare config 14 | terraform init 15 | terraform fmt -recursive 16 | terraform validate 17 | 18 | # For Linux and MacOS 19 | export AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY 20 | export AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY 21 | 22 | # For PowerShell 23 | $env:AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY" 24 | $env:AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY" 25 | 26 | # Update the deployment 27 | terraform plan -out m8.tfplan 28 | terraform apply m8.tfplan -------------------------------------------------------------------------------- /m4_solution/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | common_tags = { 3 | company = var.company 4 | project = "${var.company}-${var.project}" 5 | billing_code = var.billing_code 6 | } 7 | } -------------------------------------------------------------------------------- /m4_solution/main.tf: -------------------------------------------------------------------------------- 1 | ################################################################################## 2 | # PROVIDERS 3 | ################################################################################## 4 | 5 | provider "aws" { 6 | access_key = var.aws_access_key 7 | secret_key = var.aws_secret_key 8 | region = var.aws_region 9 | } 10 | 11 | ################################################################################## 12 | # DATA 13 | ################################################################################## 14 | 15 | data "aws_ssm_parameter" "amzn2_linux" { 16 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 17 | } 18 | 19 | ################################################################################## 20 | # RESOURCES 21 | ################################################################################## 22 | 23 | # NETWORKING # 24 | resource "aws_vpc" "app" { 25 | cidr_block = var.vpc_cidr_block 26 | enable_dns_hostnames = var.enable_dns_hostnames 27 | 28 | tags = local.common_tags 29 | } 30 | 31 | resource "aws_internet_gateway" "app" { 32 | vpc_id = aws_vpc.app.id 33 | 34 | tags = local.common_tags 35 | } 36 | 37 | resource "aws_subnet" "public_subnet1" { 38 | cidr_block = var.vpc_public_subnet1_cidr_block 39 | vpc_id = aws_vpc.app.id 40 | map_public_ip_on_launch = var.map_public_ip_on_launch 41 | 42 | tags = local.common_tags 43 | } 44 | 45 | # ROUTING # 46 | resource "aws_route_table" "app" { 47 | vpc_id = aws_vpc.app.id 48 | 49 | route { 50 | cidr_block = "0.0.0.0/0" 51 | gateway_id = aws_internet_gateway.app.id 52 | } 53 | 54 | tags = local.common_tags 55 | } 56 | 57 | resource "aws_route_table_association" "app_public_subnet1" { 58 | subnet_id = aws_subnet.public_subnet1.id 59 | route_table_id = aws_route_table.app.id 60 | } 61 | 62 | # SECURITY GROUPS # 63 | # Nginx security group 64 | resource "aws_security_group" "nginx_sg" { 65 | name = "nginx_sg" 66 | vpc_id = aws_vpc.app.id 67 | 68 | # HTTP access from anywhere 69 | ingress { 70 | from_port = 80 71 | to_port = 80 72 | protocol = "tcp" 73 | cidr_blocks = ["0.0.0.0/0"] 74 | } 75 | 76 | # outbound internet access 77 | egress { 78 | from_port = 0 79 | to_port = 0 80 | protocol = "-1" 81 | cidr_blocks = ["0.0.0.0/0"] 82 | } 83 | 84 | tags = local.common_tags 85 | } 86 | 87 | # INSTANCES # 88 | resource "aws_instance" "nginx1" { 89 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value) 90 | instance_type = var.instance_type 91 | subnet_id = aws_subnet.public_subnet1.id 92 | vpc_security_group_ids = [aws_security_group.nginx_sg.id] 93 | 94 | user_data = <Taco Team Server

You did it! Have a 🌮

' | sudo tee /usr/share/nginx/html/index.html 100 | EOF 101 | 102 | tags = local.common_tags 103 | 104 | } 105 | -------------------------------------------------------------------------------- /m4_solution/outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_instance_public_dns" { 2 | value = "http://${aws_instance.nginx1.public_dns}" 3 | description = "Public DNS for EC2 instance" 4 | } -------------------------------------------------------------------------------- /m4_solution/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | billing_code = "ACCT8675309" 2 | 3 | project = "globo-web-app" -------------------------------------------------------------------------------- /m4_solution/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_access_key" { 2 | type = string 3 | description = "AWS access key" 4 | sensitive = true 5 | } 6 | 7 | variable "aws_secret_key" { 8 | type = string 9 | description = "AWS secret key" 10 | sensitive = true 11 | } 12 | 13 | variable "aws_region" { 14 | type = string 15 | description = "AWS region for deployment" 16 | default = "us-east-1" 17 | } 18 | 19 | ## Add these after the first challenge 20 | 21 | variable "enable_dns_hostnames" { 22 | type = bool 23 | description = "Enable DNS hostnames in VPC" 24 | default = true 25 | } 26 | 27 | variable "vpc_cidr_block" { 28 | type = string 29 | description = "Base CIDR Block for VPC" 30 | default = "10.0.0.0/16" 31 | } 32 | 33 | variable "vpc_public_subnet1_cidr_block" { 34 | type = string 35 | description = "CIDR Block for Subnet 1 in VPC" 36 | default = "10.0.0.0/24" 37 | } 38 | 39 | variable "map_public_ip_on_launch" { 40 | type = bool 41 | description = "Map a public IP address for Subnet instances" 42 | default = true 43 | } 44 | 45 | variable "instance_type" { 46 | type = string 47 | description = "Type for EC2 Instnace" 48 | default = "t3.micro" 49 | } 50 | 51 | ## Add these after the local value discussion 52 | 53 | variable "company" { 54 | type = string 55 | description = "Company name for resource tagging" 56 | default = "Globomantics" 57 | } 58 | 59 | variable "project" { 60 | type = string 61 | description = "Project name for resource tagging" 62 | } 63 | 64 | variable "billing_code" { 65 | type = string 66 | description = "Billing code for resource tagging" 67 | } -------------------------------------------------------------------------------- /m5_solution/instances.tf: -------------------------------------------------------------------------------- 1 | ################################################################################## 2 | # DATA 3 | ################################################################################## 4 | 5 | data "aws_ssm_parameter" "amzn2_linux" { 6 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 7 | } 8 | 9 | ################################################################################## 10 | # RESOURCES 11 | ################################################################################## 12 | 13 | # INSTANCES # 14 | resource "aws_instance" "nginx1" { 15 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value) 16 | instance_type = var.instance_type 17 | subnet_id = aws_subnet.public_subnet1.id 18 | vpc_security_group_ids = [aws_security_group.nginx_sg.id] 19 | 20 | user_data = <Taco Team Server 1

You did it! Have a 🌮

' | sudo tee /usr/share/nginx/html/index.html 26 | EOF 27 | 28 | tags = local.common_tags 29 | 30 | } 31 | 32 | resource "aws_instance" "nginx2" { 33 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value) 34 | instance_type = var.instance_type 35 | subnet_id = aws_subnet.public_subnet2.id 36 | vpc_security_group_ids = [aws_security_group.nginx_sg.id] 37 | 38 | user_data = <Taco Team Server 2

You did it! Have a 🌮

' | sudo tee /usr/share/nginx/html/index.html 44 | EOF 45 | 46 | tags = local.common_tags 47 | 48 | } -------------------------------------------------------------------------------- /m5_solution/loadbalancer.tf: -------------------------------------------------------------------------------- 1 | # aws_lb 2 | resource "aws_lb" "nginx" { 3 | name = "globo-web-alb" 4 | internal = false 5 | load_balancer_type = "application" 6 | security_groups = [aws_security_group.alb_sg.id] 7 | subnets = [aws_subnet.public_subnet1.id, aws_subnet.public_subnet2.id] 8 | 9 | enable_deletion_protection = false 10 | 11 | tags = local.common_tags 12 | } 13 | 14 | # aws_lb_target_group 15 | resource "aws_lb_target_group" "nginx" { 16 | name = "nginx-alb-tg" 17 | port = 80 18 | protocol = "HTTP" 19 | vpc_id = aws_vpc.app.id 20 | 21 | tags = local.common_tags 22 | } 23 | 24 | # aws_lb_listener 25 | resource "aws_lb_listener" "nginx" { 26 | load_balancer_arn = aws_lb.nginx.arn 27 | port = "80" 28 | protocol = "HTTP" 29 | 30 | default_action { 31 | type = "forward" 32 | target_group_arn = aws_lb_target_group.nginx.arn 33 | } 34 | 35 | tags = local.common_tags 36 | } 37 | 38 | # aws_lb_target_group_attachment 39 | resource "aws_lb_target_group_attachment" "nginx1" { 40 | target_group_arn = aws_lb_target_group.nginx.arn 41 | target_id = aws_instance.nginx1.id 42 | port = 80 43 | } 44 | 45 | resource "aws_lb_target_group_attachment" "nginx2" { 46 | target_group_arn = aws_lb_target_group.nginx.arn 47 | target_id = aws_instance.nginx2.id 48 | port = 80 49 | } -------------------------------------------------------------------------------- /m5_solution/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | common_tags = { 3 | company = var.company 4 | project = "${var.company}-${var.project}" 5 | billing_code = var.billing_code 6 | } 7 | } -------------------------------------------------------------------------------- /m5_solution/network.tf: -------------------------------------------------------------------------------- 1 | ################################################################################## 2 | # PROVIDERS 3 | ################################################################################## 4 | 5 | provider "aws" { 6 | access_key = var.aws_access_key 7 | secret_key = var.aws_secret_key 8 | region = var.aws_region 9 | } 10 | 11 | ################################################################################## 12 | # DATA 13 | ################################################################################## 14 | 15 | data "aws_availability_zones" "available" { 16 | state = "available" 17 | } 18 | 19 | ################################################################################## 20 | # RESOURCES 21 | ################################################################################## 22 | 23 | # NETWORKING # 24 | resource "aws_vpc" "app" { 25 | cidr_block = var.vpc_cidr_block 26 | enable_dns_hostnames = var.enable_dns_hostnames 27 | 28 | tags = local.common_tags 29 | } 30 | 31 | resource "aws_internet_gateway" "app" { 32 | vpc_id = aws_vpc.app.id 33 | 34 | tags = local.common_tags 35 | } 36 | 37 | resource "aws_subnet" "public_subnet1" { 38 | cidr_block = var.vpc_public_subnets_cidr_block[0] 39 | vpc_id = aws_vpc.app.id 40 | availability_zone = data.aws_availability_zones.available.names[0] 41 | map_public_ip_on_launch = var.map_public_ip_on_launch 42 | 43 | tags = local.common_tags 44 | } 45 | 46 | resource "aws_subnet" "public_subnet2" { 47 | cidr_block = var.vpc_public_subnets_cidr_block[1] 48 | vpc_id = aws_vpc.app.id 49 | availability_zone = data.aws_availability_zones.available.names[1] 50 | map_public_ip_on_launch = var.map_public_ip_on_launch 51 | 52 | tags = local.common_tags 53 | } 54 | 55 | # ROUTING # 56 | resource "aws_route_table" "app" { 57 | vpc_id = aws_vpc.app.id 58 | 59 | route { 60 | cidr_block = "0.0.0.0/0" 61 | gateway_id = aws_internet_gateway.app.id 62 | } 63 | 64 | tags = local.common_tags 65 | } 66 | 67 | resource "aws_route_table_association" "app_public_subnet1" { 68 | subnet_id = aws_subnet.public_subnet1.id 69 | route_table_id = aws_route_table.app.id 70 | } 71 | 72 | resource "aws_route_table_association" "app_public_subnet2" { 73 | subnet_id = aws_subnet.public_subnet2.id 74 | route_table_id = aws_route_table.app.id 75 | } 76 | 77 | # SECURITY GROUPS # 78 | # Nginx security group 79 | resource "aws_security_group" "nginx_sg" { 80 | name = "nginx_sg" 81 | vpc_id = aws_vpc.app.id 82 | 83 | # HTTP access from anywhere 84 | ingress { 85 | from_port = 80 86 | to_port = 80 87 | protocol = "tcp" 88 | cidr_blocks = [var.vpc_cidr_block] 89 | } 90 | 91 | # outbound internet access 92 | egress { 93 | from_port = 0 94 | to_port = 0 95 | protocol = "-1" 96 | cidr_blocks = ["0.0.0.0/0"] 97 | } 98 | 99 | tags = local.common_tags 100 | } 101 | 102 | # ALB Security Group 103 | resource "aws_security_group" "alb_sg" { 104 | name = "nginx_alb_sg" 105 | vpc_id = aws_vpc.app.id 106 | 107 | # HTTP access from anywhere 108 | ingress { 109 | from_port = 80 110 | to_port = 80 111 | protocol = "tcp" 112 | cidr_blocks = ["0.0.0.0/0"] 113 | } 114 | 115 | # outbound internet access 116 | egress { 117 | from_port = 0 118 | to_port = 0 119 | protocol = "-1" 120 | cidr_blocks = ["0.0.0.0/0"] 121 | } 122 | 123 | tags = local.common_tags 124 | } 125 | -------------------------------------------------------------------------------- /m5_solution/outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_alb_public_dns" { 2 | value = "http://${aws_lb.nginx.dns_name}" 3 | description = "Public DNS for the ALB" 4 | } -------------------------------------------------------------------------------- /m5_solution/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | billing_code = "ACCT8675309" 2 | 3 | project = "globo-web-app" -------------------------------------------------------------------------------- /m5_solution/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_access_key" { 2 | type = string 3 | description = "AWS Access Key" 4 | sensitive = true 5 | } 6 | 7 | variable "aws_secret_key" { 8 | type = string 9 | description = "AWS Secret Key" 10 | sensitive = true 11 | } 12 | 13 | variable "aws_region" { 14 | type = string 15 | description = "Region for AWS Resources" 16 | default = "us-east-1" 17 | } 18 | 19 | variable "enable_dns_hostnames" { 20 | type = bool 21 | description = "Enable DNS hostnames in VPC" 22 | default = true 23 | } 24 | 25 | variable "vpc_cidr_block" { 26 | type = string 27 | description = "Base CIDR Block for VPC" 28 | default = "10.0.0.0/16" 29 | } 30 | 31 | variable "vpc_public_subnets_cidr_block" { 32 | type = list(string) 33 | description = "CIDR Block for Public Subnets in VPC" 34 | default = ["10.0.0.0/24", "10.0.1.0/24"] 35 | } 36 | 37 | variable "map_public_ip_on_launch" { 38 | type = bool 39 | description = "Map a public IP address for Subnet instances" 40 | default = true 41 | } 42 | 43 | variable "instance_type" { 44 | type = string 45 | description = "Type for EC2 Instance" 46 | default = "t3.micro" 47 | } 48 | 49 | variable "company" { 50 | type = string 51 | description = "Company name for resource tagging" 52 | default = "Globomantics" 53 | } 54 | 55 | variable "project" { 56 | type = string 57 | description = "Project name for resource tagging" 58 | } 59 | 60 | variable "billing_code" { 61 | type = string 62 | description = "Billing code for resource tagging" 63 | } -------------------------------------------------------------------------------- /m6_solution/instances.tf: -------------------------------------------------------------------------------- 1 | ################################################################################## 2 | # DATA 3 | ################################################################################## 4 | 5 | data "aws_ssm_parameter" "ami" { 6 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 7 | } 8 | 9 | ################################################################################## 10 | # RESOURCES 11 | ################################################################################## 12 | 13 | # INSTANCES # 14 | resource "aws_instance" "nginx1" { 15 | ami = nonsensitive(data.aws_ssm_parameter.ami.value) 16 | instance_type = var.instance_type 17 | subnet_id = aws_subnet.subnet1.id 18 | vpc_security_group_ids = [aws_security_group.nginx-sg.id] 19 | iam_instance_profile = aws_iam_instance_profile.nginx_profile.name 20 | depends_on = [aws_iam_role_policy.allow_s3_all] 21 | 22 | user_data = < 2 | 3 | Globomantics Test Site 4 | 5 | 6 |

7 | YEAH! 8 |

9 | 10 | 11 | -------------------------------------------------------------------------------- /m7_solution/instances.tf: -------------------------------------------------------------------------------- 1 | data "aws_ssm_parameter" "amzn2_linux" { 2 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 3 | } 4 | 5 | # INSTANCES # 6 | resource "aws_instance" "nginx" { 7 | count = var.instance_count 8 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value) 9 | instance_type = var.instance_type 10 | subnet_id = aws_subnet.public_subnets[(count.index % var.vpc_public_subnet_count)].id 11 | vpc_security_group_ids = [aws_security_group.nginx_sg.id] 12 | iam_instance_profile = aws_iam_instance_profile.nginx_profile.name 13 | depends_on = [aws_iam_role_policy.allow_s3_all] 14 | 15 | user_data = templatefile("${path.module}/templates/startup_script.tpl", { 16 | s3_bucket_name = aws_s3_bucket.web_bucket.id 17 | }) 18 | 19 | 20 | tags = merge(local.common_tags, { 21 | Name = "${local.naming_prefix}-nginx-${count.index}" 22 | }) 23 | } 24 | 25 | resource "aws_iam_role" "allow_nginx_s3" { 26 | name = "allow_nginx_s3" 27 | 28 | assume_role_policy = < 2 | 3 | Globomantics Test Site 4 | 5 | 6 |

7 | YEAH! 8 |

9 | 10 | 11 | -------------------------------------------------------------------------------- /m8_solution/instances.tf: -------------------------------------------------------------------------------- 1 | data "aws_ssm_parameter" "amzn2_linux" { 2 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" 3 | } 4 | 5 | # INSTANCES # 6 | resource "aws_instance" "nginx" { 7 | count = var.instance_count 8 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value) 9 | instance_type = var.instance_type 10 | subnet_id = module.app.public_subnets[(count.index % var.vpc_public_subnet_count)] 11 | vpc_security_group_ids = [aws_security_group.nginx_sg.id] 12 | iam_instance_profile = module.web_app_s3.instance_profile.name 13 | depends_on = [module.web_app_s3] 14 | 15 | user_data = templatefile("${path.module}/templates/startup_script.tpl", { 16 | s3_bucket_name = module.web_app_s3.web_bucket.id 17 | }) 18 | 19 | 20 | tags = merge(local.common_tags, { 21 | Name = "${local.naming_prefix}-nginx-${count.index}" 22 | }) 23 | } 24 | 25 | -------------------------------------------------------------------------------- /m8_solution/loadbalancer.tf: -------------------------------------------------------------------------------- 1 | # aws_elb_service_account 2 | 3 | data "aws_elb_service_account" "root" {} 4 | 5 | # aws_lb 6 | resource "aws_lb" "nginx" { 7 | name = "${local.naming_prefix}-alb" 8 | internal = false 9 | load_balancer_type = "application" 10 | security_groups = [aws_security_group.alb_sg.id] 11 | subnets = module.app.public_subnets 12 | depends_on = [module.web_app_s3] 13 | 14 | enable_deletion_protection = false 15 | 16 | access_logs { 17 | bucket = module.web_app_s3.web_bucket.id 18 | prefix = "alb-logs" 19 | enabled = true 20 | } 21 | 22 | tags = local.common_tags 23 | } 24 | 25 | # aws_lb_target_group 26 | resource "aws_lb_target_group" "nginx" { 27 | name = "${local.naming_prefix}-alb-tg" 28 | port = 80 29 | protocol = "HTTP" 30 | vpc_id = module.app.vpc_id 31 | 32 | tags = local.common_tags 33 | } 34 | 35 | # aws_lb_listener 36 | resource "aws_lb_listener" "nginx" { 37 | load_balancer_arn = aws_lb.nginx.arn 38 | port = "80" 39 | protocol = "HTTP" 40 | 41 | default_action { 42 | type = "forward" 43 | target_group_arn = aws_lb_target_group.nginx.arn 44 | } 45 | 46 | tags = merge(local.common_tags, { 47 | Name = "${local.naming_prefix}-nginx" 48 | }) 49 | } 50 | 51 | # aws_lb_target_group_attachment 52 | resource "aws_lb_target_group_attachment" "nginx" { 53 | count = var.instance_count 54 | target_group_arn = aws_lb_target_group.nginx.arn 55 | target_id = aws_instance.nginx[count.index].id 56 | port = 80 57 | } 58 | 59 | -------------------------------------------------------------------------------- /m8_solution/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | common_tags = { 3 | company = var.company 4 | project = "${var.company}-${var.project}" 5 | billing_code = var.billing_code 6 | environment = var.environment 7 | } 8 | 9 | s3_bucket_name = "${lower(local.naming_prefix)}-${random_integer.s3.result}" 10 | 11 | website_content = { 12 | website = "/website/index.html" 13 | logo = "/website/Globo_logo_Vert.png" 14 | } 15 | 16 | naming_prefix = "${var.naming_prefix}-${var.environment}" 17 | } 18 | 19 | resource "random_integer" "s3" { 20 | min = 10000 21 | max = 99999 22 | } -------------------------------------------------------------------------------- /m8_solution/modules/globo-web-app-s3/main.tf: -------------------------------------------------------------------------------- 1 | # S3 Bucket config# 2 | resource "aws_s3_bucket" "web_bucket" { 3 | bucket = var.bucket_name 4 | force_destroy = true 5 | 6 | tags = var.common_tags 7 | 8 | } 9 | 10 | resource "aws_s3_bucket_policy" "web_bucket" { 11 | bucket = aws_s3_bucket.web_bucket.id 12 | policy = < 2 | 3 | Globomantics Test Site 4 | 5 | 6 |

7 | YEAH! 8 |

9 | 10 | 11 | -------------------------------------------------------------------------------- /website/Globo_logo_Vert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ned1313/Getting-Started-Terraform/8e90313f09c1393a4f781c8610b4b9b810da1e59/website/Globo_logo_Vert.png -------------------------------------------------------------------------------- /website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Globomantics Test Site 4 | 5 | 6 |

7 | YEAH! 8 |

9 | 10 | 11 | --------------------------------------------------------------------------------