├── 09-A-Web-Server ├── modules │ └── tls │ │ ├── variables.tf │ │ ├── main.tf │ │ ├── README │ │ ├── outputs.tf │ │ └── LICENSE ├── outputs.tf ├── variables.tf ├── install_libraries.sh ├── main.tf └── guide.md ├── .vscode └── settings.json ├── 08-Modules ├── modules │ └── tls │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── variables.tf │ │ └── LICENSE ├── outputs.tf ├── variables.tf ├── main.tf └── guide.md ├── 04-Variables-Continued ├── terraform.tfvars ├── main.tf ├── variables.tf ├── guide.md └── .terraform.lock.hcl ├── 06-Data-Blocks ├── output.tf ├── variables.tf ├── main.tf └── guide.md ├── 07-Another-Provider ├── output.tf ├── variables.tf ├── main.tf ├── guide.md └── .terraform.lock.hcl ├── README.md ├── 05-AWS-Setup-and-Outputs ├── variables.tf ├── main.tf ├── .terraform.lock.hcl └── guide.md ├── 02-Lock-and-State-Files ├── main.tf ├── .terraform.lock.hcl └── guide.md ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitignore ├── 03-Variables ├── main.tf ├── guide.md └── .terraform.lock.hcl ├── LICENSE └── 01-Basic-Commands ├── .terraform.lock.hcl ├── main.tf └── guide.md /09-A-Web-Server/modules/tls/variables.tf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "kreuzwerker" 4 | ] 5 | } -------------------------------------------------------------------------------- /09-A-Web-Server/modules/tls/main.tf: -------------------------------------------------------------------------------- 1 | resource "tls_private_key" "mykey" { 2 | algorithm = "RSA" 3 | rsa_bits = 4096 4 | } -------------------------------------------------------------------------------- /08-Modules/modules/tls/README.md: -------------------------------------------------------------------------------- 1 | # TLS Module 2 | 3 | Creates a private/public key pair to be used with EC2 instances to ssh into them. -------------------------------------------------------------------------------- /08-Modules/modules/tls/main.tf: -------------------------------------------------------------------------------- 1 | resource "tls_private_key" "mykey" { 2 | algorithm = var.algorithm 3 | rsa_bits = var.rsa_bits 4 | } -------------------------------------------------------------------------------- /09-A-Web-Server/modules/tls/README: -------------------------------------------------------------------------------- 1 | # TLS Module 2 | 3 | Creates a private/public key pair to be used with EC2 instances to ssh into them. -------------------------------------------------------------------------------- /04-Variables-Continued/terraform.tfvars: -------------------------------------------------------------------------------- 1 | image_name = "nginx" 2 | image_tag = "1.23.3" 3 | internal_port = 80 4 | external_port = 8090 -------------------------------------------------------------------------------- /06-Data-Blocks/output.tf: -------------------------------------------------------------------------------- 1 | output "public_ip" { 2 | value = aws_instance.webserver.public_ip 3 | description = "EC2 Public IP" 4 | } -------------------------------------------------------------------------------- /09-A-Web-Server/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_ip" { 2 | value = aws_instance.webserver.public_ip 3 | description = "EC2 Public IP" 4 | } -------------------------------------------------------------------------------- /08-Modules/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_ip" { 2 | value = aws_instance.webserver.public_ip 3 | description = "EC2 Public IP" 4 | } 5 | 6 | output "private_key" { 7 | value = module.tls.private_key_out 8 | sensitive = true 9 | } -------------------------------------------------------------------------------- /08-Modules/modules/tls/outputs.tf: -------------------------------------------------------------------------------- 1 | output "private_key_out" { 2 | value = tls_private_key.mykey.private_key_pem 3 | sensitive = true 4 | } 5 | 6 | output "public_key_out" { 7 | value = tls_private_key.mykey.public_key_openssh 8 | } 9 | -------------------------------------------------------------------------------- /09-A-Web-Server/modules/tls/outputs.tf: -------------------------------------------------------------------------------- 1 | output "private_key" { 2 | value = tls_private_key.mykey.private_key_pem 3 | sensitive = true 4 | } 5 | 6 | output "public_key_out" { 7 | value = tls_private_key.mykey.public_key_openssh 8 | } 9 | -------------------------------------------------------------------------------- /08-Modules/modules/tls/variables.tf: -------------------------------------------------------------------------------- 1 | variable "algorithm" { 2 | type = string 3 | description = "The algorithm used" 4 | } 5 | 6 | variable "rsa_bits" { 7 | type = number 8 | description = "The strength of the algorithm in bits" 9 | } -------------------------------------------------------------------------------- /07-Another-Provider/output.tf: -------------------------------------------------------------------------------- 1 | output "public_ip" { 2 | value = aws_instance.webserver.public_ip 3 | description = "EC2 Public IP" 4 | } 5 | 6 | output "private_key" { 7 | value = tls_private_key.mykey.private_key_pem 8 | sensitive = true 9 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | A repo to teach Terraform for beginners. 3 | 4 | You can watch the [accompanied video on YouTube.](https://www.youtube.com/watch?v=HbG3zVBVYvM) along with a [written tutorial on my blog post.](https://tekanaid.com/posts/terraform-for-beginners-course-and-training) 5 | 6 | -------------------------------------------------------------------------------- /05-AWS-Setup-and-Outputs/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "The AWS region" 4 | default = "us-east-1" 5 | } 6 | 7 | variable "my_instance_type" { 8 | type = string 9 | description = "EC2 instance type" 10 | default = "t2.micro" 11 | } -------------------------------------------------------------------------------- /08-Modules/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "The AWS region" 4 | default = "us-east-1" 5 | } 6 | 7 | variable "my_aws_key" { 8 | type = string 9 | description = "AWS key to SSH into EC2 instances" 10 | default = "mykey.pem" 11 | } 12 | 13 | variable "my_instance_type" { 14 | type = string 15 | description = "EC2 instance type" 16 | default = "t2.micro" 17 | } -------------------------------------------------------------------------------- /06-Data-Blocks/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "The AWS region" 4 | default = "us-east-1" 5 | } 6 | 7 | variable "my_aws_key" { 8 | type = string 9 | description = "AWS key to SSH into EC2 instances" 10 | default = "mykey.pem" 11 | } 12 | 13 | variable "my_instance_type" { 14 | type = string 15 | description = "EC2 instance type" 16 | default = "t2.micro" 17 | } -------------------------------------------------------------------------------- /09-A-Web-Server/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "The AWS region" 4 | default = "us-east-1" 5 | } 6 | 7 | variable "my_aws_key" { 8 | type = string 9 | description = "AWS key to SSH into EC2 instances" 10 | default = "mykey.pem" 11 | } 12 | 13 | variable "my_instance_type" { 14 | type = string 15 | description = "EC2 instance type" 16 | default = "t2.micro" 17 | } -------------------------------------------------------------------------------- /07-Another-Provider/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "The AWS region" 4 | default = "us-east-1" 5 | } 6 | 7 | variable "my_aws_key" { 8 | type = string 9 | description = "AWS key to SSH into EC2 instances" 10 | default = "mykey.pem" 11 | } 12 | 13 | variable "my_instance_type" { 14 | type = string 15 | description = "EC2 instance type" 16 | default = "t2.micro" 17 | } -------------------------------------------------------------------------------- /05-AWS-Setup-and-Outputs/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "4.19.0" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.region 12 | } 13 | 14 | resource "aws_instance" "webserver" { 15 | ami = "ami-08d4ac5b634553e16" 16 | instance_type = var.my_instance_type 17 | } 18 | 19 | output "public_ip" { 20 | value = aws_instance.webserver.public_ip 21 | description = "EC2 Public IP" 22 | } 23 | -------------------------------------------------------------------------------- /02-Lock-and-State-Files/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | docker = { 4 | source = "kreuzwerker/docker" 5 | version = "3.0.1" 6 | } 7 | } 8 | } 9 | 10 | provider "docker" {} 11 | 12 | resource "docker_image" "nginx_image" { 13 | name = "nginx:1.23.3" 14 | } 15 | 16 | resource "docker_container" "nginx_container" { 17 | name = "web-server" 18 | image = docker_image.nginx_image.image_id 19 | ports { 20 | internal = 80 21 | external = 8080 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/universal:2-linux 2 | ARG TERRAFORM_VERSION=1.3.7 3 | USER root 4 | # Install Terraform and VAULT 5 | RUN wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ 6 | unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ 7 | mv terraform /usr/bin && \ 8 | rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \ 9 | # Install AWS 10 | pip install --upgrade pip && \ 11 | pip install --upgrade awscli 12 | USER codespace 13 | -------------------------------------------------------------------------------- /04-Variables-Continued/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | docker = { 4 | source = "kreuzwerker/docker" 5 | version = "3.0.1" 6 | } 7 | } 8 | } 9 | 10 | provider "docker" {} 11 | 12 | resource "docker_image" "nginx_image" { 13 | name = "${var.image_name}:${var.image_tag}" 14 | } 15 | 16 | resource "docker_container" "nginx_container" { 17 | name = "web-server" 18 | image = docker_image.nginx_image.image_id 19 | ports { 20 | internal = var.internal_port 21 | external = var.external_port 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /04-Variables-Continued/variables.tf: -------------------------------------------------------------------------------- 1 | variable "image_name" { 2 | type = string 3 | description = "The name of the Docker image" 4 | } 5 | 6 | variable "image_tag" { 7 | type = string 8 | description = "The tag of the Docker image" 9 | } 10 | 11 | variable "internal_port" { 12 | type = number 13 | description = "The internal port number for the container" 14 | default = 80 15 | } 16 | 17 | variable "external_port" { 18 | type = number 19 | description = "The external port number for the container" 20 | default = 8080 21 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | 11 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 12 | # .tfvars files are managed as part of configuration and so should be included in 13 | # version control. 14 | # 15 | # example.tfvars 16 | 17 | # Ignore override files as they are usually used to override resources locally and so 18 | # are not checked in 19 | override.tf 20 | override.tf.json 21 | *_override.tf 22 | *_override.tf.json 23 | 24 | # Include override files you do wish to add to version control using negated pattern 25 | # 26 | # !example_override.tf 27 | 28 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 29 | # example: *tfplan* 30 | *.pem 31 | -------------------------------------------------------------------------------- /06-Data-Blocks/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "4.19.0" 6 | } 7 | tls = { 8 | source = "hashicorp/tls" 9 | version = "4.0.0" 10 | } 11 | } 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | } 17 | 18 | data "aws_ami" "ubuntu" { 19 | most_recent = true 20 | 21 | filter { 22 | name = "name" 23 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 24 | } 25 | 26 | filter { 27 | name = "virtualization-type" 28 | values = ["hvm"] 29 | } 30 | 31 | owners = ["099720109477"] # Canonical https://ubuntu.com/server/docs/cloud-images/amazon-ec2 32 | } 33 | 34 | resource "aws_instance" "webserver" { 35 | ami = data.aws_ami.ubuntu.id 36 | instance_type = var.my_instance_type 37 | } -------------------------------------------------------------------------------- /04-Variables-Continued/guide.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | In this lab we learn about Variables. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the `04-Variables-Continued` directory: 8 | 9 | ```bash 10 | cd 04-Variables-Continued 11 | ``` 12 | 13 | 2. Initialize and Terraform Apply 14 | 15 | ```bash 16 | terraform init 17 | terraform apply --auto-approve 18 | ``` 19 | 20 | 3. Examine the `variables.tf` file 21 | 22 | Notice that we just moved the 4 variable blocks here. Terraform doesn't care how many `.tf` files you have, it will merge all of these files that live in the same directory. 23 | 24 | This `variables.tf` file is used to declare our variables with reasonable defaults if applicable. 25 | 26 | 4. Examine the `terraform.tfvars` file 27 | 28 | This file is used to assign values to our variables and will override the default values assigned in the `variables.tf` file. 29 | 30 | 5. Now go ahead and destroy the environment. 31 | 32 | ```bash 33 | terraform destroy --auto-approve 34 | ``` 35 | 36 | > Congratulations you have finished this lab! 37 | 38 | -------------------------------------------------------------------------------- /03-Variables/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | docker = { 4 | source = "kreuzwerker/docker" 5 | version = "3.0.1" 6 | } 7 | } 8 | } 9 | 10 | provider "docker" {} 11 | 12 | resource "docker_image" "nginx_image" { 13 | name = "${var.image_name}:${var.image_tag}" 14 | } 15 | 16 | resource "docker_container" "nginx_container" { 17 | name = "web-server" 18 | image = docker_image.nginx_image.image_id 19 | ports { 20 | internal = var.internal_port 21 | external = var.external_port 22 | } 23 | } 24 | 25 | variable "image_name" { 26 | type = string 27 | description = "The name of the Docker image" 28 | default = "nginx" 29 | } 30 | 31 | variable "image_tag" { 32 | type = string 33 | description = "The tag of the Docker image" 34 | default = "1.23.3" 35 | } 36 | 37 | variable "internal_port" { 38 | type = number 39 | description = "The internal port number for the container" 40 | } 41 | 42 | variable "external_port" { 43 | type = number 44 | description = "The external port number for the container" 45 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Sam 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 | -------------------------------------------------------------------------------- /08-Modules/modules/tls/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 TeKanAid Solutions Inc. 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. -------------------------------------------------------------------------------- /09-A-Web-Server/modules/tls/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 TeKanAid Solutions Inc. 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. -------------------------------------------------------------------------------- /09-A-Web-Server/install_libraries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Installing tree, jq, and nginx" 3 | apt -y update 4 | apt install tree jq -y 5 | apt install nginx -y 6 | systemctl enable nginx 7 | sudo cat << EOF > /tmp/index.html 8 | 9 | 10 | Schoolapp 11 | 12 | 13 |

