SlideShare a Scribd company logo
1 of 111
Download to read offline
Terraform at Scale
◉ Introduction
◉ Terraform at Scale?
◉ Gotta Keep ‘em Separated
◉ Reducing Complexity With Style
◉ A Tale of Two Modules
Introduction: Agenda and Takeaways
80%
Percentage of Outages Caused by Changes*
* Source: Behr, K., Kim, G., & Spafford, G. (2013). The Visible Ops Handbook
Post-fordism, cognitive-cultural economy derived from spontaneous order.
Simple and Powerful
Source: terraform.io
Terraform Providers
Terraform Providers
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
Terraform at Scale?
This is fine.
Gotta Keep ‘em Separated
Dive In: Code Samples
AWS Provider
provider.tf
provider "aws" {
region = "us-east-1"
}
AWS Route53 Provisioners
route53.tf
resource "aws_route53_zone" "route53_zone" {
name = "devops-demo.xyz"
}
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "dev-api.devops-demo.xyz"
type = "A"
ttl = "300"
records = ["86.75.30.9"]
}
AWS Route53 Provisioners
route53.tf
resource "aws_route53_zone" "route53_zone" {
name = "devops-demo.xyz"
}
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "dev-api.devops-demo.xyz"
type = "A"
ttl = "300"
records = ["86.75.30.9"]
}
route53.tf
resource "aws_route53_zone" "route53_zone" {
name = "devops-demo.xyz"
}
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "dev-api.devops-demo.xyz"
type = "A"
ttl = "300"
records = ["86.75.30.9"]
}
AWS Route53 Provisioners
route53.tf
resource "aws_route53_zone" "route53_zone" {
name = "devops-demo.xyz"
}
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "dev-api.devops-demo.xyz"
type = "A"
ttl = "300"
records = ["86.75.30.9"]
}
AWS Route53 Provisioners
route53.tf
resource "aws_route53_zone" "route53_zone" {
name = "devops-demo.xyz"
}
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "dev-api.devops-demo.xyz"
type = "A"
ttl = "300"
records = ["86.75.30.9"]
}
AWS Route53 Provisioners
terraform plan (console output)
+ aws_route53_record.api_route53_record
fqdn: "<computed>"
name: "dev-api.devops-demo.xyz"
records.#: "1"
records.4228697306: "86.75.30.9"
ttl: "300"
type: "A"
zone_id: "${aws_route53_zone.route53_zone.zone_id}"
+ aws_route53_zone.route53_zone
comment: "Managed by Terraform"
force_destroy: "false"
name: "devops-demo.xyz"
name_servers.#: "<computed>"
vpc_region: "<computed>"
zone_id: "<computed>"
Plan: 2 to add, 0 to change, 0 to destroy.
Root Module Example
providers.tf
route53.tf
Our First Module v1
outputs.tf
providers.tf
route53.tf
variables.tf
Congratulations On Your WET Module!
Image Source: “Campaign Shake-Up” Parks and Recreation, season 4, episode 17, NBC, 01 Mar. 2012
Parameterize To Stay DRY
provider.tf
provider "aws" {
region = "us-east-1"
}
route53.tf
resource "aws_route53_zone" "route53_zone" {
name = "devops-demo.xyz"
}
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "dev-api.devops-demo.xyz"
type = "A"
ttl = "300"
records = ["86.75.30.9"]
}
Input Variables
provider.tf
provider "aws" {
region = "${var.aws_region}"
}
route53.tf
resource "aws_route53_zone" "route53_zone" {
name = "${var.domain_name}"
}
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "${var.environment}-api.${var.domain_name}"
type = "A"
ttl = "300"
records = ["${var.ip_address}”]
}
variables.tf
variable "aws_account" {
default = "sysadvent-production"
}
variable "aws_region" {
default = "us-east-1"
}
variable "domain_name" {
default = "devops-demo.xyz"
}
variable "environment" {
default = "dev"
}
variable "ip_address" {
default = "86.75.30.9"
}
terraform apply (console output)
aws_route53_zone.route53_zone: Creating...
comment: "" => "Managed by Terraform"
force_destroy: "" => "false"
name: "" => "devops-demo.xyz"
name_servers.#: "" => "<computed>"
vpc_region: "" => "<computed>"
zone_id: "" => "<computed>"
aws_route53_zone.route53_zone: Creation complete (ID: Z3CIMZCT6RGERD)
aws_route53_record.api_route53_record: Creating...
fqdn: "" => "<computed>"
name: "" => "dev-api.devops-demo.xyz"
records.#: "" => "1"
records.4228697306: "" => "86.75.30.9"
ttl: "" => "300"
type: "" => "A"
zone_id: "" => "Z3QZEABS9HJLIN"
aws_route53_record.api_route53_record: Creation complete (ID: Z3CIMZCT6RGERD_api.devops-demo.xyz_A)
...
terraform apply (console output cont.)
...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.
State path:
Outputs:
api_fqdn = dev-api.devops-demo.xyz
domain_name = devops-demo.xyz
zone_id = Z3QZEABS9HJLIN
terraform apply (console output cont.)
...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.
State path:
Outputs:
api_fqdn = dev-api.devops-demo.xyz
domain_name = devops-demo.xyz
zone_id = Z3QZEABS9HJLIN
outputs.tf
output "api_fqdn" {
value = "${aws_route53_record.api_route53_record.fqdn}"
}
output "domain_name" {
value = "${var.domain_name}"
}
output "zone_id" {
value = "${aws_route53_zone.route53_zone.id}"
}
Output Variables
outputs.tf
output "api_fqdn" {
value = "${aws_route53_record.api_route53_record.fqdn}"
}
output "domain_name" {
value = "${var.domain_name}"
}
output "zone_id" {
value = "${aws_route53_zone.route53_zone.id}"
}
Output Variables
outputs.tf
output "api_fqdn" {
value = "${aws_route53_record.api_route53_record.fqdn}"
}
output "domain_name" {
value = "${var.domain_name}"
}
output "zone_id" {
value = "${aws_route53_zone.route53_zone.id}"
}
Output Variables
Root Module Example
outputs.tf
providers.tf
route53.tf
variables.tf
Our First Module v2
Reducing Complexity
With Style
eip.tf
internet_gateway.tf
nat_gateway.tf
outputs.tf
providers.tf
route.tf
subnets.tf
variables.tf
vpc.tf
Naming Conventions: File Names
Naming Conventions: Resource Names
◉ RESOURCE NAME = RESOURCE TYPE - PROVIDER NAME
resource "aws_security_group" "security_group" {
name = "${var.resource_name}-security-group"
...
Naming Conventions: Resource Names (cont.)
resource "aws_s3_bucket" "data_s3_bucket" {
bucket = "${var.env}-data-${var.aws_region}"
}
resource "aws_s3_bucket" "images_s3_bucket" {
bucket = "${var.env}-images-${var.aws_region}"
}
◉ Multiple resources of the same TYPE should have a minimalistic identifier to
differentiate between the two resources.
When to Use an Underscore
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "${var.environment}-api.${var.domain_name}"
type = "A"
ttl = "300"
records = ["${var.ip_address}"]
}
◉ Variable Names
◉ Resource Names
◉ Anything Interpolated
When to Use a Hyphen
resource "aws_route53_record" "api_route53_record" {
zone_id = "${aws_route53_zone.route53_zone.zone_id}"
name = "${var.environment}-api.${var.domain_name}"
type = "A"
ttl = "300"
records = ["${var.ip_address}"]
}
◉ Resources Being Created
Source: terraform.io
Source: terraform.io
A Tale of Two Modules
Service Module
● Reusable library
● Creates all required resources a service needs to be operational i.e. EC2
instances, S3 bucket, DNS Entries
A Tale of Two Modules
Infrastructure Module
● Single repository comprised of multiple root modules
● This is where Service Modules are instantiated/Terraform is run.
Why Infrastructure Modules?
◉ Conditional Statements
○ Don’t exist Are painful (1604)
◉ Segment State
○ Reduce risk of changes
○ Flexible state reference
◉ DRY Terraform Configurations
○ Instantiate reusable modules
◉ Environments Aren’t Recommended (0.9)
◉ Workspaces are a thing (renamed statefile)
The Candle Problem
Source: http://whatismotivation.weebly.com/uploads/2/9/9/1/29913749/1713129_orig.png
Overcome Functional Fixedness
Source: http://whatismotivation.weebly.com/uploads/2/9/9/1/29913749/1713129_orig.png
https://kvaes.wordpress.com/2013/06/05/lingo-explained-greenfield-vs-brownfield/
Terraform: Configuration by Convention
Configuration by Convention
Infrastructure Repository Folder Structure
AWS (provider)
|__ production-account (aws-account root module)
|__ us-east-1 (aws-region root module)
|__ production-us-east-1-vpc (vpc root module)
|__ production (environment root module)
|__ inf-bastion (service root module)
Account Root Module
Infrastructure Repository Folder Structure
AWS (provider)
|__ production-account (aws-account root module) <~~~~~ YOU ARE HERE
|__ us-east-1 (aws-region root module)
|__ production-us-east-1-vpc (vpc root module)
|__ production (environment root module)
|__ inf-bastion (service root module)
terraform.sh (bash wrapper)
Usage: ./templates/account-terraform.sh [apply|destroy|plan|refresh|show]
The following arguments are supported:
apply Refresh the Terraform remote state, "terraform get -update", and "terraform apply"
destroy Refresh the Terraform remote state and destroy the Terraform stack
plan Refresh the Terraform remote state, "terraform get -update", and "terraform plan"
refresh Refresh the Terraform remote state
show Refresh and show the Terraform remote state
Refresh Function
...
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-1)}')
aws_account=$(pwd | awk -F "/" '{print $NF}')
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${root}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
...
Refresh Function
...
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-1)}')
aws_account=$(pwd | awk -F "/" '{print $NF}')
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${root}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
...
Refresh Function
...
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-1)}')
aws_account=$(pwd | awk -F "/" '{print $NF}')
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${root}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
...
...
createBackendConfig() {
/bin/cat > backend.tf <<EOL
terraform {
backend "s3" {}
}
EOL
}
...
Create Backend Config
backend.tf
terraform {
backend "s3" {}
}
Account Root Module
backend.tf
outputs.tf
providers.tf
terraform.sh
route53.tf
variables.tf
Our First Module v3
AWS-Region Root Module
Infrastructure Repository Folder Structure
AWS (provider)
|__ production-account (aws-account root module)
|__ us-east-1 (aws-region root module) <~~~~~ PLACEHOLDER
|__ production-us-east-1-vpc (vpc root module)
|__ production (environment root module)
|__ inf-bastion (service root module)
VPC Root Module
Infrastructure Repository Folder Structure
AWS (provider)
|__ production-account (aws-account root module)
|__ us-east-1 (aws-region root module)
|__ production-us-east-1-vpc (vpc root module) <~~~~~ YOU ARE HERE
|__ production (environment root module)
|__ inf-bastion (service root module)
VPC Root Module
backend.tf
main.tf
outputs.tf
terraform.sh
variables.tf
VPC Root Module (cont.)
main.tf
module "vpc" {
source = "git@github.com:TerraformDesignPattern/vpc.git?ref=1.2.3"
availability_zones = "${var.availability_zones}"
aws_region = "${var.aws_region}"
private_subnets = "${var.private_subnets}"
public_subnets = "${var.public_subnets}"
vpc_cidr = "${var.vpc_cidr}"
vpc_name = "${var.vpc_name}"
}
VPC Root Module (cont.)
main.tf
module "vpc" {
source = "git@github.com:TerraformDesignPattern/vpc.git?ref=1.2.3"
availability_zones = "${var.availability_zones}"
aws_region = "${var.aws_region}"
private_subnets = "${var.private_subnets}"
public_subnets = "${var.public_subnets}"
vpc_cidr = "${var.vpc_cidr}"
vpc_name = "${var.vpc_name}"
}
VPC Root Module (cont.)
main.tf
module "vpc" {
source = "git@github.com:TerraformDesignPattern/vpc.git?ref=${var.branch}"
availability_zones = "${var.availability_zones}"
aws_region = "${var.aws_region}"
private_subnets = "${var.private_subnets}"
public_subnets = "${var.public_subnets}"
vpc_cidr = "${var.vpc_cidr}"
vpc_name = "${var.vpc_name}"
}
VPC Root Module (cont.)
NOPE.
main.tf
module "vpc" {
source = "git@github.com:TerraformDesignPattern/vpc.git?ref=${var.branch}"
availability_zones = "${var.availability_zones}"
aws_region = "${var.aws_region}"
private_subnets = "${var.private_subnets}"
public_subnets = "${var.public_subnets}"
vpc_cidr = "${var.vpc_cidr}"
vpc_name = "${var.vpc_name}"
}
VPC Root Module (cont.)
No interpolation allowed!
Issue #1439
main.tf
module "vpc" {
source = "git@github.com:TerraformDesignPattern/vpc.git?ref=${var.branch}"
availability_zones = "${var.availability_zones}"
aws_region = "${var.aws_region}"
private_subnets = "${var.private_subnets}"
public_subnets = "${var.public_subnets}"
vpc_cidr = "${var.vpc_cidr}"
vpc_name = "${var.vpc_name}"
}
VPC Root Module (cont.)
No interpolation allowed!
Issue #1439
NOPE.
Soon.
Maybe?
main.tf
module "vpc" {
source = "git@github.com:TerraformDesignPattern/vpc.git?ref=1.2.3"
availability_zones = "${var.availability_zones}"
aws_region = "${var.aws_region}"
private_subnets = "${var.private_subnets}"
public_subnets = "${var.public_subnets}"
vpc_cidr = "${var.vpc_cidr}"
vpc_name = "${var.vpc_name}"
}
VPC Root Module (cont.)
VPC Root Module
backend.tf
main.tf
outputs.tf
terraform.sh
variables.tf
VPC Root Module (cont.)
variables.tf
...
variable "public_subnets" {
default = [
"172.19.101.0/24",
"172.19.102.0/24",
"172.19.103.0/24",
]
}
...
VPC Root Module (cont.)
VPC Module Repository
eip.tf
internet_gateway.tf
nat_gateway.tf
outputs.tf
providers.tf
route.tf
subnets.tf
variables.tf
vpc.tf
VPC Service Module
outputs.tf
...
output "public_subnet_ids" {
value = ["${aws_subnet.public_subnet.*.id}"]
}
...
VPC Service Module Outputs
outputs.tf
...
output "public_subnet_ids" {
value = ["${module.vpc.public_subnet_ids}"]
}
...
VPC Root Module Outputs
To Begin Again... From The Beginning
Quote: Waking Life. Dir. Richard Linklater. Fox Searchlight Pictures, 2001. FIlm.
Image: Fight Club. Dir. David Fincher. 20th Century Fox, 1999. Film.
s3.tf
resource "aws_s3_bucket_object" "outputs_object" {
bucket = "${var.aws_account}-terraform-state"
key =
"aws/${var.aws_region}/${var.vpc_name}/dummy_object_outputs"
source = "outputs.tf"
etag = "${md5(file("outputs.tf"))}"
}
resource "aws_s3_bucket_object" "variables_object" {
bucket = "${var.aws_account}-terraform-state"
key =
"aws/${var.aws_region}/${var.vpc_name}/dummy_object_variables"
source = "variables.tf"
etag = "${md5(file("variables.tf"))}"
}
Root Module State Seeding
outputs.tf
output "public_subnet_ids" {
value = ["${var.public_subnets}"]
}
...
variables.tf
variable "public_subnets" {
default = [
"172.19.101.0/24",
"172.19.102.0/24",
"172.19.103.0/24",
]
}
...
Environment Root Module
Infrastructure Repository Folder Structure
AWS (provider)
|__ production-account (aws-account root module)
|__ us-east-1 (aws-region root module)
|__ production-us-east-1-vpc (vpc root module)
|__ production (environment root module) <~~~~~ PLACEHOLDER
|__ inf-bastion (service root module)
http://kristinvogel.edublogs.org/files/2014/04/home-stretch-ouwf9k.jpg
Service Root Module
Infrastructure Repository Folder Structure
AWS (provider)
|__ production-account (aws-account root module)
|__ us-east-1 (aws-region root module)
|__ production-us-east-1-vpc (vpc root module)
|__ production (environment root module)
|__ inf-bastion (service root module) <~~~~~ YOU ARE HERE
SSH Bastion Root Module
backend.tf
main.tf
outputs.tf
terraform.sh
variables.tf
SSH Bastion Root Module
variables.tf
variable "aws_account" {}
variable "aws_region" {}
variable "aws_environment_name" {}
variable "service_name" {}
variable "vpc_name" {}
SSH Bastion Root Module (cont.)
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-5)}')
aws_account=$(pwd | awk -F "/" '{print $(NF-4)}')
aws_region=$(pwd | awk -F "/" '{print $(NF-3)}')
vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}')
environment_name=$(pwd | awk -F "/" '{print $(NF-1)}')
service_name=$(pwd | awk -F "/" '{print $NF}')
bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}"
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_VAR_aws_region="$aws_region"
export TF_VAR_vpc_name="$vpc_name"
export TF_VAR_environment_name="$environment_name"
export TF_VAR_service_name="$service_name"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${bucket_key}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
Refresh Function
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-5)}')
aws_account=$(pwd | awk -F "/" '{print $(NF-4)}')
aws_region=$(pwd | awk -F "/" '{print $(NF-3)}')
vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}')
environment_name=$(pwd | awk -F "/" '{print $(NF-1)}')
service_name=$(pwd | awk -F "/" '{print $NF}')
bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}"
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_VAR_aws_region="$aws_region"
export TF_VAR_vpc_name="$vpc_name"
export TF_VAR_environment_name="$environment_name"
export TF_VAR_service_name="$service_name"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${bucket_key}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
Refresh Function
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-5)}')
aws_account=$(pwd | awk -F "/" '{print $(NF-4)}')
aws_region=$(pwd | awk -F "/" '{print $(NF-3)}')
vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}')
environment_name=$(pwd | awk -F "/" '{print $(NF-1)}')
service_name=$(pwd | awk -F "/" '{print $NF}')
bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}"
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_VAR_aws_region="$aws_region"
export TF_VAR_vpc_name="$vpc_name"
export TF_VAR_environment_name="$environment_name"
export TF_VAR_service_name="$service_name"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${bucket_key}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
Refresh Function
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-5)}')
aws_account=$(pwd | awk -F "/" '{print $(NF-4)}')
aws_region=$(pwd | awk -F "/" '{print $(NF-3)}')
vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}')
environment_name=$(pwd | awk -F "/" '{print $(NF-1)}')
service_name=$(pwd | awk -F "/" '{print $NF}')
bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}"
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_VAR_aws_region="$aws_region"
export TF_VAR_vpc_name="$vpc_name"
export TF_VAR_environment_name="$environment_name"
export TF_VAR_service_name="$service_name"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${bucket_key}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
Refresh Function
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-5)}')
aws_account=$(pwd | awk -F "/" '{print $(NF-4)}')
aws_region=$(pwd | awk -F "/" '{print $(NF-3)}')
vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}')
environment_name=$(pwd | awk -F "/" '{print $(NF-1)}')
service_name=$(pwd | awk -F "/" '{print $NF}')
bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}"
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_VAR_aws_region="$aws_region"
export TF_VAR_vpc_name="$vpc_name"
export TF_VAR_environment_name="$environment_name"
export TF_VAR_service_name="$service_name"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${bucket_key}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
Refresh Function
refresh() {
root=$(pwd | awk -F "/" '{print $(NF-5)}')
aws_account=$(pwd | awk -F "/" '{print $(NF-4)}')
aws_region=$(pwd | awk -F "/" '{print $(NF-3)}')
vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}')
environment_name=$(pwd | awk -F "/" '{print $(NF-1)}')
service_name=$(pwd | awk -F "/" '{print $NF}')
bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}"
export TF_VAR_root="$root"
export TF_VAR_aws_account="$aws_account"
export TF_VAR_aws_region="$aws_region"
export TF_VAR_vpc_name="$vpc_name"
export TF_VAR_environment_name="$environment_name"
export TF_VAR_service_name="$service_name"
export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache"
echo -e "nn***** Refreshing State and Upgrading Modules *****"
echo "no" | terraform init -get=true 
-upgrade 
-input=false 
-backend=true 
-backend-config "bucket=${aws_account}-terraform-state" 
-backend-config "key=${bucket_key}/terraform.tfstate" 
-backend-config "profile=${aws_account}" 
-backend-config "region=us-east-1"
}
Refresh Function
module “bastion" {
source = "git@github.com:TerraformDesignPattern/bastionhost.git?ref=0.1.0"
aws_account = "${var.aws_account}"
aws_region = "${var.aws_region}"
environment_name = "${var.environment_name}"
vpc_name = "${var.vpc_name}"
service_name = "${element(split("-", var.service_name), 0)}"
tier_name = "${element(split("-", var.service_name), 1)}"
}
SSH Bastion Root Module (cont.)
SSH Bastion Module Repository
data.tf
ec2.tf
outputs.tf
providers.tf
route53.tf
security_groups.tf
variables.tf
SSH Bastion Service Module
data.tf
data "terraform_remote_state" "account" {
backend = "s3"
config {
bucket = "${var.aws_account}-terraform-state"
key = "aws/terraform.tfstate"
region = "us-east-1"
}
}
data "terraform_remote_state" "vpc" {
backend = "s3"
config {
bucket = "${var.aws_account}-terraform-state"
key = "aws/${var.aws_region}/${var.vpc_name}/terraform.tfstate"
region = "us-east-1"
}
}
route53.tf
resource "aws_route53_record" "route53_record" {
zone_id = "${data.terraform_remote_state.account.zone_id}"
name = "${local.hostname}.${data.terraform_remote_state.account.domain_name}"
type = "A"
ttl = "300"
records = ["${aws_instance.instance.public_ip}"]
}
...
security_groups.tf
resource "aws_security_group" "elb_security_group" {
name = "${local.resource_name}-elb-sg-${data.terraform_remote_state.vpc.aws_region_shortname}"
vpc_id = "${data.terraform_remote_state.vpc.vpc_id}"
...
route53.tf
resource "aws_route53_record" "route53_record" {
zone_id = "${data.terraform_remote_state.account.zone_id}"
name = "${local.hostname}.${data.terraform_remote_state.account.domain_name}"
type = "A"
ttl = "300"
records = ["${aws_instance.instance.public_ip}"]
}
...
security_groups.tf
resource "aws_security_group" "elb_security_group" {
name = "${local.resource_name}-elb-sg-${data.terraform_remote_state.vpc.aws_region_shortname}"
vpc_id = "${data.terraform_remote_state.vpc.vpc_id}"
...
variables.tf
locals {
hostname = "${var.environment_name}-${var.service_name}-${var.tier}-${data.terraform_remote_state.vpc.aws_region_shortname}"
resource_name = "${var.environment_name}-${var.service_name}-${var.tier}"
}
...
Module “Local” Variables
Manage State & Variables Dynamically
Infrastructure Repository Folder Structure
AWS (provider)
|__ production-account (root aws-account module)
|__ us-east-1 (root aws-region module)
|__ production-us-east-1-vpc (root vpc module)
|__ production (root environment module)
|__ bastion (root service module)
◉ Introduction
◉ Terraform at Scale?
◉ Gotta Keep ‘em Separated
◉ Reducing Complexity With Style
◉ A Tale of Two Modules
Closing: Agenda and Takeaways
Contact:
◉ Twitter: @jonbrouse
◉ Github: github.com/jonbrouse
◉ Site: jonbrouse.com
◉ github.com/TerraformDesignPattern
Thank you!
Jon Brouse

More Related Content

What's hot

Terraform Introduction
Terraform IntroductionTerraform Introduction
Terraform Introductionsoniasnowfrog
 
Comprehensive Terraform Training
Comprehensive Terraform TrainingComprehensive Terraform Training
Comprehensive Terraform TrainingYevgeniy Brikman
 
Terraform modules restructured
Terraform modules restructuredTerraform modules restructured
Terraform modules restructuredAmi Mahloof
 
Introductory Overview to Managing AWS with Terraform
Introductory Overview to Managing AWS with TerraformIntroductory Overview to Managing AWS with Terraform
Introductory Overview to Managing AWS with TerraformMichael Heyns
 
Terraform Abstractions for Safety and Power
Terraform Abstractions for Safety and PowerTerraform Abstractions for Safety and Power
Terraform Abstractions for Safety and PowerCalvin French-Owen
 
"Continuously delivering infrastructure using Terraform and Packer" training ...
"Continuously delivering infrastructure using Terraform and Packer" training ..."Continuously delivering infrastructure using Terraform and Packer" training ...
"Continuously delivering infrastructure using Terraform and Packer" training ...Anton Babenko
 
Infrastructure as Code with Terraform
Infrastructure as Code with TerraformInfrastructure as Code with Terraform
Infrastructure as Code with TerraformTim Berry
 
Scaling terraform
Scaling terraformScaling terraform
Scaling terraformPaolo Tonin
 
Terraform 0.9 + good practices
Terraform 0.9 + good practicesTerraform 0.9 + good practices
Terraform 0.9 + good practicesRadek Simko
 
Hashiconf EU 2019 - A Tour of Terraform 0.12
Hashiconf EU 2019 - A Tour of Terraform 0.12Hashiconf EU 2019 - A Tour of Terraform 0.12
Hashiconf EU 2019 - A Tour of Terraform 0.12Mitchell Pronschinske
 
Terraform introduction
Terraform introductionTerraform introduction
Terraform introductionJason Vance
 
Terraform Modules and Continuous Deployment
Terraform Modules and Continuous DeploymentTerraform Modules and Continuous Deployment
Terraform Modules and Continuous DeploymentZane Williamson
 
Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021TomStraub5
 
Infrastructure as Code: Introduction to Terraform
Infrastructure as Code: Introduction to TerraformInfrastructure as Code: Introduction to Terraform
Infrastructure as Code: Introduction to TerraformAlexander Popov
 
How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...Yevgeniy Brikman
 

What's hot (20)

Terraform Introduction
Terraform IntroductionTerraform Introduction
Terraform Introduction
 
Comprehensive Terraform Training
Comprehensive Terraform TrainingComprehensive Terraform Training
Comprehensive Terraform Training
 
Refactoring terraform
Refactoring terraformRefactoring terraform
Refactoring terraform
 
Terraform modules restructured
Terraform modules restructuredTerraform modules restructured
Terraform modules restructured
 
Introductory Overview to Managing AWS with Terraform
Introductory Overview to Managing AWS with TerraformIntroductory Overview to Managing AWS with Terraform
Introductory Overview to Managing AWS with Terraform
 
Terraform Abstractions for Safety and Power
Terraform Abstractions for Safety and PowerTerraform Abstractions for Safety and Power
Terraform Abstractions for Safety and Power
 
"Continuously delivering infrastructure using Terraform and Packer" training ...
"Continuously delivering infrastructure using Terraform and Packer" training ..."Continuously delivering infrastructure using Terraform and Packer" training ...
"Continuously delivering infrastructure using Terraform and Packer" training ...
 
Terraform at Scale
Terraform at ScaleTerraform at Scale
Terraform at Scale
 
Infrastructure as Code with Terraform
Infrastructure as Code with TerraformInfrastructure as Code with Terraform
Infrastructure as Code with Terraform
 
Intro to Terraform
Intro to TerraformIntro to Terraform
Intro to Terraform
 
Scaling terraform
Scaling terraformScaling terraform
Scaling terraform
 
Terraform 0.9 + good practices
Terraform 0.9 + good practicesTerraform 0.9 + good practices
Terraform 0.9 + good practices
 
Final terraform
Final terraformFinal terraform
Final terraform
 
Hashiconf EU 2019 - A Tour of Terraform 0.12
Hashiconf EU 2019 - A Tour of Terraform 0.12Hashiconf EU 2019 - A Tour of Terraform 0.12
Hashiconf EU 2019 - A Tour of Terraform 0.12
 
Terraform introduction
Terraform introductionTerraform introduction
Terraform introduction
 
Terraform Modules and Continuous Deployment
Terraform Modules and Continuous DeploymentTerraform Modules and Continuous Deployment
Terraform Modules and Continuous Deployment
 
Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021
 
Infrastructure as Code: Introduction to Terraform
Infrastructure as Code: Introduction to TerraformInfrastructure as Code: Introduction to Terraform
Infrastructure as Code: Introduction to Terraform
 
How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...How to test infrastructure code: automated testing for Terraform, Kubernetes,...
How to test infrastructure code: automated testing for Terraform, Kubernetes,...
 
Terraform day02
Terraform day02Terraform day02
Terraform day02
 

Similar to Terraform at Scale: Reducing Complexity With Style

Debasihish da final.ppt
Debasihish da final.pptDebasihish da final.ppt
Debasihish da final.pptKalkey
 
Terraform infraestructura como código
Terraform infraestructura como códigoTerraform infraestructura como código
Terraform infraestructura como códigoVictor Adsuar
 
leboncoin DataEngineering / Terraform - beginner to advanced
leboncoin DataEngineering / Terraform - beginner to advancedleboncoin DataEngineering / Terraform - beginner to advanced
leboncoin DataEngineering / Terraform - beginner to advancedleboncoin engineering
 
Atmosphere Conference 2015: Taming the Modern Datacenter
Atmosphere Conference 2015: Taming the Modern DatacenterAtmosphere Conference 2015: Taming the Modern Datacenter
Atmosphere Conference 2015: Taming the Modern DatacenterPROIDEA
 
Hive dirty/beautiful hacks in TD
Hive dirty/beautiful hacks in TDHive dirty/beautiful hacks in TD
Hive dirty/beautiful hacks in TDSATOSHI TAGOMORI
 
Meetup bangalore aug31st2019
Meetup bangalore aug31st2019Meetup bangalore aug31st2019
Meetup bangalore aug31st2019D.Rajesh Kumar
 
Declarative Infrastructure Tools
Declarative Infrastructure Tools Declarative Infrastructure Tools
Declarative Infrastructure Tools Yulia Shcherbachova
 
Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly -Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly -Giulio Vian
 
Quick trip around the Cosmos - Things every astronaut supposed to know
Quick trip around the Cosmos - Things every astronaut supposed to knowQuick trip around the Cosmos - Things every astronaut supposed to know
Quick trip around the Cosmos - Things every astronaut supposed to knowRafał Hryniewski
 
OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...
OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...
OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...NETWAYS
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesLindsay Holmwood
 
Presto anatomy
Presto anatomyPresto anatomy
Presto anatomyDongmin Yu
 
Introduction to cloudforecast
Introduction to cloudforecastIntroduction to cloudforecast
Introduction to cloudforecastMasahiro Nagano
 
Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly - Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly - Giulio Vian
 
Let your DBAs get some REST(api)
Let your DBAs get some REST(api)Let your DBAs get some REST(api)
Let your DBAs get some REST(api)Ludovico Caldara
 
Terraform Immutablish Infrastructure with Consul-Template
Terraform Immutablish Infrastructure with Consul-TemplateTerraform Immutablish Infrastructure with Consul-Template
Terraform Immutablish Infrastructure with Consul-TemplateZane Williamson
 
Systems Bioinformatics Workshop Keynote
Systems Bioinformatics Workshop KeynoteSystems Bioinformatics Workshop Keynote
Systems Bioinformatics Workshop KeynoteDeepak Singh
 

Similar to Terraform at Scale: Reducing Complexity With Style (20)

London HUG 12/4
London HUG 12/4London HUG 12/4
London HUG 12/4
 
Debasihish da final.ppt
Debasihish da final.pptDebasihish da final.ppt
Debasihish da final.ppt
 
Terraform infraestructura como código
Terraform infraestructura como códigoTerraform infraestructura como código
Terraform infraestructura como código
 
Terraform in action
Terraform in actionTerraform in action
Terraform in action
 
leboncoin DataEngineering / Terraform - beginner to advanced
leboncoin DataEngineering / Terraform - beginner to advancedleboncoin DataEngineering / Terraform - beginner to advanced
leboncoin DataEngineering / Terraform - beginner to advanced
 
Atmosphere Conference 2015: Taming the Modern Datacenter
Atmosphere Conference 2015: Taming the Modern DatacenterAtmosphere Conference 2015: Taming the Modern Datacenter
Atmosphere Conference 2015: Taming the Modern Datacenter
 
Hive dirty/beautiful hacks in TD
Hive dirty/beautiful hacks in TDHive dirty/beautiful hacks in TD
Hive dirty/beautiful hacks in TD
 
Meetup bangalore aug31st2019
Meetup bangalore aug31st2019Meetup bangalore aug31st2019
Meetup bangalore aug31st2019
 
Declarative Infrastructure Tools
Declarative Infrastructure Tools Declarative Infrastructure Tools
Declarative Infrastructure Tools
 
Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly -Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly -
 
Quick trip around the Cosmos - Things every astronaut supposed to know
Quick trip around the Cosmos - Things every astronaut supposed to knowQuick trip around the Cosmos - Things every astronaut supposed to know
Quick trip around the Cosmos - Things every astronaut supposed to know
 
OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...
OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...
OSDC 2015: Mitchell Hashimoto | Automating the Modern Datacenter, Development...
 
TIAD : Automating the modern datacenter
TIAD : Automating the modern datacenterTIAD : Automating the modern datacenter
TIAD : Automating the modern datacenter
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
Presto anatomy
Presto anatomyPresto anatomy
Presto anatomy
 
Introduction to cloudforecast
Introduction to cloudforecastIntroduction to cloudforecast
Introduction to cloudforecast
 
Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly - Terraform for azure: the good, the bad and the ugly -
Terraform for azure: the good, the bad and the ugly -
 
Let your DBAs get some REST(api)
Let your DBAs get some REST(api)Let your DBAs get some REST(api)
Let your DBAs get some REST(api)
 
Terraform Immutablish Infrastructure with Consul-Template
Terraform Immutablish Infrastructure with Consul-TemplateTerraform Immutablish Infrastructure with Consul-Template
Terraform Immutablish Infrastructure with Consul-Template
 
Systems Bioinformatics Workshop Keynote
Systems Bioinformatics Workshop KeynoteSystems Bioinformatics Workshop Keynote
Systems Bioinformatics Workshop Keynote
 

Recently uploaded

Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...apidays
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 

Recently uploaded (20)

Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 

Terraform at Scale: Reducing Complexity With Style

  • 2. ◉ Introduction ◉ Terraform at Scale? ◉ Gotta Keep ‘em Separated ◉ Reducing Complexity With Style ◉ A Tale of Two Modules Introduction: Agenda and Takeaways
  • 3. 80% Percentage of Outages Caused by Changes* * Source: Behr, K., Kim, G., & Spafford, G. (2013). The Visible Ops Handbook
  • 4. Post-fordism, cognitive-cultural economy derived from spontaneous order.
  • 5.
  • 23. Gotta Keep ‘em Separated
  • 24. Dive In: Code Samples
  • 25. AWS Provider provider.tf provider "aws" { region = "us-east-1" }
  • 26. AWS Route53 Provisioners route53.tf resource "aws_route53_zone" "route53_zone" { name = "devops-demo.xyz" } resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "dev-api.devops-demo.xyz" type = "A" ttl = "300" records = ["86.75.30.9"] }
  • 27. AWS Route53 Provisioners route53.tf resource "aws_route53_zone" "route53_zone" { name = "devops-demo.xyz" } resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "dev-api.devops-demo.xyz" type = "A" ttl = "300" records = ["86.75.30.9"] }
  • 28. route53.tf resource "aws_route53_zone" "route53_zone" { name = "devops-demo.xyz" } resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "dev-api.devops-demo.xyz" type = "A" ttl = "300" records = ["86.75.30.9"] } AWS Route53 Provisioners
  • 29. route53.tf resource "aws_route53_zone" "route53_zone" { name = "devops-demo.xyz" } resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "dev-api.devops-demo.xyz" type = "A" ttl = "300" records = ["86.75.30.9"] } AWS Route53 Provisioners
  • 30. route53.tf resource "aws_route53_zone" "route53_zone" { name = "devops-demo.xyz" } resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "dev-api.devops-demo.xyz" type = "A" ttl = "300" records = ["86.75.30.9"] } AWS Route53 Provisioners
  • 31. terraform plan (console output) + aws_route53_record.api_route53_record fqdn: "<computed>" name: "dev-api.devops-demo.xyz" records.#: "1" records.4228697306: "86.75.30.9" ttl: "300" type: "A" zone_id: "${aws_route53_zone.route53_zone.zone_id}" + aws_route53_zone.route53_zone comment: "Managed by Terraform" force_destroy: "false" name: "devops-demo.xyz" name_servers.#: "<computed>" vpc_region: "<computed>" zone_id: "<computed>" Plan: 2 to add, 0 to change, 0 to destroy.
  • 33. outputs.tf providers.tf route53.tf variables.tf Congratulations On Your WET Module! Image Source: “Campaign Shake-Up” Parks and Recreation, season 4, episode 17, NBC, 01 Mar. 2012
  • 34. Parameterize To Stay DRY provider.tf provider "aws" { region = "us-east-1" } route53.tf resource "aws_route53_zone" "route53_zone" { name = "devops-demo.xyz" } resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "dev-api.devops-demo.xyz" type = "A" ttl = "300" records = ["86.75.30.9"] }
  • 35. Input Variables provider.tf provider "aws" { region = "${var.aws_region}" } route53.tf resource "aws_route53_zone" "route53_zone" { name = "${var.domain_name}" } resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "${var.environment}-api.${var.domain_name}" type = "A" ttl = "300" records = ["${var.ip_address}”] }
  • 36. variables.tf variable "aws_account" { default = "sysadvent-production" } variable "aws_region" { default = "us-east-1" } variable "domain_name" { default = "devops-demo.xyz" } variable "environment" { default = "dev" } variable "ip_address" { default = "86.75.30.9" }
  • 37. terraform apply (console output) aws_route53_zone.route53_zone: Creating... comment: "" => "Managed by Terraform" force_destroy: "" => "false" name: "" => "devops-demo.xyz" name_servers.#: "" => "<computed>" vpc_region: "" => "<computed>" zone_id: "" => "<computed>" aws_route53_zone.route53_zone: Creation complete (ID: Z3CIMZCT6RGERD) aws_route53_record.api_route53_record: Creating... fqdn: "" => "<computed>" name: "" => "dev-api.devops-demo.xyz" records.#: "" => "1" records.4228697306: "" => "86.75.30.9" ttl: "" => "300" type: "" => "A" zone_id: "" => "Z3QZEABS9HJLIN" aws_route53_record.api_route53_record: Creation complete (ID: Z3CIMZCT6RGERD_api.devops-demo.xyz_A) ...
  • 38. terraform apply (console output cont.) ... Apply complete! Resources: 2 added, 0 changed, 0 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: Outputs: api_fqdn = dev-api.devops-demo.xyz domain_name = devops-demo.xyz zone_id = Z3QZEABS9HJLIN
  • 39.
  • 40. terraform apply (console output cont.) ... Apply complete! Resources: 2 added, 0 changed, 0 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: Outputs: api_fqdn = dev-api.devops-demo.xyz domain_name = devops-demo.xyz zone_id = Z3QZEABS9HJLIN
  • 41. outputs.tf output "api_fqdn" { value = "${aws_route53_record.api_route53_record.fqdn}" } output "domain_name" { value = "${var.domain_name}" } output "zone_id" { value = "${aws_route53_zone.route53_zone.id}" } Output Variables
  • 42. outputs.tf output "api_fqdn" { value = "${aws_route53_record.api_route53_record.fqdn}" } output "domain_name" { value = "${var.domain_name}" } output "zone_id" { value = "${aws_route53_zone.route53_zone.id}" } Output Variables
  • 43. outputs.tf output "api_fqdn" { value = "${aws_route53_record.api_route53_record.fqdn}" } output "domain_name" { value = "${var.domain_name}" } output "zone_id" { value = "${aws_route53_zone.route53_zone.id}" } Output Variables
  • 47. Naming Conventions: Resource Names ◉ RESOURCE NAME = RESOURCE TYPE - PROVIDER NAME resource "aws_security_group" "security_group" { name = "${var.resource_name}-security-group" ...
  • 48. Naming Conventions: Resource Names (cont.) resource "aws_s3_bucket" "data_s3_bucket" { bucket = "${var.env}-data-${var.aws_region}" } resource "aws_s3_bucket" "images_s3_bucket" { bucket = "${var.env}-images-${var.aws_region}" } ◉ Multiple resources of the same TYPE should have a minimalistic identifier to differentiate between the two resources.
  • 49. When to Use an Underscore resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "${var.environment}-api.${var.domain_name}" type = "A" ttl = "300" records = ["${var.ip_address}"] } ◉ Variable Names ◉ Resource Names ◉ Anything Interpolated
  • 50. When to Use a Hyphen resource "aws_route53_record" "api_route53_record" { zone_id = "${aws_route53_zone.route53_zone.zone_id}" name = "${var.environment}-api.${var.domain_name}" type = "A" ttl = "300" records = ["${var.ip_address}"] } ◉ Resources Being Created
  • 53.
  • 54. A Tale of Two Modules
  • 55. Service Module ● Reusable library ● Creates all required resources a service needs to be operational i.e. EC2 instances, S3 bucket, DNS Entries A Tale of Two Modules Infrastructure Module ● Single repository comprised of multiple root modules ● This is where Service Modules are instantiated/Terraform is run.
  • 56. Why Infrastructure Modules? ◉ Conditional Statements ○ Don’t exist Are painful (1604) ◉ Segment State ○ Reduce risk of changes ○ Flexible state reference ◉ DRY Terraform Configurations ○ Instantiate reusable modules ◉ Environments Aren’t Recommended (0.9) ◉ Workspaces are a thing (renamed statefile)
  • 57. The Candle Problem Source: http://whatismotivation.weebly.com/uploads/2/9/9/1/29913749/1713129_orig.png
  • 58. Overcome Functional Fixedness Source: http://whatismotivation.weebly.com/uploads/2/9/9/1/29913749/1713129_orig.png
  • 60. Configuration by Convention Infrastructure Repository Folder Structure AWS (provider) |__ production-account (aws-account root module) |__ us-east-1 (aws-region root module) |__ production-us-east-1-vpc (vpc root module) |__ production (environment root module) |__ inf-bastion (service root module)
  • 61. Account Root Module Infrastructure Repository Folder Structure AWS (provider) |__ production-account (aws-account root module) <~~~~~ YOU ARE HERE |__ us-east-1 (aws-region root module) |__ production-us-east-1-vpc (vpc root module) |__ production (environment root module) |__ inf-bastion (service root module)
  • 62. terraform.sh (bash wrapper) Usage: ./templates/account-terraform.sh [apply|destroy|plan|refresh|show] The following arguments are supported: apply Refresh the Terraform remote state, "terraform get -update", and "terraform apply" destroy Refresh the Terraform remote state and destroy the Terraform stack plan Refresh the Terraform remote state, "terraform get -update", and "terraform plan" refresh Refresh the Terraform remote state show Refresh and show the Terraform remote state
  • 63. Refresh Function ... refresh() { root=$(pwd | awk -F "/" '{print $(NF-1)}') aws_account=$(pwd | awk -F "/" '{print $NF}') export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${root}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } ...
  • 64. Refresh Function ... refresh() { root=$(pwd | awk -F "/" '{print $(NF-1)}') aws_account=$(pwd | awk -F "/" '{print $NF}') export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${root}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } ...
  • 65. Refresh Function ... refresh() { root=$(pwd | awk -F "/" '{print $(NF-1)}') aws_account=$(pwd | awk -F "/" '{print $NF}') export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${root}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } ...
  • 66. ... createBackendConfig() { /bin/cat > backend.tf <<EOL terraform { backend "s3" {} } EOL } ... Create Backend Config backend.tf terraform { backend "s3" {} }
  • 68. AWS-Region Root Module Infrastructure Repository Folder Structure AWS (provider) |__ production-account (aws-account root module) |__ us-east-1 (aws-region root module) <~~~~~ PLACEHOLDER |__ production-us-east-1-vpc (vpc root module) |__ production (environment root module) |__ inf-bastion (service root module)
  • 69. VPC Root Module Infrastructure Repository Folder Structure AWS (provider) |__ production-account (aws-account root module) |__ us-east-1 (aws-region root module) |__ production-us-east-1-vpc (vpc root module) <~~~~~ YOU ARE HERE |__ production (environment root module) |__ inf-bastion (service root module)
  • 71. main.tf module "vpc" { source = "git@github.com:TerraformDesignPattern/vpc.git?ref=1.2.3" availability_zones = "${var.availability_zones}" aws_region = "${var.aws_region}" private_subnets = "${var.private_subnets}" public_subnets = "${var.public_subnets}" vpc_cidr = "${var.vpc_cidr}" vpc_name = "${var.vpc_name}" } VPC Root Module (cont.)
  • 72. main.tf module "vpc" { source = "git@github.com:TerraformDesignPattern/vpc.git?ref=1.2.3" availability_zones = "${var.availability_zones}" aws_region = "${var.aws_region}" private_subnets = "${var.private_subnets}" public_subnets = "${var.public_subnets}" vpc_cidr = "${var.vpc_cidr}" vpc_name = "${var.vpc_name}" } VPC Root Module (cont.)
  • 73. main.tf module "vpc" { source = "git@github.com:TerraformDesignPattern/vpc.git?ref=${var.branch}" availability_zones = "${var.availability_zones}" aws_region = "${var.aws_region}" private_subnets = "${var.private_subnets}" public_subnets = "${var.public_subnets}" vpc_cidr = "${var.vpc_cidr}" vpc_name = "${var.vpc_name}" } VPC Root Module (cont.)
  • 74. NOPE.
  • 75. main.tf module "vpc" { source = "git@github.com:TerraformDesignPattern/vpc.git?ref=${var.branch}" availability_zones = "${var.availability_zones}" aws_region = "${var.aws_region}" private_subnets = "${var.private_subnets}" public_subnets = "${var.public_subnets}" vpc_cidr = "${var.vpc_cidr}" vpc_name = "${var.vpc_name}" } VPC Root Module (cont.) No interpolation allowed! Issue #1439
  • 76.
  • 77. main.tf module "vpc" { source = "git@github.com:TerraformDesignPattern/vpc.git?ref=${var.branch}" availability_zones = "${var.availability_zones}" aws_region = "${var.aws_region}" private_subnets = "${var.private_subnets}" public_subnets = "${var.public_subnets}" vpc_cidr = "${var.vpc_cidr}" vpc_name = "${var.vpc_name}" } VPC Root Module (cont.) No interpolation allowed! Issue #1439
  • 78. NOPE.
  • 79.
  • 80. Soon.
  • 82. main.tf module "vpc" { source = "git@github.com:TerraformDesignPattern/vpc.git?ref=1.2.3" availability_zones = "${var.availability_zones}" aws_region = "${var.aws_region}" private_subnets = "${var.private_subnets}" public_subnets = "${var.public_subnets}" vpc_cidr = "${var.vpc_cidr}" vpc_name = "${var.vpc_name}" } VPC Root Module (cont.)
  • 84. variables.tf ... variable "public_subnets" { default = [ "172.19.101.0/24", "172.19.102.0/24", "172.19.103.0/24", ] } ... VPC Root Module (cont.)
  • 86. outputs.tf ... output "public_subnet_ids" { value = ["${aws_subnet.public_subnet.*.id}"] } ... VPC Service Module Outputs
  • 87. outputs.tf ... output "public_subnet_ids" { value = ["${module.vpc.public_subnet_ids}"] } ... VPC Root Module Outputs
  • 88.
  • 89. To Begin Again... From The Beginning Quote: Waking Life. Dir. Richard Linklater. Fox Searchlight Pictures, 2001. FIlm. Image: Fight Club. Dir. David Fincher. 20th Century Fox, 1999. Film.
  • 90. s3.tf resource "aws_s3_bucket_object" "outputs_object" { bucket = "${var.aws_account}-terraform-state" key = "aws/${var.aws_region}/${var.vpc_name}/dummy_object_outputs" source = "outputs.tf" etag = "${md5(file("outputs.tf"))}" } resource "aws_s3_bucket_object" "variables_object" { bucket = "${var.aws_account}-terraform-state" key = "aws/${var.aws_region}/${var.vpc_name}/dummy_object_variables" source = "variables.tf" etag = "${md5(file("variables.tf"))}" } Root Module State Seeding outputs.tf output "public_subnet_ids" { value = ["${var.public_subnets}"] } ... variables.tf variable "public_subnets" { default = [ "172.19.101.0/24", "172.19.102.0/24", "172.19.103.0/24", ] } ...
  • 91. Environment Root Module Infrastructure Repository Folder Structure AWS (provider) |__ production-account (aws-account root module) |__ us-east-1 (aws-region root module) |__ production-us-east-1-vpc (vpc root module) |__ production (environment root module) <~~~~~ PLACEHOLDER |__ inf-bastion (service root module)
  • 93. Service Root Module Infrastructure Repository Folder Structure AWS (provider) |__ production-account (aws-account root module) |__ us-east-1 (aws-region root module) |__ production-us-east-1-vpc (vpc root module) |__ production (environment root module) |__ inf-bastion (service root module) <~~~~~ YOU ARE HERE
  • 94. SSH Bastion Root Module backend.tf main.tf outputs.tf terraform.sh variables.tf SSH Bastion Root Module
  • 95. variables.tf variable "aws_account" {} variable "aws_region" {} variable "aws_environment_name" {} variable "service_name" {} variable "vpc_name" {} SSH Bastion Root Module (cont.)
  • 96. refresh() { root=$(pwd | awk -F "/" '{print $(NF-5)}') aws_account=$(pwd | awk -F "/" '{print $(NF-4)}') aws_region=$(pwd | awk -F "/" '{print $(NF-3)}') vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}') environment_name=$(pwd | awk -F "/" '{print $(NF-1)}') service_name=$(pwd | awk -F "/" '{print $NF}') bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}" export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_VAR_aws_region="$aws_region" export TF_VAR_vpc_name="$vpc_name" export TF_VAR_environment_name="$environment_name" export TF_VAR_service_name="$service_name" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${bucket_key}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } Refresh Function
  • 97. refresh() { root=$(pwd | awk -F "/" '{print $(NF-5)}') aws_account=$(pwd | awk -F "/" '{print $(NF-4)}') aws_region=$(pwd | awk -F "/" '{print $(NF-3)}') vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}') environment_name=$(pwd | awk -F "/" '{print $(NF-1)}') service_name=$(pwd | awk -F "/" '{print $NF}') bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}" export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_VAR_aws_region="$aws_region" export TF_VAR_vpc_name="$vpc_name" export TF_VAR_environment_name="$environment_name" export TF_VAR_service_name="$service_name" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${bucket_key}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } Refresh Function
  • 98. refresh() { root=$(pwd | awk -F "/" '{print $(NF-5)}') aws_account=$(pwd | awk -F "/" '{print $(NF-4)}') aws_region=$(pwd | awk -F "/" '{print $(NF-3)}') vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}') environment_name=$(pwd | awk -F "/" '{print $(NF-1)}') service_name=$(pwd | awk -F "/" '{print $NF}') bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}" export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_VAR_aws_region="$aws_region" export TF_VAR_vpc_name="$vpc_name" export TF_VAR_environment_name="$environment_name" export TF_VAR_service_name="$service_name" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${bucket_key}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } Refresh Function
  • 99. refresh() { root=$(pwd | awk -F "/" '{print $(NF-5)}') aws_account=$(pwd | awk -F "/" '{print $(NF-4)}') aws_region=$(pwd | awk -F "/" '{print $(NF-3)}') vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}') environment_name=$(pwd | awk -F "/" '{print $(NF-1)}') service_name=$(pwd | awk -F "/" '{print $NF}') bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}" export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_VAR_aws_region="$aws_region" export TF_VAR_vpc_name="$vpc_name" export TF_VAR_environment_name="$environment_name" export TF_VAR_service_name="$service_name" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${bucket_key}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } Refresh Function
  • 100. refresh() { root=$(pwd | awk -F "/" '{print $(NF-5)}') aws_account=$(pwd | awk -F "/" '{print $(NF-4)}') aws_region=$(pwd | awk -F "/" '{print $(NF-3)}') vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}') environment_name=$(pwd | awk -F "/" '{print $(NF-1)}') service_name=$(pwd | awk -F "/" '{print $NF}') bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}" export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_VAR_aws_region="$aws_region" export TF_VAR_vpc_name="$vpc_name" export TF_VAR_environment_name="$environment_name" export TF_VAR_service_name="$service_name" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${bucket_key}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } Refresh Function
  • 101. refresh() { root=$(pwd | awk -F "/" '{print $(NF-5)}') aws_account=$(pwd | awk -F "/" '{print $(NF-4)}') aws_region=$(pwd | awk -F "/" '{print $(NF-3)}') vpc_name=$(pwd | awk -F "/" '{print $(NF-2)}') environment_name=$(pwd | awk -F "/" '{print $(NF-1)}') service_name=$(pwd | awk -F "/" '{print $NF}') bucket_key="${root}/${aws_region}/${vpc_name}/${environment_name}/${service_name}" export TF_VAR_root="$root" export TF_VAR_aws_account="$aws_account" export TF_VAR_aws_region="$aws_region" export TF_VAR_vpc_name="$vpc_name" export TF_VAR_environment_name="$environment_name" export TF_VAR_service_name="$service_name" export TF_PLUGIN_CACHE_DIR="~/.terraform.d/plugin-cache" echo -e "nn***** Refreshing State and Upgrading Modules *****" echo "no" | terraform init -get=true -upgrade -input=false -backend=true -backend-config "bucket=${aws_account}-terraform-state" -backend-config "key=${bucket_key}/terraform.tfstate" -backend-config "profile=${aws_account}" -backend-config "region=us-east-1" } Refresh Function
  • 102. module “bastion" { source = "git@github.com:TerraformDesignPattern/bastionhost.git?ref=0.1.0" aws_account = "${var.aws_account}" aws_region = "${var.aws_region}" environment_name = "${var.environment_name}" vpc_name = "${var.vpc_name}" service_name = "${element(split("-", var.service_name), 0)}" tier_name = "${element(split("-", var.service_name), 1)}" } SSH Bastion Root Module (cont.)
  • 103. SSH Bastion Module Repository data.tf ec2.tf outputs.tf providers.tf route53.tf security_groups.tf variables.tf SSH Bastion Service Module
  • 104. data.tf data "terraform_remote_state" "account" { backend = "s3" config { bucket = "${var.aws_account}-terraform-state" key = "aws/terraform.tfstate" region = "us-east-1" } } data "terraform_remote_state" "vpc" { backend = "s3" config { bucket = "${var.aws_account}-terraform-state" key = "aws/${var.aws_region}/${var.vpc_name}/terraform.tfstate" region = "us-east-1" } }
  • 105. route53.tf resource "aws_route53_record" "route53_record" { zone_id = "${data.terraform_remote_state.account.zone_id}" name = "${local.hostname}.${data.terraform_remote_state.account.domain_name}" type = "A" ttl = "300" records = ["${aws_instance.instance.public_ip}"] } ... security_groups.tf resource "aws_security_group" "elb_security_group" { name = "${local.resource_name}-elb-sg-${data.terraform_remote_state.vpc.aws_region_shortname}" vpc_id = "${data.terraform_remote_state.vpc.vpc_id}" ...
  • 106. route53.tf resource "aws_route53_record" "route53_record" { zone_id = "${data.terraform_remote_state.account.zone_id}" name = "${local.hostname}.${data.terraform_remote_state.account.domain_name}" type = "A" ttl = "300" records = ["${aws_instance.instance.public_ip}"] } ... security_groups.tf resource "aws_security_group" "elb_security_group" { name = "${local.resource_name}-elb-sg-${data.terraform_remote_state.vpc.aws_region_shortname}" vpc_id = "${data.terraform_remote_state.vpc.vpc_id}" ...
  • 107. variables.tf locals { hostname = "${var.environment_name}-${var.service_name}-${var.tier}-${data.terraform_remote_state.vpc.aws_region_shortname}" resource_name = "${var.environment_name}-${var.service_name}-${var.tier}" } ... Module “Local” Variables
  • 108. Manage State & Variables Dynamically Infrastructure Repository Folder Structure AWS (provider) |__ production-account (root aws-account module) |__ us-east-1 (root aws-region module) |__ production-us-east-1-vpc (root vpc module) |__ production (root environment module) |__ bastion (root service module)
  • 109. ◉ Introduction ◉ Terraform at Scale? ◉ Gotta Keep ‘em Separated ◉ Reducing Complexity With Style ◉ A Tale of Two Modules Closing: Agenda and Takeaways
  • 110.
  • 111. Contact: ◉ Twitter: @jonbrouse ◉ Github: github.com/jonbrouse ◉ Site: jonbrouse.com ◉ github.com/TerraformDesignPattern Thank you! Jon Brouse