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

AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
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
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...HostedbyConfluent
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxnull - The Open Security Community
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsHyundai Motor Group
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 

Recently uploaded (20)

AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
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
 
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
Transforming Data Streams with Kafka Connect: An Introduction to Single Messa...
 
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptxMaking_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
Making_way_through_DLL_hollowing_inspite_of_CFG_by_Debjeet Banerjee.pptx
 
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter RoadsSnow Chain-Integrated Tire for a Safe Drive on Winter Roads
Snow Chain-Integrated Tire for a Safe Drive on Winter Roads
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 

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