├── m5
├── terraform_cloud_config
│ ├── outputs.tf
│ ├── terraform.tf
│ ├── main.tf
│ └── variables.tf
├── github_config
│ ├── outputs.tf
│ ├── terraform.tf
│ ├── variables.tf
│ └── main.tf
├── gitignore.example
├── terraform.yml
└── m5_commands.txt
├── m9
├── create_secrets_manager
│ ├── terraform.tfvars.example
│ ├── terraform.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── main.tf
├── application_config_complete
│ ├── .gitignore
│ ├── terraform.tf
│ ├── keys.tf
│ ├── outputs.tf
│ ├── templates
│ │ ├── application.config.tpl
│ │ └── userdata.sh
│ ├── datasources.tf
│ ├── ssm_parameters.tf
│ ├── .github
│ │ └── workflows
│ │ │ └── terraform.yml
│ ├── security_groups.tf
│ ├── variables.tf
│ └── resources.tf
├── updated_userdata
│ └── userdata.sh
└── m9_commands.txt
├── m3
├── network_config_example
│ ├── terraform.tf
│ ├── outputs.tf
│ ├── variables.tf
│ ├── imports.tf
│ └── resources.tf
├── m3_commands.txt
└── cloud_formation_template
│ └── vpc_template.yaml
├── m7
├── application_config_example
│ ├── terraform.tf
│ ├── terraform.tfvars.example
│ ├── keys.tf
│ ├── templates
│ │ ├── application.config.tpl
│ │ └── userdata.sh
│ ├── outputs.tf
│ ├── datasources.tf
│ ├── .github
│ │ └── workflows
│ │ │ └── terraform.yml
│ ├── variables.tf
│ ├── security_groups.tf
│ └── resources.tf
├── github_config_application
│ ├── variables.tf
│ ├── terraform.tf
│ ├── outputs.tf
│ └── main.tf
└── m7_commands.txt
├── m4
├── backend.tf
└── m4_commands.txt
├── m8
├── ansible_updates
│ └── userdata.sh
└── m8_commands.txt
├── .gitignore
├── m6
└── m6_commands.txt
├── CHANGELOG.md
└── README.md
/m5/terraform_cloud_config/outputs.tf:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/m9/create_secrets_manager/terraform.tfvars.example:
--------------------------------------------------------------------------------
1 | api_key = "SOME_VALUE"
--------------------------------------------------------------------------------
/m3/network_config_example/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~>5.0"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/m5/github_config/outputs.tf:
--------------------------------------------------------------------------------
1 | output "remote_url" {
2 | value = github_repository.main.http_clone_url
3 | description = "URL to use when adding remote to local git repo."
4 | }
--------------------------------------------------------------------------------
/m5/github_config/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | github = {
4 | source = "integrations/github"
5 | version = "~> 5.0"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/m5/github_config/variables.tf:
--------------------------------------------------------------------------------
1 | variable "repository_name" {
2 | type = string
3 | description = "Name of repository to create"
4 | default = "globo-networking"
5 | }
--------------------------------------------------------------------------------
/m5/terraform_cloud_config/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | tfe = {
4 | source = "hashicorp/tfe"
5 | version = ">=0.45.0"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/m9/create_secrets_manager/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~>5.0"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/m7/application_config_example/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~>5.0"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/m7/github_config_application/variables.tf:
--------------------------------------------------------------------------------
1 | variable "repository_name" {
2 | type = string
3 | description = "Name of repository to create"
4 | default = "globo-webapp"
5 | }
--------------------------------------------------------------------------------
/m7/github_config_application/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | github = {
4 | source = "integrations/github"
5 | version = "~> 5.0"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/m9/create_secrets_manager/outputs.tf:
--------------------------------------------------------------------------------
1 | output "role_name" {
2 | value = aws_iam_role.web_app.name
3 | }
4 |
5 | output "secret_id" {
6 | value = aws_secretsmanager_secret.api_key.id
7 | }
--------------------------------------------------------------------------------
/m7/github_config_application/outputs.tf:
--------------------------------------------------------------------------------
1 | output "remote_url" {
2 | value = github_repository.main.http_clone_url
3 | description = "URL to use when adding remote to local git repo."
4 | }
--------------------------------------------------------------------------------
/m3/network_config_example/outputs.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # OUTPUT
3 | ##################################################################################
4 |
--------------------------------------------------------------------------------
/m7/application_config_example/terraform.tfvars.example:
--------------------------------------------------------------------------------
1 | prefix = "globo-dev"
2 | environment = "testing"
3 | billing_code = "8652147555"
4 | public_subnets = [ "SUBNET_1","SUBNET_2" ]
5 | vpc_id = "VPC_ID"
6 | api_key = "Tac0$AreDel!c1ous"
--------------------------------------------------------------------------------
/m5/gitignore.example:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/m9/application_config_complete/.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
--------------------------------------------------------------------------------
/m9/application_config_complete/terraform.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | version = "~>5.0"
6 | }
7 | tfe = {
8 | source = "hashicorp/tfe"
9 | version = "~> 0.0"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/m4/backend.tf:
--------------------------------------------------------------------------------
1 | ## Move this backend file to your network config when migrating state
2 | terraform {
3 | cloud {
4 | # Organization ID
5 | organization = "ORGANIZATION_NAME"
6 | # Workspace ID
7 | workspaces {
8 | name = "web-network-dev"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/m7/application_config_example/keys.tf:
--------------------------------------------------------------------------------
1 | # Create SSH Key pair for aws instances using a module
2 | module "ssh_keys" {
3 | source = "terraform-aws-modules/key-pair/aws"
4 | version = "~>2.0.0"
5 |
6 | key_name = "${local.name_prefix}-tdd-keys"
7 | create_private_key = true
8 | }
9 |
--------------------------------------------------------------------------------
/m9/application_config_complete/keys.tf:
--------------------------------------------------------------------------------
1 | # Create SSH Key pair for aws instances using a module
2 | module "ssh_keys" {
3 | source = "terraform-aws-modules/key-pair/aws"
4 | version = "~>2.0.0"
5 |
6 | key_name = "${local.name_prefix}-tdd-keys"
7 | create_private_key = true
8 | }
9 |
--------------------------------------------------------------------------------
/m7/application_config_example/templates/application.config.tpl:
--------------------------------------------------------------------------------
1 | # Change every time hosts are added or removed
2 | host_list {
3 | %{ for host in hosts ~}
4 | hostname ${host}
5 | %{ endfor ~}
6 | }
7 |
8 | app_config {
9 | site_name = "${site_name}"
10 | api_key = "${api_key}"
11 | }
--------------------------------------------------------------------------------
/m7/application_config_example/outputs.tf:
--------------------------------------------------------------------------------
1 | output "public_lb_dns" {
2 | value = aws_lb.main.dns_name
3 | }
4 |
5 | output "webapp_instance0_public_ip" {
6 | value = aws_instance.main[0].public_ip
7 | }
8 |
9 | output "private_key_pem" {
10 | value = nonsensitive(module.ssh_keys.private_key_pem)
11 | }
--------------------------------------------------------------------------------
/m9/application_config_complete/outputs.tf:
--------------------------------------------------------------------------------
1 | output "public_lb_dns" {
2 | value = aws_lb.main.dns_name
3 | }
4 |
5 | output "webapp_instance0_public_ip" {
6 | value = aws_instance.main[0].public_ip
7 | }
8 |
9 | output "private_key_pem" {
10 | value = nonsensitive(module.ssh_keys.private_key_pem)
11 | }
--------------------------------------------------------------------------------
/m9/application_config_complete/templates/application.config.tpl:
--------------------------------------------------------------------------------
1 | # Change every time hosts are added or removed
2 | host_list {
3 | %{ for host in hosts ~}
4 | hostname ${host}
5 | %{ endfor ~}
6 | }
7 |
8 | app_config {
9 | site_name = "${site_name}"
10 | api_key = "${api_key}"
11 | }
--------------------------------------------------------------------------------
/m8/ansible_updates/userdata.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | sudo yum update
3 | sudo yum install git -y
4 | sudo amazon-linux-extras install ansible2 -y
5 | sudo mkdir /var/ansible_playbooks
6 | sudo git clone ${playbook_repository} /var/ansible_playbooks
7 | ansible-playbook /var/ansible_playbooks/playbook.yml -i /var/ansible_playbooks/hosts
--------------------------------------------------------------------------------
/m9/create_secrets_manager/variables.tf:
--------------------------------------------------------------------------------
1 | variable "region" {
2 | type = string
3 | description = "(Optional) AWS Region to deploy in. Defaults to us-east-1."
4 | default = "us-east-1"
5 | }
6 |
7 | variable "api_key" {
8 | type = string
9 | description = "(Required) String to use for API key"
10 | sensitive = true
11 | }
--------------------------------------------------------------------------------
/.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 | # Consul data directories
18 | **/network_config/*
19 | **/application_config/*
20 |
21 | # .hcl files
22 | *.hcl
--------------------------------------------------------------------------------
/m7/application_config_example/datasources.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # DATA SOURCES
3 | ##################################################################################
4 |
5 | data "aws_ssm_parameter" "amzn2_linux" {
6 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
7 | }
--------------------------------------------------------------------------------
/m5/terraform_cloud_config/main.tf:
--------------------------------------------------------------------------------
1 | provider "tfe" {
2 | # Configuration options
3 | hostname = var.hostname
4 | }
5 |
6 | resource "tfe_oauth_client" "github" {
7 | name = var.oauth_name
8 | organization = var.organization
9 | api_url = "https://api.github.com"
10 | http_url = "https://github.com"
11 | oauth_token = var.gh_pat
12 | service_provider = "github"
13 | }
--------------------------------------------------------------------------------
/m7/application_config_example/templates/userdata.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | sudo amazon-linux-extras install -y nginx1
3 | sudo service nginx start
4 | sudo rm /usr/share/nginx/html/index.html
5 | echo '
Taco Wagon ServerYou did it! Have a 🌮
' | sudo tee /usr/share/nginx/html/index.html
--------------------------------------------------------------------------------
/m9/application_config_complete/datasources.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # DATA SOURCES
3 | ##################################################################################
4 |
5 | data "tfe_outputs" "networking" {
6 | organization = var.tfe_organization
7 | workspace = var.tfe_workspace_name
8 | }
9 |
10 | data "aws_ssm_parameter" "amzn2_linux" {
11 | name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
12 | }
--------------------------------------------------------------------------------
/m5/terraform_cloud_config/variables.tf:
--------------------------------------------------------------------------------
1 | variable "hostname" {
2 | type = string
3 | description = "The Terraform Cloud/Enterprise hostname to connect to"
4 | default = "app.terraform.io"
5 | }
6 |
7 | variable "oauth_name" {
8 | type = string
9 | default = "tdd-github"
10 | }
11 |
12 | variable "organization" {
13 | type = string
14 | description = "Terraform Cloud organization"
15 | }
16 |
17 | variable "gh_pat" {
18 | type = string
19 | description = "Github Personal Access token"
20 | }
--------------------------------------------------------------------------------
/m5/github_config/main.tf:
--------------------------------------------------------------------------------
1 | resource "github_repository" "main" {
2 | name = var.repository_name
3 | description = "Terraform Deep Dive Repository for Globomantics Networking"
4 | visibility = "public"
5 | auto_init = true
6 | gitignore_template = "Terraform"
7 | }
8 |
9 | resource "github_branch" "main" {
10 | repository = github_repository.main.name
11 | branch = "main"
12 | }
13 |
14 | resource "github_branch_default" "default" {
15 | repository = github_repository.main.name
16 | branch = github_branch.main.branch
17 | }
--------------------------------------------------------------------------------
/m7/github_config_application/main.tf:
--------------------------------------------------------------------------------
1 | resource "github_repository" "main" {
2 | name = var.repository_name
3 | description = "Terraform Deep Dive Repository for Globomantics Application"
4 | visibility = "public"
5 | auto_init = true
6 | gitignore_template = "Terraform"
7 | }
8 |
9 | resource "github_branch" "main" {
10 | repository = github_repository.main.name
11 | branch = "main"
12 | }
13 |
14 | resource "github_branch_default" "default" {
15 | repository = github_repository.main.name
16 | branch = github_branch.main.branch
17 | }
--------------------------------------------------------------------------------
/m6/m6_commands.txt:
--------------------------------------------------------------------------------
1 | # Get your local repository up to date
2 | cd network_config
3 | git checkout main
4 | git pull
5 |
6 | # Create a new branch for adding the workspace tag and removing variable defaults
7 | git checkout -b add-workspace-tag
8 |
9 | # Make code changes
10 | # Commit changes to branch
11 | git add .
12 | git commit -m "Add workspace tag and make variables required"
13 |
14 | # Push changes to GitHub
15 | git push --set-upstream origin add-workspace-tag
16 |
17 | # Head over to GitHub for the rest of the workflow
18 |
19 | # Push an empty commit
20 | git commit --allow-empty -m "Trigger workflow"
21 | git push
22 |
--------------------------------------------------------------------------------
/m9/updated_userdata/userdata.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | sudo yum update
3 | sudo yum install git -y
4 | sudo amazon-linux-extras install ansible2 -y
5 | sudo mkdir /var/ansible_playbooks
6 | sudo git clone ${playbook_repository} /var/ansible_playbooks
7 | aws secretsmanager get-secret-value --secret-id "${secret_id}" --region us-east-1 --query SecretString --output text > /var/ansible_playbooks/api_key.txt
8 | aws ssm get-parameter --name "${host_list_ssm_name}" --region us-east-1 --query Parameter.Value --output text > /var/ansible_playbooks/host_list.txt
9 | aws ssm get-parameter --name "${site_name_ssm_name}" --region us-east-1 --query Parameter.Value --output text > /var/ansible_playbooks/site_name.txt
10 | ansible-playbook /var/ansible_playbooks/playbook.yml -i /var/ansible_playbooks/hosts
--------------------------------------------------------------------------------
/m9/application_config_complete/templates/userdata.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | sudo yum update
3 | sudo yum install git -y
4 | sudo amazon-linux-extras install ansible2 -y
5 | sudo mkdir /var/ansible_playbooks
6 | sudo git clone ${playbook_repository} /var/ansible_playbooks
7 | aws secretsmanager get-secret-value --secret-id "${secret_id}" --region us-east-1 --query SecretString --output text > /var/ansible_playbooks/api_key.txt
8 | aws ssm get-parameter --name "${host_list_ssm_name}" --region us-east-1 --query Parameter.Value --output text > /var/ansible_playbooks/host_list.txt
9 | aws ssm get-parameter --name "${site_name_ssm_name}" --region us-east-1 --query Parameter.Value --output text > /var/ansible_playbooks/site_name.txt
10 | ansible-playbook /var/ansible_playbooks/playbook.yml -i /var/ansible_playbooks/hosts
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | **2023-08-11**
2 |
3 | FULL COURSE UPDATE - v3
4 |
5 | This is a full update of the course. The default branch for the repository is now `main`. The `v2` branch will be kept for historical purposes. V3 is a complete overhaul of V2, so the code is not compatible with V2 of the Deep Dive course.
6 |
7 | **2020-12-21**
8 |
9 | ISSUES fixed
10 | * Removed backend.tf file from m3 (not supposed to be there until you copy it over in m4)
11 | * Added command to install `jq` if it's missing in the `junior_admin.sh` script in m3
12 | * Added quotes for import commands in the `ImportCOmmands.txt` file in m3
13 | * Removed route table ID references (they should not have been there)
14 |
15 | 0.14.3 Updates
16 | * Change VPC module version to support 0.14 of Terraform
17 | * Update the configurations to use the `terraform` and `required_providers` blocks
18 | * exclude `hcl` files in `.gitignore`
19 | * Add quota increase request
20 |
--------------------------------------------------------------------------------
/m9/m9_commands.txt:
--------------------------------------------------------------------------------
1 | # Set AWS_PROFILE
2 |
3 | # Linux or MacOS
4 | export AWS_PROFILE=deep-dive
5 |
6 | # Windows
7 | $env:AWS_PROFILE="deep-dive"
8 |
9 | # Move API Key to secrets manager
10 | # Create secrets manager and ec2 role with Terraform
11 | cd m9/create_secrets_manager
12 | terraform init
13 |
14 | # Set the API key value in terraform.tfvars
15 | terraform apply
16 |
17 | # Note the role name and secret_id
18 |
19 | # Create a new branch
20 | cd ../../application_config
21 | git checkout main
22 | git pull
23 | git checkout -b add-secrets-manager
24 |
25 | # Update code to remove the terraform_data block
26 | # Add variable for role name and secret ID
27 | # Remove variable for api key
28 | # Add instance profile resource
29 | # Update iam_instance to use profile
30 | # Add secret id to userdata script and templatefile function
31 |
32 | # Commit changes
33 | git add .
34 | git commit -m "Switch to using secrets manager"
35 | git push --set-upstream origin add-secrets-manager
36 |
37 | # Update variables in workspace
38 | # Create pull request and merge if it looks right
39 |
--------------------------------------------------------------------------------
/m3/network_config_example/variables.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # VARIABLES
3 | ##################################################################################
4 |
5 | variable "region" {
6 | type = string
7 | description = "(Optional) AWS Region to use. Default: us-east-1"
8 | default = "us-east-1"
9 | }
10 |
11 | variable "prefix" {
12 | type = string
13 | description = "(Optional) Prefix to use for all resources in this module. Default: globo-dev"
14 | default = "globo-dev"
15 | }
16 |
17 | variable "cidr_block" {
18 | type = string
19 | description = "(Optional) The CIDR block for the VPC. Default:10.42.0.0/16"
20 | default = "10.42.0.0/16"
21 | }
22 |
23 | variable "public_subnets" {
24 | type = map(string)
25 | description = "(Optional) Map of public subnets to create with CIDR blocks. Key will be used as subnet name with prefix. Default: {subnet-1 ="
26 | default = {
27 | public-1 = "10.42.10.0/24"
28 | public-2 = "10.42.11.0/24"
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/m9/application_config_complete/ssm_parameters.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | host_list_ssm_name = "/${local.name_prefix}/host-list"
3 | site_name_ssm_name = "/${local.name_prefix}/site-name"
4 | }
5 |
6 | resource "aws_ssm_parameter" "host_list" {
7 | name = local.host_list_ssm_name
8 | type = "StringList"
9 | value = join(",", aws_instance.main.*.private_dns)
10 | }
11 |
12 | resource "aws_ssm_parameter" "site_name" {
13 | name = local.site_name_ssm_name
14 | type = "String"
15 | value = "${local.name_prefix}-taco-wagon"
16 | }
17 |
18 | data "aws_iam_policy_document" "ssm_access" {
19 | statement {
20 | effect = "Allow"
21 | actions = ["ssm:GetParameter"]
22 | resources = [aws_ssm_parameter.host_list.arn, aws_ssm_parameter.site_name.arn]
23 | }
24 | }
25 |
26 | resource "aws_iam_policy" "ssm_access" {
27 | name = "${local.name_prefix}-ssm-access"
28 | policy = data.aws_iam_policy_document.ssm_access.json
29 | }
30 |
31 | resource "aws_iam_role_policy_attachment" "ssm_access" {
32 | role = var.ec2_role_name
33 | policy_arn = aws_iam_policy.ssm_access.arn
34 | }
--------------------------------------------------------------------------------
/m8/m8_commands.txt:
--------------------------------------------------------------------------------
1 | # Start a new branch for ansible changes
2 | git checkout main
3 | git pull
4 | git checkout -b ansible-change
5 |
6 | # Don't forget to fmt and validate changes!
7 |
8 | # Copy the userdata update over
9 | cp ../m8/ansible_updates/userdata.sh ./templates/userdata.sh
10 |
11 | # Make code changes to use ansible instead of provisioners
12 | # Repository url is https://github.com/ned1313/ansible-playbook-nginx.git
13 | # Commit changes and push to origin
14 | git add .
15 | git commit -m "Switch to using Ansible for nginx install"
16 | git push --set-upstream origin ansible-change
17 |
18 | # Add playbook variable to workspace
19 | # Create pull request and review results
20 |
21 | # Updating the null_resource
22 | # Create a new branch called terraform-data
23 | git checkout main
24 | git pull
25 | git checkout -b terraform-data
26 |
27 | # Switch out the null_resource for terraform_data
28 | # Commit changes and push to origin
29 | git add .
30 | git commit -m "Switch to using terraform_data"
31 | git push --set-upstream origin terraform-data
32 |
33 | # Create pull request and merge changes to development
34 | # Merge changes to main also
35 |
--------------------------------------------------------------------------------
/m4/m4_commands.txt:
--------------------------------------------------------------------------------
1 | # Make sure your AWS profile is set
2 | # Linux or MacOS
3 | export AWS_PROFILE=deep-dive
4 |
5 | # Windows
6 | $env:AWS_PROFILE="deep-dive"
7 |
8 | ## First let's try out some terraform state commands
9 | ## Go to the network_config folder and run the state commands
10 |
11 | # View all the Terraform resources
12 | terraform state list
13 |
14 | # Now let's look at a specific resource
15 | terraform state show module.main.aws_vpc.this[0]
16 |
17 | # We can also view all the state data
18 | terraform state pull
19 |
20 | ## Now it's time to migrate our state data to Terraform cloud
21 | ## Create a terraform cloud account and organization called deep-dive-
22 | ## Sign up for an account here: https://app.terraform.io/public/signup/account
23 |
24 | ## Login into Terraform Cloud to get an user access token
25 | terraform login
26 |
27 | # Copy the backend file from the m4 directory to the network_config
28 | cp ../m4/backend.tf .
29 |
30 | # Update the backend info and run Terraform init to migrate state data
31 | terraform init
32 |
33 | # Now run a Terraform plan and note that it fails
34 | terraform plan
35 |
36 | # After adding AWS credentials, run a Terraform apply
37 | terraform apply
38 |
--------------------------------------------------------------------------------
/m9/create_secrets_manager/main.tf:
--------------------------------------------------------------------------------
1 | provider "aws" {
2 | region = var.region
3 | }
4 |
5 | resource "aws_secretsmanager_secret" "api_key" {
6 | name = "taco_wagon_dev_api_key"
7 | }
8 |
9 | resource "aws_secretsmanager_secret_version" "api_key" {
10 | secret_id = aws_secretsmanager_secret.api_key.id
11 | secret_string = var.api_key
12 | }
13 |
14 | data "aws_iam_policy_document" "web_app_access" {
15 | statement {
16 | principals {
17 | type = "AWS"
18 | identifiers = [aws_iam_role.web_app.arn]
19 | }
20 |
21 | actions = ["secretsmanager:GetSecretValue"]
22 |
23 | resources = ["*"]
24 | }
25 | }
26 |
27 | resource "aws_secretsmanager_secret_policy" "api_key" {
28 | secret_arn = aws_secretsmanager_secret.api_key.arn
29 | policy = data.aws_iam_policy_document.web_app_access.json
30 | }
31 |
32 | resource "aws_iam_role" "web_app" {
33 | name = "web_app_dev_api_key_access"
34 |
35 | assume_role_policy = jsonencode({
36 | Version = "2012-10-17"
37 | Statement = [
38 | {
39 | Action = "sts:AssumeRole"
40 | Effect = "Allow"
41 | Sid = ""
42 | Principal = {
43 | Service = "ec2.amazonaws.com"
44 | }
45 | },
46 | ]
47 | })
48 | }
--------------------------------------------------------------------------------
/m5/terraform.yml:
--------------------------------------------------------------------------------
1 | name: 'Terraform'
2 |
3 | on: push
4 |
5 | env:
6 | TF_LOG: INFO
7 | TF_INPUT: false
8 |
9 | jobs:
10 | terraform:
11 | name: 'Terraform'
12 | runs-on: ubuntu-latest
13 |
14 | # Use the Bash shell regardless whether the GitHub Actions runner is
15 | # ubuntu-latest, macos-latest, or windows-latest
16 | defaults:
17 | run:
18 | shell: bash
19 |
20 | steps:
21 | # Checkout the repository to the GitHub Actions runner
22 | - name: Checkout
23 | uses: actions/checkout@v3
24 |
25 | # Install the preferred version of Terraform CLI
26 | - name: Setup Terraform
27 | uses: hashicorp/setup-terraform@v2
28 |
29 | # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
30 | - name: Terraform Init
31 | id: init
32 | run: terraform init
33 |
34 | # Run a terraform fmt for push
35 | - name: Terraform Format
36 | id: fmt
37 | run: terraform fmt -check
38 |
39 | # Run a terraform validate
40 | # Run even if formatting fails
41 | - name: Terraform Validate
42 | id: validate
43 | if: (success() || failure())
44 | run: terraform validate
45 |
--------------------------------------------------------------------------------
/m7/application_config_example/.github/workflows/terraform.yml:
--------------------------------------------------------------------------------
1 | name: 'Terraform'
2 |
3 | on: push
4 |
5 | env:
6 | TF_LOG: INFO
7 | TF_INPUT: false
8 |
9 | jobs:
10 | terraform:
11 | name: 'Terraform'
12 | runs-on: ubuntu-latest
13 |
14 | # Use the Bash shell regardless whether the GitHub Actions runner is
15 | # ubuntu-latest, macos-latest, or windows-latest
16 | defaults:
17 | run:
18 | shell: bash
19 |
20 | steps:
21 | # Checkout the repository to the GitHub Actions runner
22 | - name: Checkout
23 | uses: actions/checkout@v3
24 |
25 | # Install the preferred version of Terraform CLI
26 | - name: Setup Terraform
27 | uses: hashicorp/setup-terraform@v2
28 |
29 | # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
30 | - name: Terraform Init
31 | id: init
32 | run: terraform init
33 |
34 | # Run a terraform fmt for push
35 | - name: Terraform Format
36 | id: fmt
37 | run: terraform fmt -check
38 |
39 | # Run a terraform validate
40 | # Run even if formatting fails
41 | - name: Terraform Validate
42 | id: validate
43 | if: (success() || failure())
44 | run: terraform validate
45 |
--------------------------------------------------------------------------------
/m9/application_config_complete/.github/workflows/terraform.yml:
--------------------------------------------------------------------------------
1 | name: 'Terraform'
2 |
3 | on: push
4 |
5 | env:
6 | TF_LOG: INFO
7 | TF_INPUT: false
8 |
9 | jobs:
10 | terraform:
11 | name: 'Terraform'
12 | runs-on: ubuntu-latest
13 |
14 | # Use the Bash shell regardless whether the GitHub Actions runner is
15 | # ubuntu-latest, macos-latest, or windows-latest
16 | defaults:
17 | run:
18 | shell: bash
19 |
20 | steps:
21 | # Checkout the repository to the GitHub Actions runner
22 | - name: Checkout
23 | uses: actions/checkout@v3
24 |
25 | # Install the preferred version of Terraform CLI
26 | - name: Setup Terraform
27 | uses: hashicorp/setup-terraform@v2
28 |
29 | # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
30 | - name: Terraform Init
31 | id: init
32 | run: terraform init
33 |
34 | # Run a terraform fmt for push
35 | - name: Terraform Format
36 | id: fmt
37 | run: terraform fmt -check
38 |
39 | # Run a terraform validate
40 | # Run even if formatting fails
41 | - name: Terraform Validate
42 | id: validate
43 | if: (success() || failure())
44 | run: terraform validate
45 |
--------------------------------------------------------------------------------
/m3/network_config_example/imports.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # IMPORTS
3 | ##################################################################################
4 |
5 | import {
6 | to = module.main.aws_vpc.this[0]
7 | id = "VPC" #VPC
8 | }
9 |
10 | import {
11 | to = module.main.aws_subnet.public[0]
12 | id = "PublicSubnet1" #PublicSubnet1
13 | }
14 |
15 | import {
16 | to = module.main.aws_subnet.public[1]
17 | id = "PublicSubnet2" #PublicSubnet2
18 | }
19 |
20 | import {
21 | to = module.main.aws_internet_gateway.this[0]
22 | id = "InternetGateway" #InternetGateway
23 | }
24 |
25 | import {
26 | to = module.main.aws_route.public_internet_gateway[0]
27 | id = "DefaultPublicRoute" #DefaultPublicRoute
28 | }
29 |
30 | import {
31 | to = module.main.aws_route_table.public[0]
32 | id = "PublicRouteTable" #PublicRouteTable
33 | }
34 |
35 | import {
36 | to = module.main.aws_route_table_association.public[0]
37 | id = "PublicSubnet1/PublicRouteTable" #PublicSubnet1/PublicRouteTable
38 | }
39 |
40 | import {
41 | to = module.main.aws_route_table_association.public[1]
42 | id = "PublicSubnet2/PublicRouteTable" #PublicSubnet2/PublicRouteTable
43 | }
44 |
45 | import {
46 | to = aws_security_group.ingress
47 | id = "NoIngressSecurityGroup" #NoIngressSecurityGroup
48 | }
49 |
--------------------------------------------------------------------------------
/m7/application_config_example/variables.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "region" {
3 | type = string
4 | description = "(Optional) AWS Region to deploy in. Defaults to us-east-1."
5 | default = "us-east-1"
6 | }
7 |
8 | variable "prefix" {
9 | type = string
10 | description = "(Required) Prefix to use for all resources in this module."
11 | }
12 |
13 | variable "environment" {
14 | type = string
15 | description = "(Required) Environment of all resources"
16 | }
17 |
18 | variable "billing_code" {
19 | type = string
20 | description = "(Required) Billing code for network resources"
21 | }
22 |
23 | # Application variables
24 |
25 | variable "ip_range" {
26 | default = "0.0.0.0/0"
27 | }
28 |
29 | variable "instance_type" {
30 | type = string
31 | description = "(Optional) EC2 Instance type to use for web app. Defaults to t3.micro."
32 | default = "t3.micro"
33 | }
34 |
35 | variable "api_key" {
36 | type = string
37 | description = "(Required) API key for web app to talk to SaaS platform."
38 | }
39 |
40 | variable "public_subnets" {
41 | type = list(string)
42 | description = "(Required) List of subnet IDs for EC2 instance deployments."
43 | }
44 |
45 | variable "vpc_id" {
46 | type = string
47 | description = "(Required) VPC ID of VPC for application deployment."
48 | }
49 |
50 |
51 |
--------------------------------------------------------------------------------
/m3/network_config_example/resources.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # PROVIDERS
3 | ##################################################################################
4 |
5 | provider "aws" {
6 | region = var.region
7 | }
8 |
9 | ##################################################################################
10 | # DATA
11 | ##################################################################################
12 |
13 | data "aws_availability_zones" "available" {}
14 |
15 | ##################################################################################
16 | # RESOURCES
17 | ##################################################################################
18 | locals {
19 | common_tags = {
20 |
21 | }
22 | }
23 |
24 | module "main" {
25 | source = "terraform-aws-modules/vpc/aws"
26 | version = "5.0.0"
27 |
28 | name = var.prefix
29 | cidr = var.cidr_block
30 |
31 | azs = slice(data.aws_availability_zones.available.names, 0, length(var.public_subnets))
32 | public_subnets = [for k, v in var.public_subnets : v]
33 | public_subnet_names = [for k, v in var.public_subnets : "${var.prefix}-${k}"]
34 | enable_dns_hostnames = true
35 | public_subnet_suffix = ""
36 | public_route_table_tags = { Name = "${var.prefix}-public" }
37 | map_public_ip_on_launch = true
38 |
39 | enable_nat_gateway = false
40 |
41 | tags = local.common_tags
42 | }
--------------------------------------------------------------------------------
/m7/application_config_example/security_groups.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # RESOURCES
3 | ##################################################################################
4 |
5 | resource "aws_security_group" "webapp_http_inbound_sg" {
6 | name = "${local.name_prefix}-http-inbound"
7 | description = "Allow HTTP from Anywhere"
8 |
9 | ingress {
10 | from_port = 80
11 | to_port = 80
12 | protocol = "tcp"
13 | cidr_blocks = ["0.0.0.0/0"]
14 | }
15 |
16 | egress {
17 | from_port = 0
18 | to_port = 0
19 | protocol = "-1"
20 | cidr_blocks = ["0.0.0.0/0"]
21 | }
22 |
23 | vpc_id = var.vpc_id
24 |
25 | tags = local.common_tags
26 | }
27 |
28 | resource "aws_security_group" "webapp_ssh_inbound_sg" {
29 | name = "${local.name_prefix}-ssh-inbound"
30 | description = "Allow SSH from certain ranges"
31 |
32 | ingress {
33 | from_port = 22
34 | to_port = 22
35 | protocol = "tcp"
36 | cidr_blocks = [var.ip_range]
37 | }
38 |
39 | vpc_id = var.vpc_id
40 |
41 | tags = local.common_tags
42 | }
43 |
44 | resource "aws_security_group" "webapp_outbound_sg" {
45 | name = "${local.name_prefix}-webapp-outbound"
46 | description = "Allow outbound connections"
47 |
48 | egress {
49 | from_port = 0
50 | to_port = 0
51 | protocol = "-1"
52 | cidr_blocks = ["0.0.0.0/0"]
53 | }
54 |
55 | vpc_id = var.vpc_id
56 |
57 | tags = local.common_tags
58 | }
--------------------------------------------------------------------------------
/m9/application_config_complete/security_groups.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # RESOURCES
3 | ##################################################################################
4 |
5 | resource "aws_security_group" "webapp_http_inbound_sg" {
6 | name = "${local.name_prefix}-http-inbound"
7 | description = "Allow HTTP from Anywhere"
8 |
9 | ingress {
10 | from_port = 80
11 | to_port = 80
12 | protocol = "tcp"
13 | cidr_blocks = ["0.0.0.0/0"]
14 | }
15 |
16 | egress {
17 | from_port = 0
18 | to_port = 0
19 | protocol = "-1"
20 | cidr_blocks = ["0.0.0.0/0"]
21 | }
22 |
23 | vpc_id = data.tfe_outputs.networking.nonsensitive_values.vpc_id
24 |
25 | tags = local.common_tags
26 | }
27 |
28 | resource "aws_security_group" "webapp_ssh_inbound_sg" {
29 | name = "${local.name_prefix}-ssh-inbound"
30 | description = "Allow SSH from certain ranges"
31 |
32 | ingress {
33 | from_port = 22
34 | to_port = 22
35 | protocol = "tcp"
36 | cidr_blocks = [var.ip_range]
37 | }
38 |
39 | vpc_id = data.tfe_outputs.networking.nonsensitive_values.vpc_id
40 |
41 | tags = local.common_tags
42 | }
43 |
44 | resource "aws_security_group" "webapp_outbound_sg" {
45 | name = "${local.name_prefix}-webapp-outbound"
46 | description = "Allow outbound connections"
47 |
48 | egress {
49 | from_port = 0
50 | to_port = 0
51 | protocol = "-1"
52 | cidr_blocks = ["0.0.0.0/0"]
53 | }
54 |
55 | vpc_id = data.tfe_outputs.networking.nonsensitive_values.vpc_id
56 |
57 | tags = local.common_tags
58 | }
--------------------------------------------------------------------------------
/m9/application_config_complete/variables.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "region" {
3 | type = string
4 | description = "(Optional) AWS Region to deploy in. Defaults to us-east-1."
5 | default = "us-east-1"
6 | }
7 |
8 | variable "prefix" {
9 | type = string
10 | description = "(Required) Prefix to use for all resources in this module."
11 | }
12 |
13 | variable "environment" {
14 | type = string
15 | description = "(Required) Environment of all resources"
16 | }
17 |
18 | variable "billing_code" {
19 | type = string
20 | description = "(Required) Billing code for network resources"
21 | }
22 |
23 | # Application variables
24 |
25 | variable "ip_range" {
26 | default = "0.0.0.0/0"
27 | }
28 |
29 | variable "instance_type" {
30 | type = string
31 | description = "(Optional) EC2 Instance type to use for web app. Defaults to t3.micro."
32 | default = "t3.micro"
33 | }
34 |
35 | variable "api_key_secret_id" {
36 | type = string
37 | description = "(Required) Secret ID for API key for web app to talk to SaaS platform."
38 | }
39 |
40 | variable "playbook_repository" {
41 | type = string
42 | description = "(Required) URL of Ansible playbook repository."
43 | }
44 |
45 | variable "ec2_role_name" {
46 | type = string
47 | description = "(Required) Name of role to associate with EC2 instance profile."
48 | }
49 |
50 | # TFC variables
51 | variable "tfe_organization" {
52 | type = string
53 | description = "(Required) Name of TFC organization."
54 | }
55 |
56 | variable "tfe_workspace_name" {
57 | type = string
58 | description = "(Required) Name of networking workspace to get information."
59 | }
60 |
--------------------------------------------------------------------------------
/m3/m3_commands.txt:
--------------------------------------------------------------------------------
1 | # Configure an AWS profile with proper credentials
2 | aws configure --profile deep-dive
3 |
4 | # Linux or MacOS
5 | export AWS_PROFILE=deep-dive
6 |
7 | # Windows
8 | $env:AWS_PROFILE="deep-dive"
9 |
10 | # Deploy the current environment using CloudFormation
11 | cd ./m3/cloud_formation_template
12 | aws cloudformation deploy --template-file="vpc_template.yaml" --stack-name dev-net --parameter-overrides EnvironmentName=globo-dev
13 | aws cloudformation describe-stacks --stack-name dev-net --query 'Stacks[0].Outputs[].[OutputKey, OutputValue]' --output table > table.txt
14 |
15 | # Copy the network config to the root directory
16 | cd ../.. # Move up to the root directory
17 |
18 | ## PowerShell
19 | copy .\m3\network_config_example\ .\network_config -Recurse
20 |
21 | ## Bash or zsh
22 | cp ./m3/network_config_example ./network_config
23 |
24 | # Retrieve the values to use in the import blocks
25 | # They are in the table.txt file in the cloud_formation_template directory
26 |
27 | # Now run a plan and see what happens
28 | cd ./network_config
29 | terraform init
30 | terraform plan
31 |
32 | # Looks like we need to create another object
33 | terraform plan -generate-config-out="generated.tf"
34 |
35 | # Update the VPC ID reference in the generated block to use the module VPC ID
36 | # Move the block to the resources file and delete the generated file
37 |
38 | # Run a plan again and note the imports and adds
39 |
40 | terraform plan
41 |
42 | # There should be 9 imports and 3 adds for default objects
43 |
44 | terraform apply
45 |
46 | # Add the environment input variable and tag
47 | # and update the tags on the security group
48 | terraform plan -out="tags.tfplan"
49 | terraform apply tags.tfplan
--------------------------------------------------------------------------------
/m5/m5_commands.txt:
--------------------------------------------------------------------------------
1 | # Start by setting the GitHub token environment variable
2 | # PowerShell
3 | $env:GITHUB_TOKEN="TOKEN_VALUE"
4 |
5 | # bash or zsh
6 | export GITHUB_TOKEN=TOKEN_VALUE
7 |
8 | # Head into the m5/github_config directory
9 | cd ./m5/github_config
10 |
11 | # initialize and apply the Terraform config
12 | terraform init
13 | terraform apply
14 |
15 | # Head into the network_config directory
16 | cd ../../network_config
17 |
18 | # Run git init
19 | git init --initial-branch=main
20 |
21 | # Copy the example gitignore file
22 | cp ../m5/gitignore.example .gitignore
23 |
24 | # Add files to git
25 | git add .
26 |
27 | # Commit files to git
28 | git commit -m "Initial commit"
29 |
30 | # Add remote origin
31 | git remote add origin ORIGIN_URL
32 |
33 | # Track main branch
34 | git fetch
35 | git branch --set-upstream-to="origin/main" main
36 |
37 | # Push code to GitHub
38 | git push origin --force
39 |
40 | # Create the github actions folders
41 | mkdir .github
42 | mkdir .github/workflows
43 |
44 | # Copy the terraform.yml file to the workflows folder
45 | cp ../m5/terraform.yml ./.github/workflows/
46 |
47 | # Rename the backend file to backend_local.tf
48 | mv backend.tf backend_local.tf
49 |
50 | # Add backend_local.tf to the .gitignore file
51 | # Add the changes to git
52 | git add .
53 |
54 | # Commit the changes
55 | git commit -m "Add CI workflow"
56 |
57 | # Push the changes to GitHub
58 | git push
59 |
60 | # Fix formatting and push updates
61 | terraform fmt
62 | git add .
63 | git commit -m "Fix formatting"
64 | git push
65 |
66 | # Setting up Terraform Cloud to GitHub connection
67 | # Go to the m5/terraform_cloud_config directory
68 | cd ../m5/terraform_cloud_config
69 | terraform init
70 |
71 | # Replace the organization name with your org name
72 | # PowerShell environment variable
73 | terraform apply -var="gh_pat=$env:GITHUB_TOKEN" -var="organization=ORG_NAME"
74 |
75 | # Bash or zsh environment variable
76 | terraform apply -var="gh_pat=$GITHUB_TOKEN" -var="organization=ORG_NAME"
77 |
78 | # Create a new branch called add-third-subnet
79 | git checkout -b add-third-subnet
80 |
81 | # Commit code changes to branch
82 | git add .
83 | git commit -m "Add third subnet and tags"
84 | git push --set-upstream origin add-third-subnet
85 |
86 | # Add variable values before creating pull request
--------------------------------------------------------------------------------
/m7/m7_commands.txt:
--------------------------------------------------------------------------------
1 | # Set AWS_PROFILE
2 |
3 | # Linux or MacOS
4 | export AWS_PROFILE=deep-dive
5 |
6 | # Windows
7 | $env:AWS_PROFILE="deep-dive"
8 |
9 | ## PowerShell
10 | copy .\m7\application_config_example\ .\application_config -Recurse
11 |
12 | ## Bash or zsh
13 | cp -r ./m7/application_config_example ./application_config
14 |
15 | ## Create Git Repository
16 | # Start by setting the GitHub token environment variable
17 | # PowerShell
18 | $env:GITHUB_TOKEN="TOKEN_VALUE"
19 |
20 | # bash or zsh
21 | export GITHUB_TOKEN=TOKEN_VALUE
22 |
23 | # Head into the m7/github_config_application directory
24 | cd ./m7/github_config_application
25 |
26 | # initialize and apply the Terraform config
27 | terraform init
28 | terraform apply
29 |
30 | # Head into the application_config directory
31 | cd ../../application_config
32 |
33 | # Run git init
34 | git init --initial-branch=main
35 |
36 | # Copy the example gitignore file
37 | cp ../m5/gitignore.example .gitignore
38 |
39 | # Add files to git
40 | git add .
41 |
42 | # Commit files to git
43 | git commit -m "Initial commit"
44 |
45 | # Add remote origin
46 | git remote add origin ORIGIN_URL
47 |
48 | # Track main branch
49 | git fetch
50 | git branch --set-upstream-to="origin/main" main
51 |
52 | # Push code to GitHub
53 | git push origin --force
54 |
55 | # Make sure checks complete
56 | # Add new workspace to Terraform Cloud linked to Repository
57 | # Add the necessary variable inputs using the portal or commands below
58 | aws ec2 describe-vpcs --region=us-east-1 --filters "Name=tag:Name, Values=globo-dev" --query Vpcs[].VpcId --output text
59 | aws ec2 describe-subnets --region=us-east-1 --filters "Name=vpc-id,Values=vpc-0fa3eb2daaeae0e29" --query Subnets[].SubnetId
60 |
61 | # Check config of first EC2 instance
62 | ssh -i PATH_TO_PEM_FILE ec2-user@PUBLIC_IP_ADDRESS
63 |
64 | # Switch to networking config
65 | cd ../network_config
66 |
67 | # Create a new branch called tfe-outputs
68 | git checkout main
69 | git pull
70 |
71 | git checkout -b tfe-outputs
72 |
73 | # Make code changes
74 | # Commit code changes to branch
75 | git add .
76 | git commit -m "Switch to using tfe_outputs"
77 | git push --set-upstream origin tfe-outputs
78 |
79 | # Go through the code deployment process
80 | # Update network workspace state sharing
81 |
82 | # Go to application config directory
83 | cd ../application_config
84 |
85 | # Create a new branch called tfe-outputs
86 | git checkout -b tfe-outputs
87 |
88 | # Make code changes
89 | # Commit code changes to branch
90 | git add .
91 | git commit -m "Switch to using tfe_outputs"
92 | git push --set-upstream origin tfe-outputs
93 |
94 | # Add new variable values to application workspace
95 | # Create pull request and merge once there are no changes
96 |
97 | # Merge the changes back to the main branch in GitHub too
98 |
--------------------------------------------------------------------------------
/m9/application_config_complete/resources.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # PROVIDERS
3 | ##################################################################################
4 |
5 | provider "aws" {
6 | region = var.region
7 | }
8 |
9 | ##################################################################################
10 | # LOCALS
11 | ##################################################################################
12 |
13 | locals {
14 |
15 | common_tags = {
16 | Environment = var.environment
17 | BillingCode = var.billing_code
18 | }
19 |
20 | name_prefix = "${var.prefix}-${var.environment}"
21 |
22 | }
23 |
24 | ##################################################################################
25 | # RESOURCES
26 | ##################################################################################
27 | resource "aws_iam_instance_profile" "main" {
28 | name = "${local.name_prefix}-webapp"
29 | role = var.ec2_role_name
30 |
31 | tags = local.common_tags
32 | }
33 |
34 | resource "aws_instance" "main" {
35 | count = length(data.tfe_outputs.networking.nonsensitive_values.public_subnets)
36 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value)
37 | instance_type = var.instance_type
38 | subnet_id = data.tfe_outputs.networking.nonsensitive_values.public_subnets[count.index]
39 | vpc_security_group_ids = [
40 | aws_security_group.webapp_http_inbound_sg.id,
41 | aws_security_group.webapp_ssh_inbound_sg.id,
42 | aws_security_group.webapp_outbound_sg.id,
43 | ]
44 |
45 | key_name = module.ssh_keys.key_pair_name
46 |
47 | tags = merge(local.common_tags, {
48 | "Name" = "${local.name_prefix}-webapp-${count.index}"
49 | })
50 |
51 | user_data = templatefile("${path.module}/templates/userdata.sh", {
52 | playbook_repository = var.playbook_repository
53 | secret_id = var.api_key_secret_id
54 | host_list_ssm_name = local.host_list_ssm_name
55 | site_name_ssm_name = local.site_name_ssm_name
56 | })
57 |
58 | user_data_replace_on_change = true
59 | iam_instance_profile = aws_iam_instance_profile.main.name
60 |
61 | }
62 |
63 | resource "aws_lb" "main" {
64 | name = "${local.name_prefix}-webapp"
65 | internal = false
66 | load_balancer_type = "application"
67 | security_groups = [aws_security_group.webapp_http_inbound_sg.id]
68 | subnets = data.tfe_outputs.networking.nonsensitive_values.public_subnets
69 |
70 | enable_deletion_protection = false
71 |
72 | tags = local.common_tags
73 | }
74 |
75 | resource "aws_lb_listener" "main" {
76 | load_balancer_arn = aws_lb.main.arn
77 | port = "80"
78 | protocol = "HTTP"
79 |
80 | default_action {
81 | type = "forward"
82 | target_group_arn = aws_lb_target_group.main.arn
83 | }
84 | }
85 |
86 | resource "aws_lb_target_group" "main" {
87 | name = "${local.name_prefix}-webapp"
88 | port = 80
89 | target_type = "instance"
90 | protocol = "HTTP"
91 | vpc_id = data.tfe_outputs.networking.nonsensitive_values.vpc_id
92 | }
93 |
94 | resource "aws_alb_target_group_attachment" "main" {
95 | count = length(aws_instance.main.*.id)
96 | target_group_arn = aws_lb_target_group.main.arn
97 | target_id = aws_instance.main[count.index].id
98 | }
--------------------------------------------------------------------------------
/m7/application_config_example/resources.tf:
--------------------------------------------------------------------------------
1 | ##################################################################################
2 | # PROVIDERS
3 | ##################################################################################
4 |
5 | provider "aws" {
6 | region = var.region
7 | }
8 |
9 | ##################################################################################
10 | # LOCALS
11 | ##################################################################################
12 |
13 | locals {
14 |
15 | common_tags = {
16 | Environment = var.environment
17 | BillingCode = var.billing_code
18 | }
19 |
20 | name_prefix = "${var.prefix}-${var.environment}"
21 |
22 | }
23 |
24 | ##################################################################################
25 | # RESOURCES
26 | ##################################################################################
27 |
28 | resource "aws_instance" "main" {
29 | count = length(var.public_subnets)
30 | ami = nonsensitive(data.aws_ssm_parameter.amzn2_linux.value)
31 | instance_type = var.instance_type
32 | subnet_id = var.public_subnets[count.index]
33 | vpc_security_group_ids = [
34 | aws_security_group.webapp_http_inbound_sg.id,
35 | aws_security_group.webapp_ssh_inbound_sg.id,
36 | aws_security_group.webapp_outbound_sg.id,
37 | ]
38 |
39 | key_name = module.ssh_keys.key_pair_name
40 |
41 | tags = merge(local.common_tags, {
42 | "Name" = "${local.name_prefix}-webapp-${count.index}"
43 | })
44 |
45 | # Provisioner Stuff
46 | connection {
47 | type = "ssh"
48 | user = "ec2-user"
49 | port = "22"
50 | host = self.public_ip
51 | private_key = module.ssh_keys.private_key_openssh
52 | }
53 |
54 | provisioner "file" {
55 | source = "./templates/userdata.sh"
56 | destination = "/home/ec2-user/userdata.sh"
57 | }
58 |
59 | provisioner "remote-exec" {
60 | inline = [
61 | "chmod +x /home/ec2-user/userdata.sh",
62 | "sh /home/ec2-user/userdata.sh",
63 | ]
64 | on_failure = continue
65 | }
66 |
67 | }
68 |
69 | resource "null_resource" "webapp" {
70 |
71 | triggers = {
72 | webapp_server_count = length(aws_instance.main.*.id)
73 | web_server_names = join(",", aws_instance.main.*.id)
74 | }
75 |
76 | provisioner "file" {
77 | content = templatefile("./templates/application.config.tpl", {
78 | hosts = aws_instance.main.*.private_dns
79 | site_name = "${local.name_prefix}-taco-wagon"
80 | api_key = var.api_key
81 | })
82 | destination = "/home/ec2-user/application.config"
83 | }
84 |
85 | connection {
86 | type = "ssh"
87 | user = "ec2-user"
88 | port = "22"
89 | host = aws_instance.main[0].public_ip
90 | private_key = module.ssh_keys.private_key_openssh
91 | }
92 |
93 | }
94 |
95 | resource "aws_lb" "main" {
96 | name = "${local.name_prefix}-webapp"
97 | internal = false
98 | load_balancer_type = "application"
99 | security_groups = [aws_security_group.webapp_http_inbound_sg.id]
100 | subnets = var.public_subnets
101 |
102 | enable_deletion_protection = false
103 |
104 | tags = local.common_tags
105 | }
106 |
107 | resource "aws_lb_listener" "main" {
108 | load_balancer_arn = aws_lb.main.arn
109 | port = "80"
110 | protocol = "HTTP"
111 |
112 | default_action {
113 | type = "forward"
114 | target_group_arn = aws_lb_target_group.main.arn
115 | }
116 | }
117 |
118 | resource "aws_lb_target_group" "main" {
119 | name = "${local.name_prefix}-webapp"
120 | port = 80
121 | target_type = "instance"
122 | protocol = "HTTP"
123 | vpc_id = var.vpc_id
124 | }
125 |
126 | resource "aws_alb_target_group_attachment" "main" {
127 | count = length(aws_instance.main.*.id)
128 | target_group_arn = aws_lb_target_group.main.arn
129 | target_id = aws_instance.main[count.index].id
130 | }
--------------------------------------------------------------------------------
/m3/cloud_formation_template/vpc_template.yaml:
--------------------------------------------------------------------------------
1 | Description: This template deploys a VPC, with a pair of public subnets spread
2 | across two Availability Zones. It deploys an internet gateway, with a default
3 | route on the public subnets.
4 |
5 | Parameters:
6 | EnvironmentName:
7 | Description: An environment name that is prefixed to resource names
8 | Type: String
9 |
10 | VpcCIDR:
11 | Description: Please enter the IP range (CIDR notation) for this VPC
12 | Type: String
13 | Default: 10.42.0.0/16
14 |
15 | PublicSubnet1CIDR:
16 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
17 | Type: String
18 | Default: 10.42.10.0/24
19 |
20 | PublicSubnet2CIDR:
21 | Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone
22 | Type: String
23 | Default: 10.42.11.0/24
24 |
25 | Resources:
26 | VPC:
27 | Type: AWS::EC2::VPC
28 | Properties:
29 | CidrBlock: !Ref VpcCIDR
30 | EnableDnsSupport: true
31 | EnableDnsHostnames: true
32 | Tags:
33 | - Key: Name
34 | Value: !Ref EnvironmentName
35 |
36 | InternetGateway:
37 | Type: AWS::EC2::InternetGateway
38 | Properties:
39 | Tags:
40 | - Key: Name
41 | Value: !Ref EnvironmentName
42 |
43 | InternetGatewayAttachment:
44 | Type: AWS::EC2::VPCGatewayAttachment
45 | Properties:
46 | InternetGatewayId: !Ref InternetGateway
47 | VpcId: !Ref VPC
48 |
49 | PublicSubnet1:
50 | Type: AWS::EC2::Subnet
51 | Properties:
52 | VpcId: !Ref VPC
53 | AvailabilityZone: !Select [ 0, !GetAZs '' ]
54 | CidrBlock: !Ref PublicSubnet1CIDR
55 | MapPublicIpOnLaunch: true
56 | Tags:
57 | - Key: Name
58 | Value: !Sub ${EnvironmentName}-public-1
59 |
60 | PublicSubnet2:
61 | Type: AWS::EC2::Subnet
62 | Properties:
63 | VpcId: !Ref VPC
64 | AvailabilityZone: !Select [ 1, !GetAZs '' ]
65 | CidrBlock: !Ref PublicSubnet2CIDR
66 | MapPublicIpOnLaunch: true
67 | Tags:
68 | - Key: Name
69 | Value: !Sub ${EnvironmentName}-public-2
70 |
71 | PublicRouteTable:
72 | Type: AWS::EC2::RouteTable
73 | Properties:
74 | VpcId: !Ref VPC
75 | Tags:
76 | - Key: Name
77 | Value: !Sub ${EnvironmentName}-public
78 |
79 | DefaultPublicRoute:
80 | Type: AWS::EC2::Route
81 | DependsOn: InternetGatewayAttachment
82 | Properties:
83 | RouteTableId: !Ref PublicRouteTable
84 | DestinationCidrBlock: 0.0.0.0/0
85 | GatewayId: !Ref InternetGateway
86 |
87 | PublicSubnet1RouteTableAssociation:
88 | Type: AWS::EC2::SubnetRouteTableAssociation
89 | Properties:
90 | RouteTableId: !Ref PublicRouteTable
91 | SubnetId: !Ref PublicSubnet1
92 |
93 | PublicSubnet2RouteTableAssociation:
94 | Type: AWS::EC2::SubnetRouteTableAssociation
95 | Properties:
96 | RouteTableId: !Ref PublicRouteTable
97 | SubnetId: !Ref PublicSubnet2
98 |
99 | NoIngressSecurityGroup:
100 | Type: AWS::EC2::SecurityGroup
101 | Properties:
102 | GroupName: "no-ingress-sg"
103 | GroupDescription: "Security group with no ingress rule"
104 | VpcId: !Ref VPC
105 |
106 | Outputs:
107 | VPC:
108 | Description: A reference to the created VPC
109 | Value: !Ref VPC
110 |
111 | PublicSubnet1:
112 | Description: A reference to the public subnet in the 1st Availability Zone
113 | Value: !Ref PublicSubnet1
114 |
115 | PublicSubnet2:
116 | Description: A reference to the public subnet in the 2nd Availability Zone
117 | Value: !Ref PublicSubnet2
118 |
119 | NoIngressSecurityGroup:
120 | Description: Security group with no ingress rule
121 | Value: !Ref NoIngressSecurityGroup
122 |
123 | InternetGateway:
124 | Description: InternetGateway reference
125 | Value: !Ref InternetGateway
126 |
127 | PublicRouteTable:
128 | Description: Route table for the public subnets
129 | Value: !Ref PublicRouteTable
130 |
131 | DefaultPublicRoute:
132 | Description: Default public route for public subnet route table
133 | Value: !Sub '${PublicRouteTable}_0.0.0.0/0'
134 |
135 | PublicSubnet1RouteTableAssociation:
136 | Description: Route table associate for first public subnet
137 | Value: !Sub '${PublicSubnet1}/${PublicRouteTable}'
138 |
139 | PublicSubnet2RouteTableAssociation:
140 | Description: Route table associate for second public subnet
141 | Value: !Sub '${PublicSubnet2}/${PublicRouteTable}'
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deep-Dive-Terraform
2 |
3 | Welcome to Terraform - Deep Dive version 3. These exercise files are meant to accompany my course on [Pluralsight](https://app.pluralsight.com/library/courses/terraform-deep-dive-2023). The course was developed using version 1.5.x of Terraform.
4 |
5 | If you're looking for the older versions of the course, that is still available on the v1 and v2 branches. I am no longer maintaining them, but I thought I would keep them around for posterity or if you're already working through those versions of the course.
6 |
7 | ## AWS Account
8 |
9 | You are going to need an account where you have FullAdmin permissions. You are going to be creating policies, roles, profiles, VPCs, etc. If you don't have enough permissions in your current environment, then I recommend creating a temporary account to mess around in. In fact, probably do that regardless. You don't want to accidentally mess something up at work because you were trying to learn about Terraform.
10 |
11 | You may exceed your EIP address quota when deploying multiple enviornments. You can request an increase through the AWS console in the Services Quotas area, under the Amazon Elastic Compute Cloud category. I recommend setting it to 15 just to be safe. It should be approved almost immediately, but may take 30 minutes to apply. So if you do it now, it should be ready long before you get to that portion of the course.
12 |
13 | ## Using the files
14 |
15 | Each folder represents a module from the course and they all build off of each other. You will be creating two working directories in your local copy of the exercise files: `network_config` and `application_config`. Both of these folders are part of the `.gitignore` so they will not be committed back to the repository.
16 |
17 | In the module folders, I have included Terraform configurations to help set up the necessary prerequisites. I've also included the commands you should run if you're following along. You don't have to run them verbatim, and I encourage you to mess around and try out different flags and commands. If you run into an issue, please submit it as such and I will do my best to remediate it.
18 |
19 | ## Line Endings
20 |
21 | 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.
22 |
23 | ## MONEY!!!
24 |
25 | 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. In most cases I have tried to use the [Free-tier](https://aws.amazon.com/free/) when possible, but AWS is about to start charging for IPv4 addresses even when they're attached to a resource. I'm not sure how this will impact the free tier, so just be cognizant of what you're creating.
26 |
27 | When you complete an exercise in the course, you can always run `terraform destroy` and approve the destruction to remove all resources from AWS. Once you get to the application deployment, you will need to leave the networking in place for a successful application deployment. If you wish to tear things down, destroy the application first and then the network.
28 |
29 | ## Terraform Cloud and GitHub Actions
30 |
31 | Previous versions of this course used Docker, Consul, and Jenkins to provide remote state and CI/CD operations. This ended up being a sticking point for several people, and they ended up abandoning the course. To simplify things, I have replaced those technologies with Terraform Cloud and GitHub Actions. This is not an overt endorsement of either technology, there are plenty of other great options when it comes to remote state storage and CI/CD for IaC. I chose GitHub Actions because we are already using GitHub any way, and I chose Terraform Cloud because it has a solid free tier and it is part of the Terraform Associate certification. Speaking of which...
32 |
33 | ## Certification
34 |
35 | 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 [Terraform - Getting Started](https://app.pluralsight.com/library/courses/terraform-getting-started-2023) course on Pluralsight will meet all of the learning objectives for the certification, but there is no substitute for running the software on your own and hacking away.
36 |
37 | I have coauthored a certification guide with Adin Ermie 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.
38 |
39 | ## Conclusion
40 |
41 | I hope you enjoy taking this course as much as I did creating it. I'd love to hear feedback and suggestions for revisions.
42 |
43 | Thanks and happy automating!
44 |
45 | Ned
46 |
--------------------------------------------------------------------------------