cluster deboistech.in Get started
Cheatsheet

Infrastructure as Code

Terraform, Ansible and CloudFormation side by side: the commands and the file shapes you use again and again.

Practice this path

Terraform workflow

  • terraform init

    Download providers and set up the backend (once per folder, and after changes to them)

  • terraform fmt -recursive && terraform validate

    Tidy the formatting and check the syntax

  • terraform plan -out=tfplan

    Show what would change, and save that plan

  • terraform apply tfplan

    Make exactly that plan happen

  • terraform destroy

    Delete everything this configuration manages

  • terraform output  |  terraform output -raw name

    Show the output values

Terraform language

  • variable "env" {
      type    = string
      default = "dev"
      validation {
        condition     = contains(["dev", "prod"], var.env)
        error_message = "env must be dev or prod."
      }
    }

    An input with a type, a default and a rule

  • variable "db_password" {
      type      = string
      sensitive = true
    }

    A secret input: no default, hidden in output

  • locals {
      name = "${var.env}-app"
    }

    A named value computed once (use local.name)

  • resource "aws_s3_bucket" "assets" {
      bucket = local.name
    }

    A resource: type, local name and arguments

  • output "bucket" {
      value = aws_s3_bucket.assets.bucket
    }

    A value to show after apply

  • terraform apply -var="env=prod"  |  -var-file=prod.tfvars

    Set inputs on the command line or from a file

Terraform: loops, modules, state

  • for_each = toset(["a", "b"])
    bucket   = "${var.env}-${each.key}"

    One resource per item; keys keep their identity when the list changes

  • count = var.enabled ? 1 : 0

    Create a resource only when a condition holds

  • module "site" {
      source = "./modules/bucket"
      name   = "docs"
    }

    Reuse a folder of configuration as a module

  • lifecycle {
      prevent_destroy = true
      ignore_changes  = [tags]
    }

    Protect a resource, or ignore changes made elsewhere

  • moved {
      from = aws_s3_bucket.old
      to   = aws_s3_bucket.assets
    }

    Rename in code without destroying and recreating

  • terraform state list  |  terraform state show aws_s3_bucket.assets

    See what Terraform tracks, and one resource in detail

  • terraform import aws_s3_bucket.assets my-existing-bucket

    Bring an existing resource under Terraform

  • .gitignore:  .terraform/  *.tfstate  *.tfstate.*

    Keep state and caches out of Git (use a remote backend for state)

Ansible: inventory and commands

  • [web]
    web1 ansible_host=10.0.0.11
    web2 ansible_host=10.0.0.12
    
    [prod:children]
    web
    
    [web:vars]
    ansible_user=deploy

    An INI inventory with a group of groups and group variables

  • ansible all -i inventory.ini -m ping

    Can Ansible log in to every host?

  • ansible web -i inventory.ini -m command -a "uptime"

    Run one command on a group

  • ansible-inventory -i inventory.ini --graph

    See how hosts and groups fit together

  • ansible-playbook -i inventory.ini site.yml --check --diff

    A dry run that shows the differences

  • ansible-playbook -i inventory.ini site.yml --limit web1 --tags config

    Run only part of a playbook, or only some hosts

Ansible: playbooks and roles

  • - name: Web servers
      hosts: web
      become: true
      tasks:
        - name: Install nginx
          ansible.builtin.package:
            name: nginx
            state: present

    A play with a task

  • - name: Config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/conf.d/app.conf
      notify: restart nginx

    Fill a template, and notify a handler when the file changed

  • handlers:
      - name: restart nginx
        ansible.builtin.service:
          name: nginx
          state: restarted

    A handler runs once, only if notified

  • loop: [git, curl]
    when: ansible_os_family == "Debian"

    Repeat a task, and run it only under a condition

  • - name: Init database
      ansible.builtin.command: /usr/local/bin/init-db.sh
      args:
        creates: /var/lib/app/.initialized

    Keep a command idempotent with creates

  • roles/web/{tasks,handlers,defaults,templates}/main.yml

    The folders of a role; roles: [web] in a play uses it

  • ansible-vault encrypt_string "s3cret" --name db_password

    Encrypt one value to keep in a variables file

CloudFormation

  • aws cloudformation deploy --template-file template.yaml --stack-name app --parameter-overrides Env=prod

    Create the stack, or update it if it exists

  • aws cloudformation describe-stacks --stack-name app --query "Stacks[0].Outputs"

    Read the outputs of a stack

  • aws cloudformation list-stack-resources --stack-name app

    What the stack created

  • aws cloudformation validate-template --template-body file://template.yaml

    Check a template

  • aws cloudformation delete-stack --stack-name app

    Delete the stack and what it created

  • Parameters:
      Env: { Type: String, Default: dev }
    Resources:
      Assets:
        Type: AWS::S3::Bucket
        Properties:
          BucketName: !Sub "debo-${Env}-assets"
    Outputs:
      Bucket:
        Value: !Ref Assets

    A template with a parameter, a bucket and an output

  • !Ref Name  !Sub "text-${Name}"  !GetAtt Res.Arn  !Join ["-", [a, b]]

    The intrinsic functions you use most

Cheatsheet for the Infrastructure as Code path · Deboistech · All cheatsheets