Welcome to the School App

14 |

15 | School App 16 |

17 |

18 | Photo by AltumCode on Unsplash 19 |

20 |

This message confirms that your App is working with an NGINX web server. Great work!

21 | 22 | 23 | EOF 24 | sudo mv /tmp/index.html /var/www/html/index.html 25 | sudo nginx -s reload 26 | -------------------------------------------------------------------------------- /05-AWS-Setup-and-Outputs/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.19.0" 6 | constraints = "4.19.0" 7 | hashes = [ 8 | "h1:4vAZv9/3q5z78CV+YAumfuaoSNSNwAXDEhI/XnGVM5E=", 9 | "zh:22820bfa0065f583298015367f8dc015dffa5b19b76dbd78ecf5da8d7d599573", 10 | "zh:31a5c5fade4bd30dbc2b15f448cebb9ed527793c607e8687d3b2101bcf2c4471", 11 | "zh:37c9e469e51aa835a5542510561397541de08b62fc15292588382932624fcf88", 12 | "zh:398bfe1ba7428ef03293c6618067ddd8c0aaae8bbe764177ae951259228af724", 13 | "zh:4610f5a93ef956103d719ae73872a52ecd6cb321452c26a879896348bc27eed9", 14 | "zh:4a0d570dc5f01f41538b4eb70086a00dfb25c5d00fd27c950ac209d3609486f6", 15 | "zh:4fb65ce84801f82a3beb4e2cb72c5d52ca04d4717ed3890b206da346f02d5def", 16 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 17 | "zh:9bb3919bd6d94fb22025540f0c1db5eceec8927bd71b8fbdcd295609c999065f", 18 | "zh:ce2623a13f74677cdb948607e456ce00407c57333b8310d5c9d053fc3defbc78", 19 | "zh:e0d57e8784e6ccfa96fdd07ae1ddcc947be242bc11e7a5dd16b520b4204e0d09", 20 | "zh:f988b7c37e95a5b3a493a6b9dcc5ed270136f97d5c0effa84a51940f71626c12", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /05-AWS-Setup-and-Outputs/guide.md: -------------------------------------------------------------------------------- 1 | # AWS Setup and Outputs 2 | 3 | In this lab we learn about AWS with Terraform and Terraform Outputs. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the `05-AWS-Setup-and-Outputs` directory: 8 | 9 | ```bash 10 | cd 05-AWS-Setup-and-Outputs 11 | ``` 12 | 13 | 2. Create environment variables to connect to AWS 14 | 15 | ```bash 16 | export AWS_ACCESS_KEY_ID=your_value 17 | export AWS_SECRET_ACCESS_KEY=your_value 18 | ``` 19 | 20 | 3. Initialize Terraform Plan, and Terraform Apply 21 | 22 | ```bash 23 | terraform init 24 | terraform plan 25 | terraform apply --auto-approve 26 | ``` 27 | 28 | Notice the output of Terraform gives us the public IP of the EC2 instance. 29 | 30 | 4. Examine the `main.tf` file 31 | 32 | We are now using the `aws` provider. Also the `output` block is referencing the `public_ip` attribute of the EC2 instance likie this: `aws_instance.webserver.public_ip` 33 | 34 | 5. Examine the `variables.tf` file 35 | 36 | We have 2 variables defined with defaults: 37 | - region 38 | - my_instance_type 39 | 40 | 6. Check the EC2 instance that got created in the AWS console 41 | 42 | 7. Now go ahead and destroy the environment. 43 | 44 | ```bash 45 | terraform destroy --auto-approve 46 | ``` 47 | 48 | > Congratulations you have finished this lab! 49 | 50 | -------------------------------------------------------------------------------- /03-Variables/guide.md: -------------------------------------------------------------------------------- 1 | # Variables 2 | 3 | In this lab we learn about Variables. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the `03-Variables` directory: 8 | 9 | ```bash 10 | cd 03-Variables 11 | ``` 12 | 13 | 2. Initialize and Terraform Apply 14 | 15 | ```bash 16 | terraform init 17 | terraform apply --auto-approve 18 | ``` 19 | 20 | You will get prompted for the External and Internal ports. 21 | Use 8080 for the external port and 80 for the internal port. 22 | 23 | 3. Examine the `main.tf` file 24 | 25 | Notice how we declare variables using the `variables` block and reference variables using var.. 26 | 27 | This syntax: `"${var.image_name}:${var.image_tag}"` is called string interpolation as you can include variables inside strings. 28 | 29 | 4. Supply the variables via the CLI 30 | 31 | Now rerun `terraform apply` but this time use the following command: 32 | 33 | ```bash 34 | terraform apply -var 'external_port=8080' -var 'internal_port=80' 35 | ``` 36 | 37 | 5. Now go ahead and destroy the environment using the `-destroy` flag which is another way of destroying the environment that is often used in CI/CD pipelines. 38 | 39 | ```bash 40 | terraform apply -destroy -var 'external_port=8080' -var 'internal_port=80' 41 | ``` 42 | 43 | > Congratulations you have finished this lab! 44 | 45 | -------------------------------------------------------------------------------- /03-Variables/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/kreuzwerker/docker" { 5 | version = "3.0.1" 6 | constraints = "3.0.1" 7 | hashes = [ 8 | "h1:X2wZHQoG54NmtojeFcX0PSJPelaIejQRqyyI2h+LjWg=", 9 | "zh:02f60126ca16b344092df3c315296bf1a216c3b2a68eddb3c89fdfa5ea826118", 10 | "zh:0d2ee9624a54dbc10538b0c4e296348641b9bfba1354b3f872e43f7ec69a75f2", 11 | "zh:473d7427da8c9efc231266abc7fdc27fca5f9ee0bdfcdb9914f0a2886e3e23b8", 12 | "zh:5f0189bcd0c944c001098cb17a23efa79df8f0eec8644a64fe0e4200983ba5b7", 13 | "zh:6200319c41d6baad3f46701a4028412f8ae2496e29fc4fef9584cc71da5fbbe6", 14 | "zh:650be621f2216b1240f148eae8fcf80ec57c35925e2b212db7c23a70b9e67e06", 15 | "zh:72fcfa6207251105066a34f0ec6d27ecc658b565e84fa946da376dd1afadd265", 16 | "zh:92fc352a2090d3d380c7c8e8bbdf6f99d93a0182701056bb1d2dbfd5049e8ca6", 17 | "zh:a7e2ef666c2a7eb5661b06cfbd7635cb9543524e7bf6a3851dcf6eacc9950cc4", 18 | "zh:a8604595e61e8919c51a8656800c8c64557f9a2bc00309315895b380f2e9be19", 19 | "zh:caf65603a84b749d8f3af2ee47b66f7e21d481f981e2e1d1d59838751c5e3be4", 20 | "zh:dad40c4e57da284e7f57b5c0cc9dfac3cb27b01d2f2436fbe3464f0a2111b262", 21 | "zh:dc1b173dbcba9d74879b16f36f6d9e97ef62fbd6fca8db79ec4fe4ec69c0e2f3", 22 | "zh:e506d04677383b6d62bd69d42dc9005e27a45ccc2efc6e0de607e1f8445981d2", 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /01-Basic-Commands/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/kreuzwerker/docker" { 5 | version = "3.0.1" 6 | constraints = "3.0.1" 7 | hashes = [ 8 | "h1:X2wZHQoG54NmtojeFcX0PSJPelaIejQRqyyI2h+LjWg=", 9 | "zh:02f60126ca16b344092df3c315296bf1a216c3b2a68eddb3c89fdfa5ea826118", 10 | "zh:0d2ee9624a54dbc10538b0c4e296348641b9bfba1354b3f872e43f7ec69a75f2", 11 | "zh:473d7427da8c9efc231266abc7fdc27fca5f9ee0bdfcdb9914f0a2886e3e23b8", 12 | "zh:5f0189bcd0c944c001098cb17a23efa79df8f0eec8644a64fe0e4200983ba5b7", 13 | "zh:6200319c41d6baad3f46701a4028412f8ae2496e29fc4fef9584cc71da5fbbe6", 14 | "zh:650be621f2216b1240f148eae8fcf80ec57c35925e2b212db7c23a70b9e67e06", 15 | "zh:72fcfa6207251105066a34f0ec6d27ecc658b565e84fa946da376dd1afadd265", 16 | "zh:92fc352a2090d3d380c7c8e8bbdf6f99d93a0182701056bb1d2dbfd5049e8ca6", 17 | "zh:a7e2ef666c2a7eb5661b06cfbd7635cb9543524e7bf6a3851dcf6eacc9950cc4", 18 | "zh:a8604595e61e8919c51a8656800c8c64557f9a2bc00309315895b380f2e9be19", 19 | "zh:caf65603a84b749d8f3af2ee47b66f7e21d481f981e2e1d1d59838751c5e3be4", 20 | "zh:dad40c4e57da284e7f57b5c0cc9dfac3cb27b01d2f2436fbe3464f0a2111b262", 21 | "zh:dc1b173dbcba9d74879b16f36f6d9e97ef62fbd6fca8db79ec4fe4ec69c0e2f3", 22 | "zh:e506d04677383b6d62bd69d42dc9005e27a45ccc2efc6e0de607e1f8445981d2", 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /02-Lock-and-State-Files/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/kreuzwerker/docker" { 5 | version = "3.0.1" 6 | constraints = "3.0.1" 7 | hashes = [ 8 | "h1:X2wZHQoG54NmtojeFcX0PSJPelaIejQRqyyI2h+LjWg=", 9 | "zh:02f60126ca16b344092df3c315296bf1a216c3b2a68eddb3c89fdfa5ea826118", 10 | "zh:0d2ee9624a54dbc10538b0c4e296348641b9bfba1354b3f872e43f7ec69a75f2", 11 | "zh:473d7427da8c9efc231266abc7fdc27fca5f9ee0bdfcdb9914f0a2886e3e23b8", 12 | "zh:5f0189bcd0c944c001098cb17a23efa79df8f0eec8644a64fe0e4200983ba5b7", 13 | "zh:6200319c41d6baad3f46701a4028412f8ae2496e29fc4fef9584cc71da5fbbe6", 14 | "zh:650be621f2216b1240f148eae8fcf80ec57c35925e2b212db7c23a70b9e67e06", 15 | "zh:72fcfa6207251105066a34f0ec6d27ecc658b565e84fa946da376dd1afadd265", 16 | "zh:92fc352a2090d3d380c7c8e8bbdf6f99d93a0182701056bb1d2dbfd5049e8ca6", 17 | "zh:a7e2ef666c2a7eb5661b06cfbd7635cb9543524e7bf6a3851dcf6eacc9950cc4", 18 | "zh:a8604595e61e8919c51a8656800c8c64557f9a2bc00309315895b380f2e9be19", 19 | "zh:caf65603a84b749d8f3af2ee47b66f7e21d481f981e2e1d1d59838751c5e3be4", 20 | "zh:dad40c4e57da284e7f57b5c0cc9dfac3cb27b01d2f2436fbe3464f0a2111b262", 21 | "zh:dc1b173dbcba9d74879b16f36f6d9e97ef62fbd6fca8db79ec4fe4ec69c0e2f3", 22 | "zh:e506d04677383b6d62bd69d42dc9005e27a45ccc2efc6e0de607e1f8445981d2", 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /04-Variables-Continued/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/kreuzwerker/docker" { 5 | version = "3.0.1" 6 | constraints = "3.0.1" 7 | hashes = [ 8 | "h1:X2wZHQoG54NmtojeFcX0PSJPelaIejQRqyyI2h+LjWg=", 9 | "zh:02f60126ca16b344092df3c315296bf1a216c3b2a68eddb3c89fdfa5ea826118", 10 | "zh:0d2ee9624a54dbc10538b0c4e296348641b9bfba1354b3f872e43f7ec69a75f2", 11 | "zh:473d7427da8c9efc231266abc7fdc27fca5f9ee0bdfcdb9914f0a2886e3e23b8", 12 | "zh:5f0189bcd0c944c001098cb17a23efa79df8f0eec8644a64fe0e4200983ba5b7", 13 | "zh:6200319c41d6baad3f46701a4028412f8ae2496e29fc4fef9584cc71da5fbbe6", 14 | "zh:650be621f2216b1240f148eae8fcf80ec57c35925e2b212db7c23a70b9e67e06", 15 | "zh:72fcfa6207251105066a34f0ec6d27ecc658b565e84fa946da376dd1afadd265", 16 | "zh:92fc352a2090d3d380c7c8e8bbdf6f99d93a0182701056bb1d2dbfd5049e8ca6", 17 | "zh:a7e2ef666c2a7eb5661b06cfbd7635cb9543524e7bf6a3851dcf6eacc9950cc4", 18 | "zh:a8604595e61e8919c51a8656800c8c64557f9a2bc00309315895b380f2e9be19", 19 | "zh:caf65603a84b749d8f3af2ee47b66f7e21d481f981e2e1d1d59838751c5e3be4", 20 | "zh:dad40c4e57da284e7f57b5c0cc9dfac3cb27b01d2f2436fbe3464f0a2111b262", 21 | "zh:dc1b173dbcba9d74879b16f36f6d9e97ef62fbd6fca8db79ec4fe4ec69c0e2f3", 22 | "zh:e506d04677383b6d62bd69d42dc9005e27a45ccc2efc6e0de607e1f8445981d2", 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /06-Data-Blocks/guide.md: -------------------------------------------------------------------------------- 1 | # Data Blocks 2 | 3 | In this lab we learn about Data Blocks. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the `06-Data-Blocks` directory: 8 | 9 | ```bash 10 | cd 06-Data-Blocks 11 | ``` 12 | 13 | 2. Make sure you still have your environment variables otherwise export them as before: 14 | 15 | ```bash 16 | export AWS_ACCESS_KEY_ID=your_value 17 | export AWS_SECRET_ACCESS_KEY=your_value 18 | ``` 19 | 20 | 3. Initialize Terraform Plan, and Terraform Apply 21 | 22 | ```bash 23 | terraform init 24 | terraform plan 25 | terraform apply --auto-approve 26 | ``` 27 | 28 | 4. Notice the `data` block in `main.tf` 29 | 30 | In `main.tf` we have a new block which is the `data` block. This block is used to get information as opposed to the resource block that is used to create resources. Here we're using the `data` block to retrieve the `id` of the Ubuntu 20.04 AMI. 31 | 32 | We're then referencing this `id` using the following format: `data.aws_ami.ubuntu.id` 33 | 34 | ```hcl 35 | resource "aws_instance" "webserver" { 36 | ami = data.aws_ami.ubuntu.id 37 | ``` 38 | 39 | 5. Notice the output is moved to `output.tf` 40 | 41 | It's a common practice to move the `output` blocks to their own file. At minimum most Terraform configurations have a `main.tf` file, a `variables.tf` file, and an `output.tf` file. 42 | 43 | 6. Now go ahead and destroy the environment. 44 | 45 | ```bash 46 | terraform destroy --auto-approve 47 | ``` 48 | 49 | > Congratulations you have finished this lab! 50 | 51 | -------------------------------------------------------------------------------- /01-Basic-Commands/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { # The `required_providers` block specifies the provider(s) needed for this Terraform configuration. 3 | docker = { # Here, we are requiring the `docker` provider. 4 | source = "kreuzwerker/docker" # This line specifies the source of the provider. In this case, it is the `kreuzwerker/docker` provider. 5 | version = "3.0.1" # This line specifies the version of the provider that we need. 6 | } 7 | } 8 | } 9 | 10 | provider "docker" {} # This `provider` block specifies that we want to use the `docker` provider. 11 | 12 | resource "docker_image" "nginx_image" { # This `resource` block creates a Docker image named `nginx_image` using the `nginx:1.23.3` image. 13 | name = "nginx:1.23.3" 14 | } 15 | 16 | resource "docker_container" "nginx_container" { # This `resource` block creates a Docker container named `nginx_container` based on the `nginx_image` image. 17 | name = "web-server" # This line specifies the name of the container. 18 | image = docker_image.nginx_image.image_id # This line references the `nginx_image` resource created earlier to get the image ID. 19 | ports { # This block specifies the ports to be exposed by the container. 20 | internal = 80 # The container's internal port 80 will be exposed. 21 | external = 8080 # The container's internal port 80 will be accessible on the host's port 8080. 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /08-Modules/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "4.19.0" 6 | } 7 | tls = { 8 | source = "hashicorp/tls" 9 | version = "4.0.0" 10 | } 11 | } 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | } 17 | 18 | data "aws_ami" "ubuntu" { 19 | most_recent = true 20 | 21 | filter { 22 | name = "name" 23 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 24 | } 25 | 26 | filter { 27 | name = "virtualization-type" 28 | values = ["hvm"] 29 | } 30 | 31 | owners = ["099720109477"] # Canonical https://ubuntu.com/server/docs/cloud-images/amazon-ec2 32 | } 33 | 34 | resource "aws_key_pair" "mykey" { 35 | key_name = var.my_aws_key 36 | public_key = module.tls.public_key_out 37 | } 38 | 39 | resource "aws_instance" "webserver" { 40 | ami = data.aws_ami.ubuntu.id 41 | instance_type = var.my_instance_type 42 | key_name = aws_key_pair.mykey.key_name 43 | vpc_security_group_ids = [aws_security_group.security_group1.id] 44 | } 45 | 46 | resource "aws_security_group" "security_group1" { 47 | ingress { 48 | cidr_blocks = ["0.0.0.0/0"] 49 | description = "SSH Ingress" 50 | from_port = 22 51 | to_port = 22 52 | protocol = "tcp" 53 | } 54 | 55 | egress { 56 | from_port = 0 57 | to_port = 0 58 | protocol = "-1" 59 | cidr_blocks = ["0.0.0.0/0"] 60 | } 61 | } 62 | 63 | module "tls" { 64 | source = "./modules/tls" 65 | algorithm = "RSA" 66 | rsa_bits = 4096 67 | } -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile 3 | { 4 | "name": "Existing Dockerfile", 5 | // "image": "samgabrail/terraform101", 6 | "build": { 7 | // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. 8 | "dockerfile": "Dockerfile" 9 | }, 10 | 11 | // Features to add to the dev container. More info: https://containers.dev/features. 12 | // "features": {}, 13 | 14 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 15 | // "forwardPorts": [], 16 | 17 | // Uncomment the next line to run commands after the container is created - for example installing curl. 18 | // "postCreateCommand": "apt-get update && apt-get install -y curl", 19 | 20 | // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker. 21 | // "mounts": [ 22 | // { 23 | // "source": "/var/run/docker.sock", 24 | // "target": "/var/run/docker-host.sock", 25 | // "type": "bind" 26 | // } 27 | // ] 28 | 29 | // Configure tool-specific properties. 30 | "customizations": { 31 | // Configure properties specific to VS Code. 32 | "vscode": { 33 | // Add the IDs of extensions you want installed when the container is created. 34 | "extensions": [ 35 | "hashicorp.terraform" 36 | ] 37 | } 38 | } 39 | 40 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 41 | // "remoteUser": "root" 42 | } 43 | -------------------------------------------------------------------------------- /09-A-Web-Server/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "4.19.0" 6 | } 7 | tls = { 8 | source = "hashicorp/tls" 9 | version = "4.0.0" 10 | } 11 | } 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | } 17 | 18 | data "aws_ami" "ubuntu" { 19 | most_recent = true 20 | 21 | filter { 22 | name = "name" 23 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 24 | } 25 | 26 | filter { 27 | name = "virtualization-type" 28 | values = ["hvm"] 29 | } 30 | 31 | owners = ["099720109477"] # Canonical https://ubuntu.com/server/docs/cloud-images/amazon-ec2 32 | } 33 | 34 | resource "aws_key_pair" "mykey" { 35 | key_name = var.my_aws_key 36 | public_key = module.tls.public_key_out 37 | } 38 | 39 | resource "aws_instance" "webserver" { 40 | ami = data.aws_ami.ubuntu.id 41 | instance_type = var.my_instance_type 42 | key_name = aws_key_pair.mykey.key_name 43 | vpc_security_group_ids = [aws_security_group.security_group1.id] 44 | user_data = file("install_libraries.sh") 45 | user_data_replace_on_change = true 46 | } 47 | 48 | resource "aws_security_group" "security_group1" { 49 | ingress { 50 | cidr_blocks = ["0.0.0.0/0"] 51 | description = "SSH Ingress" 52 | from_port = 22 53 | to_port = 22 54 | protocol = "tcp" 55 | } 56 | ingress { 57 | cidr_blocks = ["0.0.0.0/0"] 58 | description = "HTTP Ingress" 59 | from_port = 80 60 | to_port = 80 61 | protocol = "tcp" 62 | 63 | } 64 | 65 | egress { 66 | from_port = 0 67 | to_port = 0 68 | protocol = "-1" 69 | cidr_blocks = ["0.0.0.0/0"] 70 | } 71 | } 72 | 73 | module "tls" { 74 | source = "./modules/tls" 75 | } 76 | -------------------------------------------------------------------------------- /07-Another-Provider/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "4.19.0" 6 | } 7 | tls = { 8 | source = "hashicorp/tls" 9 | version = "4.0.0" 10 | } 11 | } 12 | } 13 | 14 | provider "aws" { 15 | region = var.region 16 | } 17 | 18 | data "aws_ami" "ubuntu" { 19 | most_recent = true 20 | 21 | filter { 22 | name = "name" 23 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 24 | } 25 | 26 | filter { 27 | name = "virtualization-type" 28 | values = ["hvm"] 29 | } 30 | 31 | owners = ["099720109477"] # Canonical https://ubuntu.com/server/docs/cloud-images/amazon-ec2 32 | } 33 | 34 | resource "tls_private_key" "mykey" { 35 | algorithm = "RSA" 36 | rsa_bits = 4096 37 | } 38 | 39 | resource "aws_key_pair" "mykey" { 40 | key_name = "mykey.pem" 41 | public_key = tls_private_key.mykey.public_key_openssh 42 | } 43 | 44 | resource "aws_instance" "webserver" { 45 | ami = data.aws_ami.ubuntu.id 46 | instance_type = var.my_instance_type 47 | key_name = aws_key_pair.mykey.key_name 48 | vpc_security_group_ids = [aws_security_group.security_group1.id] 49 | } 50 | 51 | resource "aws_security_group" "security_group1" { 52 | ingress { 53 | cidr_blocks = ["0.0.0.0/0"] 54 | description = "SSH Ingress" 55 | from_port = 22 56 | to_port = 22 57 | protocol = "tcp" 58 | } 59 | 60 | egress { 61 | from_port = 0 62 | to_port = 0 63 | protocol = "-1" 64 | cidr_blocks = ["0.0.0.0/0"] 65 | } 66 | } 67 | 68 | 69 | # Getting the output from private key is via this command below: 70 | 71 | # terraform output -raw private_key 72 | 73 | # To ssh into the machine: 74 | # terraform output -raw private_key > myKey.pem 75 | # sudo chmod 400 myKey.pem 76 | # ssh -i myKey.pem ubuntu@$(terraform output -raw public_ip) 77 | -------------------------------------------------------------------------------- /07-Another-Provider/guide.md: -------------------------------------------------------------------------------- 1 | # Another Provider 2 | 3 | So far we've created an EC2 instance but haven't ssh'd into it. In this lab we will use the TLS provider in addition to the AWS provider to create a public/private key pair to be used to ssh into the EC2 instance. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the `07-Another-Provider` directory: 8 | 9 | ```bash 10 | cd 07-Another-Provider 11 | ``` 12 | 13 | 2. Make sure you still have your environment variables otherwise export them as before: 14 | 15 | ```bash 16 | export AWS_ACCESS_KEY_ID=your_value 17 | export AWS_SECRET_ACCESS_KEY=your_value 18 | ``` 19 | 20 | 3. Initialize Terraform Plan, and Terraform Apply 21 | 22 | ```bash 23 | terraform init 24 | terraform plan 25 | terraform apply --auto-approve 26 | ``` 27 | 28 | Notice the `private_key` in the output is marked as sensitive because we specified that in the `output.tf` file. 29 | 30 | 4. Examine `main.tf` 31 | 32 | In `main.tf` we now have another provider `tls`. We use the resource `tls_private_key` to create a private/public key pair. Then we create an `aws_key_pair` resource in AWS referencing the public key with: `tls_private_key.mykey.public_key_openssh` 33 | 34 | Also, notice that we created a security group to allow ssh inbound to the instance. This is done with the aws_security_group resource. You then reference it in the aws_instance resource like this: 35 | 36 | ```hcl 37 | vpc_security_group_ids = [aws_security_group.security_group1.id] 38 | ``` 39 | 40 | The square brackets are used because vpc_security_group_ids requires a list of security group ids. 41 | 42 | 5. Examine the state file 43 | 44 | Notice that the private key shows up in the state file! This is a very important concept to understant so you would secure your statefile. 45 | 46 | 6. SSH into the EC2 instance 47 | 48 | We can get the value of the private key and store it in a file on disk then we can change the permissions on the file and finally ssh. 49 | 50 | ```bash 51 | terraform output -raw private_key > myKey.pem 52 | sudo chmod 400 myKey.pem 53 | ssh -i myKey.pem ubuntu@$(terraform output -raw public_ip) 54 | ``` 55 | 56 | 7. Now go ahead and destroy the environment. 57 | 58 | ```bash 59 | terraform destroy --auto-approve 60 | ``` 61 | 62 | > Congratulations you have finished this lab! 63 | 64 | -------------------------------------------------------------------------------- /07-Another-Provider/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.19.0" 6 | constraints = "4.19.0" 7 | hashes = [ 8 | "h1:4vAZv9/3q5z78CV+YAumfuaoSNSNwAXDEhI/XnGVM5E=", 9 | "zh:22820bfa0065f583298015367f8dc015dffa5b19b76dbd78ecf5da8d7d599573", 10 | "zh:31a5c5fade4bd30dbc2b15f448cebb9ed527793c607e8687d3b2101bcf2c4471", 11 | "zh:37c9e469e51aa835a5542510561397541de08b62fc15292588382932624fcf88", 12 | "zh:398bfe1ba7428ef03293c6618067ddd8c0aaae8bbe764177ae951259228af724", 13 | "zh:4610f5a93ef956103d719ae73872a52ecd6cb321452c26a879896348bc27eed9", 14 | "zh:4a0d570dc5f01f41538b4eb70086a00dfb25c5d00fd27c950ac209d3609486f6", 15 | "zh:4fb65ce84801f82a3beb4e2cb72c5d52ca04d4717ed3890b206da346f02d5def", 16 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 17 | "zh:9bb3919bd6d94fb22025540f0c1db5eceec8927bd71b8fbdcd295609c999065f", 18 | "zh:ce2623a13f74677cdb948607e456ce00407c57333b8310d5c9d053fc3defbc78", 19 | "zh:e0d57e8784e6ccfa96fdd07ae1ddcc947be242bc11e7a5dd16b520b4204e0d09", 20 | "zh:f988b7c37e95a5b3a493a6b9dcc5ed270136f97d5c0effa84a51940f71626c12", 21 | ] 22 | } 23 | 24 | provider "registry.terraform.io/hashicorp/tls" { 25 | version = "4.0.0" 26 | constraints = "4.0.0" 27 | hashes = [ 28 | "h1:kOrvJIaved9HipxnVcipaOv9zILQ+mU96f5XrNqXiRA=", 29 | "zh:0e274781301a67dc18b40b8544714f338759ce0170e95e79430b9a05b447aece", 30 | "zh:1dbbfae8a39ad6109036fa72dff246eb9b8a02ef5b34f9cdc8b4120c9b2de81e", 31 | "zh:40ccf9da97268cd8ddb49ef0349ddd0e0b97dd127522035d05156803234634d4", 32 | "zh:437c030559b6352a49f6e6430415e0cfbd2668712c9155fa9b54dc2e6f8267eb", 33 | "zh:50c840c579270e649d42b4276bbfacf2d3b5beae1f0dcabf0b5f535b2ce7975c", 34 | "zh:6b5caf6172521bfd1beba067b4ce71793a85abb1b0f1d96a89cf54c5b5fd1223", 35 | "zh:98b68106837b3e8577f239a8562387e76d408ff49d08647103664e75bc193427", 36 | "zh:cc5d9db1c74a2f135563d8706b4003d9b0e344dc1d17417977438ffa15db5938", 37 | "zh:de39da56477abc52f0fff7837fbd10063f8508d9fcaf377d1e6d775d0390755a", 38 | "zh:f3c8509e41f4dee8b54c2c4c1b335279de044cb8bea363b5442ac368c6bdee15", 39 | "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", 40 | "zh:f7931dda4377c6548e5fc7afddab03230ac9b42380908932542dea7661e0f078", 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /09-A-Web-Server/guide.md: -------------------------------------------------------------------------------- 1 | # A Web Server 2 | 3 | The concept of modules is to re-use code by building bigger infrastructure constructs using modules as building blocks. We will move the TLS resource into a module. Typically you won't only put one resource into a module, but a collection of resources. We're only doing this to show a simple exmaple. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the `09-A-Web-Server` directory: 8 | 9 | ```bash 10 | cd 09-A-Web-Server 11 | ``` 12 | 13 | 2. Make sure you still have your environment variables otherwise export them as before: 14 | 15 | ```bash 16 | export AWS_ACCESS_KEY_ID=your_value 17 | export AWS_SECRET_ACCESS_KEY=your_value 18 | ``` 19 | 20 | 3. Initialize Terraform Plan, and Terraform Apply 21 | 22 | Notice how Terraform initializes our new module. 23 | 24 | ```bash 25 | terraform init 26 | terraform plan 27 | terraform apply --auto-approve 28 | ``` 29 | 30 | Now in a browser tab paste the public IP to see your new and shiny web server running. 31 | 32 | 4. Examine `main.tf` 33 | 34 | We've included two additional things: 35 | 36 | - Updated the security group to include port 80 to access our web server 37 | 38 | ```hcl 39 | ...truncated 40 | ingress { 41 | cidr_blocks = ["0.0.0.0/0"] 42 | description = "HTTP Ingress" 43 | from_port = 80 44 | to_port = 80 45 | protocol = "tcp" 46 | } 47 | ...truncated 48 | ``` 49 | 50 | - Added the user_data field 51 | 52 | Here the `user_data` field uses the `file()` function to call on the `install_libraries.sh` script. The `user_data_replace_on_change = true` property means that if Terraform senses a change in the content of `user_data` then it will rebuild the EC2 instance. 53 | 54 | ```hcl 55 | resource "aws_instance" "webserver" { 56 | ami = data.aws_ami.ubuntu.id 57 | instance_type = var.my_instance_type 58 | key_name = aws_key_pair.mykey.key_name 59 | vpc_security_group_ids = [aws_security_group.security_group1.id] 60 | user_data = file("install_libraries.sh") 61 | user_data_replace_on_change = true 62 | } 63 | ``` 64 | 65 | 5. Examine the `install_libraries.sh` script 66 | 67 | This is the file that the EC2 instance uses to configure our web server. We install an nginx webserver and create a simple `index.html` page. 68 | 69 | 6. Now go ahead and destroy the environment. 70 | 71 | ```bash 72 | terraform destroy --auto-approve 73 | ``` 74 | 75 | > Congratulations you have finished this lab! 76 | 77 | -------------------------------------------------------------------------------- /08-Modules/guide.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | The concept of modules is to re-use code by building bigger infrastructure constructs using modules as building blocks. We will move the TLS resource into a module. Typically you won't only put one resource into a module, but a collection of resources. We're only doing this to show a simple exmaple. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the `08-Modules` directory: 8 | 9 | ```bash 10 | cd 08-Modules 11 | ``` 12 | 13 | 2. Make sure you still have your environment variables otherwise export them as before: 14 | 15 | ```bash 16 | export AWS_ACCESS_KEY_ID=your_value 17 | export AWS_SECRET_ACCESS_KEY=your_value 18 | ``` 19 | 20 | 3. Initialize Terraform Plan, and Terraform Apply 21 | 22 | Notice how Terraform initializes our new module. 23 | 24 | ```bash 25 | terraform init 26 | terraform plan 27 | terraform apply --auto-approve 28 | ``` 29 | 30 | Notice that everything works the same as before. 31 | 32 | 4. Examine `main.tf` 33 | 34 | Now the directory where the `main.tf` file exists is considered the root module. So far in this tutorial we've been doing everything in the root module. 35 | 36 | From the root module we're calling our child `tls` module: 37 | 38 | ```hcl 39 | module "tls" { 40 | source = "./modules/tls" 41 | algorithm = "RSA" 42 | rsa_bits = 4096 43 | } 44 | ``` 45 | 46 | We need to specify the source path of the module along with the necessary input variables into the module. 47 | 48 | We can also use a module's output by referencing the module using the syntax `mocule.. as shown below: 49 | 50 | ```hcl 51 | resource "aws_key_pair" "mykey" { 52 | key_name = var.my_aws_key 53 | public_key = module.tls.public_key_out 54 | } 55 | ``` 56 | 57 | 5. Examine the `tls` module 58 | 59 | Now take a look at the `tls` directory under the `modules` directory. As a good practice, you should include at least 5 files in a module: 60 | - README.md 61 | - LICENSE 62 | - main.tf 63 | - variables.tf 64 | - outputs.tf 65 | 66 | Notice how there is nothing special about a module. It's the same Terraform code that we've learned so far. 67 | 68 | 6. Flow of Data 69 | 70 | Notice how the output gets passed from the tls module to the root module and out to the CLI. Check the output of the tls module: 71 | 72 | ```hcl 73 | output "private_key" { 74 | value = tls_private_key.mykey.private_key_pem 75 | sensitive = true 76 | } 77 | output "public_key_out" { 78 | value = tls_private_key.mykey.public_key_openssh 79 | } 80 | ``` 81 | 82 | Check the output of the root module: 83 | 84 | ```hcl 85 | output "public_ip" { 86 | value = aws_instance.webserver.public_ip 87 | description = "EC2 Public IP" 88 | } 89 | output "private_key" { 90 | value = module.tls.private_key 91 | sensitive = true 92 | } 93 | ``` 94 | 95 | 7. Now go ahead and destroy the environment. 96 | 97 | ```bash 98 | terraform destroy --auto-approve 99 | ``` 100 | 101 | > Congratulations you have finished this lab! 102 | 103 | -------------------------------------------------------------------------------- /02-Lock-and-State-Files/guide.md: -------------------------------------------------------------------------------- 1 | # Lock and State Files 2 | 3 | In this lab we learn about the Lock and State files. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the 02-Lock-and-State-Files directory: 8 | 9 | ```bash 10 | cd 02-Lock-and-State-Files 11 | ``` 12 | 13 | 2. Initialize and Terraform Apply 14 | 15 | ```bash 16 | terraform init 17 | terraform apply --auto-approve 18 | ``` 19 | 20 | 3. Examine the `.terraform.lock.hcl` file 21 | 22 | The `.terraform.lock.hcl` file is a generated file that Terraform creates when it installs provider dependencies for a Terraform configuration. It contains a list of all the provider dependencies, including their version constraints, that were installed for the Terraform configuration. 23 | 24 | The purpose of the `.terraform.lock.hcl` file is to ensure that the exact same versions of providers are installed each time the Terraform configuration is applied, regardless of the environment where it is applied. This helps to prevent unexpected changes or failures due to version differences in the provider dependencies. 25 | 26 | When Terraform applies a configuration, it reads the `.terraform.lock.hcl` file to determine which provider dependencies to use, and then installs them if necessary. If the `.terraform.lock.hcl` file is not present, Terraform will attempt to download and install the latest version of each provider dependency, which may result in changes or failures if the latest version is incompatible with the configuration. 27 | 28 | It is recommended to include the `.terraform.lock.hcl` file in version control along with the Terraform configuration to ensure that all team members and deployment environments are using the same provider versions. 29 | 30 | 4. Examine the `terraform.tfstate` file 31 | 32 | The `terraform.tfstate` file is a generated file that Terraform uses to keep track of the current state of the resources managed by a Terraform configuration. This file contains information about the resources that were created or modified, their current state, and any metadata that is associated with them. 33 | 34 | The purpose of the `terraform.tfstate` file is to enable Terraform to determine what changes need to be made to the resources when a configuration is applied. When Terraform applies a configuration, it reads the current state from the `terraform.tfstate` file, compares it to the desired state specified in the configuration, and then creates, modifies, or deletes resources as necessary to achieve the desired state. 35 | 36 | The `terraform.tfstate` file is an important part of the Terraform workflow because it enables Terraform to perform resource management and maintain state over time. Without the `terraform.tfstate` file, Terraform would not be able to determine which resources need to be created, modified, or deleted, and it would not be able to maintain state across multiple runs of the same configuration. 37 | 38 | It is important to store the `terraform.tfstate` file securely and consistently, since it contains sensitive information such as resource IDs, passwords, and access keys. Terraform supports several ways of storing the `terraform.tfstate` file, including local file storage, remote storage solutions like AWS S3 and Azure Blob Storage, and Terraform Cloud. 39 | 40 | 5. Destroy the environment and re-examine the state file to see what changed. Be very careful when using the `--auto-approve` flag with `terraform apply` or `terraform destroy`. This should not be used in production as it will override the prompt. 41 | 42 | ```bash 43 | terraform destroy --auto-approve 44 | ``` 45 | 46 | 6. Examine the Docker provider documentation 47 | 48 | https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs 49 | 50 | > Congratulations you have finished this lab! 51 | 52 | -------------------------------------------------------------------------------- /01-Basic-Commands/guide.md: -------------------------------------------------------------------------------- 1 | # Basic Commands 2 | 3 | In this lab we learn the very basic Terraform commands. 4 | 5 | ## Instructions 6 | 7 | 1. Change directory into the 01-Basic-Commands directory: 8 | 9 | ```bash 10 | cd 01-Basic-Commands 11 | ``` 12 | 13 | 2. Run the following commands 14 | 15 | ```bash 16 | terraform version 17 | terraform -help 18 | terraform init 19 | terraform fmt 20 | terraform validate 21 | terraform plan 22 | terraform apply 23 | terraform output 24 | ``` 25 | 26 | Here is the explanation of each of these commands: 27 | - `terraform version:` This command displays the currently installed version of Terraform. It is used to verify which version of Terraform you have installed and to ensure that the correct version is being used for your infrastructure. 28 | 29 | - `terraform -help:` This command displays a list of available commands and options for Terraform. It is used to get help with Terraform commands and options and to learn how to use Terraform. 30 | 31 | - `terraform init:` This command initializes a new or existing Terraform working directory. It is used to download and install the required provider plugins, configure the backend, and prepare the working directory for Terraform operations. 32 | 33 | - `terraform fmt:` This command is used to format the Terraform configuration files in a standard and consistent way. It is used to automatically update and standardize the formatting of your Terraform code to make it easier to read and maintain. 34 | 35 | - `terraform validate:` This command is used to validate the syntax and configuration of your Terraform code. It is used to check if your code follows the correct syntax and format and can catch errors early in the development process. 36 | 37 | - `terraform plan:` This command generates an execution plan that shows what Terraform will do when you apply your configuration. It is used to preview the changes that will be made to your infrastructure and to detect any potential problems before applying the changes. 38 | 39 | - `terraform apply:` This command applies the changes to your infrastructure according to your Terraform configuration. It is used to create, modify, or delete resources in your infrastructure. 40 | 41 | - `terraform output:` This command displays the outputs of your Terraform configuration. It is used to retrieve the values of any output variables that were defined in your Terraform code. 42 | 43 | 3. Run `docker ps` and `docker images` to check the newly created container 44 | 45 | ```bash 46 | docker ps 47 | docker images 48 | ``` 49 | 50 | 4. Go to the nginx URL 51 | If port 8080 doesn't get forwarded automatically, then go to the `PORTS` tab and add port 8080 manually. 52 | 53 | Now click on the Globe Icon under Lcal Address in the `PORTS` tab to open the URL for nginx. 54 | 55 | 5. Now destroy the environment with `terraform destroy` 56 | 57 | ```bash 58 | terraform destroy 59 | ``` 60 | 61 | > Congratulations you have finished this lab! 62 | 63 | ## Lab Code Explanation 64 | 65 | Terraform is an infrastructure-as-code tool that allows you to define and manage infrastructure resources in a declarative way. This Terraform configuration sets up a Docker container running the Nginx web server with port forwarding from port 8080 on the host machine to port 80 inside the container. 66 | 67 | 68 | Let's go through the code line by line: 69 | 70 | ```hcl 71 | terraform { 72 | required_providers { # The `required_providers` block specifies the provider(s) needed for this Terraform configuration. 73 | docker = { # Here, we are requiring the `docker` provider. 74 | source = "kreuzwerker/docker" # This line specifies the source of the provider. In this case, it is the `kreuzwerker/docker` provider. 75 | version = "3.0.1" # This line specifies the version of the provider that we need. 76 | } 77 | } 78 | } 79 | 80 | provider "docker" {} # This `provider` block specifies that we want to use the `docker` provider. 81 | 82 | resource "docker_image" "nginx_image" { # This `resource` block creates a Docker image named `nginx_image` using the `nginx:1.23.3` image. 83 | name = "nginx:1.23.3" 84 | } 85 | 86 | resource "docker_container" "nginx_container" { # This `resource` block creates a Docker container named `nginx_container` based on the `nginx_image` image. 87 | name = "web-server" # This line specifies the name of the container. 88 | image = docker_image.nginx_image.image_id # This line references the `nginx_image` resource created earlier to get the image ID. 89 | ports { # This block specifies the ports to be exposed by the container. 90 | internal = 80 # The container's internal port 80 will be exposed. 91 | external = 8080 # The container's internal port 80 will be accessible on the host's port 8080. 92 | } 93 | } 94 | ``` 95 | --------------------------------------------------------------------------------