Skip to content

Instantly share code, notes, and snippets.

@vanvuvuong
Last active March 5, 2024 03:41
Show Gist options
  • Save vanvuvuong/fc941565cfc9405f709313ba9bee4917 to your computer and use it in GitHub Desktop.
Save vanvuvuong/fc941565cfc9405f709313ba9bee4917 to your computer and use it in GitHub Desktop.
Terraform Notes

Terraform

Condition

resource "null_resource" "test" {
  count = true ? 1: 0
}

Get index from map & concat string

variable "public_subnets" {
  type = map(any)
  default = {
    "key": "value"
  }
}

data "aws_availability_zones" "azs" {
  state = "available"
}

resource "aws_subnet" "public_subnets" {
  for_each                        = var.public_subnets

  vpc_id                          = aws_vpc.vpc.id
  cidr_block                      = each.value
  availability_zone               = data.aws_availability_zones.azs.names[index(keys(var.public_subnets), each.key)]
  map_public_ip_on_launch         = var.map_public_ip_on_launch
  assign_ipv6_address_on_creation = var.enable_ipv6
  tags = {
    "Name" = format("%s-%s", each.key, data.aws_availability_zones.azs.names[index(keys(var.public_subnets), each.key)])
  }
}

Copy file to remote machine after creation by adding these blocks

  provisioner "file" {
    source      = "${path.module}/file"
    destination = "${local.remote_home}/file"
  }
  connection {
    type        = "ssh"
    user        = local.remote_user
    private_key = file("${path.cwd}/${aws_key_pair.ww_key.key_name}.pem")
    host        = self.public_ip
    timeout     = "5m"
  }

Adding state ignore attribute by adding this block

  lifecycle {
    create_before_destroy = true
    ignore_changes        = [tags, metadata_options, security_groups]
  }

Create variables-based template file

data "template_file" "example" {
  template = file("./file.yml")
  vars = {
    env = "prod"
    }
}

NOTES OF TERRAFORM COMMAND USAGES

1. Terraform init (2nd time): update backend, download modules

terraform init -reconfigure

2. Terraform validate: validate terraform scripts

terraform validate

3. Output the plan to text file: write all resources changing to text file

terraform plan -no-color > tfplan.txt

4. Force unlock state: Acquire state lock from another process

terraform force-unlock <LockID>

5. Output the resources to graph

terraform graph | dot -Tsvg -o tfgraph.svg

6. Untracking resource state:

terraform state rm <ResouceState>

7. Destroy specific resource:

terraform destroy -target=<ResouceState>

8. Refresh state with manual change

tfa -refresh-only

9. Pull state from remote to local

tf state pull > resources.tfstate

10. Multiple target script

get_target() {
    # $1: pattern of target resource you want to filter(based on your terraform files), e.g: aws_subnet
    # $2: prefix that you want to add to -target, e.g: module.test.
    # result: -target module.test.aws_subnet.subnet_name
    pattern=$1
    prefix=$2
    apply_targets=""
    for target in $(grep -hRe "^resource.*$pattern" | sed -e 's/resource "//g' | sed 's/" {//g' | sed 's/" "/./g'); do apply_targets="$apply_targets -target $prefix$target"; done
}
get_target "aws_subnet" "module.test." | xargs -r terraform apply -auto-approve

11. Import existed resource as map value

tf import 'module.test.aws_subnet.ec2["10.38.0.0/24"]' subnet-0cXXXXX

Adding this bash script to ~/.zshrc or ~/.bashrc

Terracost with terraform plan output file - no argument needed

function tfpcost() {
    # use terraform plan output file file to estimate infrastructure cost
    # $1: terraform.tfstate
    state_file=${1}
    if ! [ -x "$(command -v jq)" ]; then
        echo "jq package not found. Please install 'jq'"
        exit 1
    fi

    if ! [ -e "${HOME}/terraform.jq" ]; then
        wget https://raw.githubusercontent.com/antonbabenko/terraform-cost-estimation/master/terraform.jq -O ~/terraform.jq
    fi
    terraform plan -out=/tmp/plan.tfplan >/dev/null && terraform show -json /tmp/plan.tfplan | jq -cf ~/terraform.jq | curl -s -X POST -H "Content-Type: application/json" -d @- https://cost.modules.tf/
    rm /tmp/plan.tfplan
}

Terracost with terraform.tfstate file

function terracost() {
    # use terraform.tfstate file to estimate infrastructure cost
    # $1: terraform.tfstate
    state_file=${1}
    if ! [ -x "$(command -v jq)" ]; then
        echo "jq package not found. Please install 'jq'"
        exit 1
    fi

    if ! [ -e "${HOME}/terraform.jq" ]; then
        wget https://raw.githubusercontent.com/antonbabenko/terraform-cost-estimation/master/terraform.jq -O ~/terraform.jq
    fi
    jq <"${state_file}" -cf "${HOME}/terraform.jq" | curl -s -X POST -H "Content-Type: application/json" -d @- https://cost.modules.tf/
}

Terragrunt

Inherit common variables

Folder structure

.
|--common.hcl
|--terragrunt.hcl
|--prod
   |--terragrunt.hcl

Adding this block to terragrunt.hcl in child folder

include "root" {
  path = find_in_parent_folders()
}
locals {
  common_vars = read_terragrunt_config(find_in_parent_folders("common.hcl"))
}

Generate environment-based yaml config file

Content in test.yml in based terragrunt folder

version: 0.2

env:
  variables:
    ACCOUNT_ID: "${account_id}"

Folder structure:

.
|--terragrunt.hcl
|--test.yml
|--prod
   |--terragrunt.hcl
|--modules
   |--example
      |--config
      |--main.tf

Adding this block to terragrunt.hcl in child folder:

terraform {
  source = "${path_relative_from_include()}"
}
generate "backend_buildspec_image" {
  path              = "modules/example/config/test.yml"
  if_exists         = "overwrite"
  contents          = base64decode(filebase64("test.yml"))
  disable_signature = true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment