Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Touring the Terraform Journey and Atlantis

Touring the Terraform Journey and Atlantis

In this talk we'll take a whirlwind tour of the journey many companies go through as they adopt Terraform: from your first Terraform project through to splitting up statefiles, importing resources, using remote state, writing modules, dealing with environments and scaling your workflow as your team grows. Along the way we'll talk about common mistakes and best practices so that wherever you are in your journey you can get pointed in the right direction. Finally, we'll talk about an open source tool that I maintain called Atlantis (github.com/runatlantis/atlantis). Atlantis helps teams collaborate on Terraform through pull requests and can enforce workflows and enable your developers to write Terraform without needing credentials.

This talk was given at the Barcelona HashiCorp User Group Meetup on May 29th, 2018.

Luke Kysow

May 29, 2018
Tweet

More Decks by Luke Kysow

Other Decks in Programming

Transcript

  1. Agenda • Phase 1: Starting Out • Phase 2: All

    In On Terraform • Phase 3: Collaboration and Automation
  2. Phase 1: Starting Out • How do I structure my

    repo(s) and files? • I don't know what Terraform to write
  3. . └── main.tf Single State variable "staging_vpc_id" { default =

    "vpc-1345" } variable "production_vpc_id" { default = "vpc-1345" } resource "aws_ecs_cluster" "staging" { name = "staging" } resource "aws_ecs_cluster" "production" { name = "production" } resource "aws_ecs_service" "atlantis_staging" { ... resource "aws_ecs_service" "atlantis_production" { ...
  4. . ├── production │ └── main.tf └── staging └── main.tf

    State Per Env variable "vpc_id" { default = "vpc-1345" } resource "aws_ecs_cluster" "cluster" { name = "production" } resource "aws_ecs_service" "atlantis" { ... variable "vpc_id" { default = "vpc-1345" } resource "aws_ecs_cluster" "cluster" { name = "staging" } resource "aws_ecs_service" "atlantis" { ...
  5. . ├── production │ ├── atlantis │ │ └── main.tf

    │ └── ecs │ └── main.tf └── staging ├── atlantis │ └── main.tf └── ecs └── main.tf Separate Core and Services resource "aws_ecs_cluster" "cluster" { name = "production" } resource "aws_ecs_service" "atlantis" { ...
  6. . ├── production │ ├── atlantis │ │ └── main.tf

    │ └── ecs │ └── main.tf └── staging ├── atlantis │ └── main.tf └── ecs └── main.tf Separate Core and Services resource "aws_ecs_cluster" "cluster" { name = "production" } resource "aws_ecs_service" "atlantis" { cluster = "????"
  7. . ├── production │ ├── atlantis │ │ └── main.tf

    │ └── ecs │ └── main.tf └── staging ├── atlantis │ └── main.tf └── ecs └── main.tf Separate Core and Services terraform { backend "s3" { bucket = "bucket" region = "us-east-1" key = "production/ecs" } } resource "aws_ecs_cluster" "cluster" { name = "production" } output "cluster_arn" { value = "${aws_ecs_cluster.cluster.arn}" }
  8. . ├── production │ ├── atlantis │ │ └── main.tf

    │ └── ecs │ └── main.tf └── staging ├── atlantis │ └── main.tf └── ecs └── main.tf Separate Core and Services data terraform_remote_state "ecs" { backend = "s3" config { bucket = "bucket" key = "production/ecs" region = "us-east-1" } } resource "aws_ecs_service" "atlantis" { cluster = "${data.terraform_remote_state.ecs.cluster_arn}"
  9. Terraform workspaces . ├── atlantis │ └── main.tf └── ecs

    └── main.tf $ terraform workspace new staging Created and switched to workspace "staging"! // ecs/main.tf resource "aws_ecs_cluster" "cluster" { name = "${terraform.env}" } variable "num_servers" { count = "${terraform.workspace == "production" ? 5 : 1}" }
  10. When Terraform is used to manage larger systems, teams should

    use multiple separate Terraform configurations that correspond with suitable architectural boundaries within the system so that different components can be managed separately and, if appropriate, by distinct teams. Workspaces alone are not a suitable tool for system decomposition. - https://www.terraform.io/docs/state/workspaces.html
  11. Modules . ├── modules │ ├── atlantis │ └── ecs

    ├── production │ ├── atlantis │ └── ecs └── staging ├── atlantis └── ecs module "ecs" { source = "../modules/ecs" name = "production" } variable "name" {} resource "aws_ecs_cluster" "cluster" { name = "${var.name}" } ... module "ecs" { source = "../modules/ecs" name = "staging" }
  12. .tfvars . ├── atlantis │ ├── main.tf │ ├── production.tfvars

    │ └── staging.tfvars └── ecs ├── main.tf ├── production.tfvars └── staging.tfvars $ terraform plan -var-file production.tfvars name = "production" variable "name" {} resource "aws_ecs_cluster" "cluster" { name = "${var.name}" }
  13. Phase 1: Starting Out • How do I structure my

    repo(s) and files? • I don't know what Terraform to write ◦ Use the console ◦ Use registry.terraform.io
  14. Phase 1: Starting Out • Separate states for safety •

    Consistent directory structure • DRY templates
  15. resource "aws_security_group" "internet_to_alb" {...} resource "aws_security_group" "alb_to_ecs" {...} resource "aws_lb"

    "alb" {...} resource "aws_ecs_task_definition" "atlantis" {...} resource "aws_ecs_service" "atlantis" {...} resource "aws_cloudwatch_log_group" "atlantis" {...} . ├── modules │ ├── atlantis │ ├── app2 │ └── ecs ├── production │ ├── atlantis │ ├── app2 │ └── ecs └── staging ├── atlantis ├── app2 └── ecs resource "aws_security_group" "internet_to_alb" {...} resource "aws_security_group" "alb_to_ecs" {...} resource "aws_lb" "alb" {...} resource "aws_ecs_task_definition" "app2" {...} ...
  16. module "ecs-app" { source = "../ecs-app" name = "atlantis" }

    . ├── modules │ ├── atlantis │ ├── app2 │ ├── ecs-app │ └── ecs ├── production │ ├── atlantis │ ├── app2 │ └── ecs └── staging ├── atlantis ├── app2 └── ecs resource "aws_security_group" "internet_to_alb" {...} resource "aws_security_group" "alb_to_ecs" {...} resource "aws_lb" "alb" {...} resource "aws_ecs_task_definition" "def" {...} ... module "ecs-app" { source = "../ecs-app" name = "app2" }
  17. // modules/ecs-app/main.tf resource "aws_security_group" "internet_to_alb" {...} resource "aws_security_group" "alb_to_ecs" {...}

    resource "aws_lb" "alb" {...} resource "aws_ecs_task_definition" "atlantis" {...} resource "aws_ecs_service" "atlantis" {...} resource "aws_cloudwatch_log_group" "atlantis" {...} // modules/atlantis/main.tf module "ecs-app" { source = "../../modules/ecs-app" name = "atlantis" }
  18. // modules/ecs-app/main.tf resource "aws_security_group" "internet_to_alb" {...} resource "aws_security_group" "alb_to_ecs" {...}

    resource "aws_nlb" "nlb" {...} resource "aws_ecs_task_definition" "atlantis" {...} resource "aws_ecs_service" "atlantis" {...} resource "aws_cloudwatch_log_group" "atlantis" {...} // modules/atlantis/main.tf module "ecs-app" { source = "../../modules/ecs-app" name = "atlantis" }
  19. // => github.com/myorg/tf-modules-ecs-app/main.tf resource "aws_security_group" "internet_to_alb" {...} resource "aws_security_group" "alb_to_ecs"

    {...} resource "aws_nlb" "nlb" {...} resource "aws_ecs_task_definition" "atlantis" {...} resource "aws_ecs_service" "atlantis" {...} resource "aws_cloudwatch_log_group" "atlantis" {...} // modules/atlantis/main.tf module "ecs-app" { source = "github.com/myorg/tf-modules-ecs-app?ref=v1.0" name = "atlantis" }
  20. Summary • Phase 1: Starting Out ◦ How do I

    structure my repo(s) and files? ◦ I don't know what Terraform to write • Phase 2: All In On Terraform ◦ How do I manage resources I've already created? ◦ How can I extract common patterns? • Phase 3: Collaboration and Automation