Terraform

Starting

Terraform reads all files in a directory to build a project. By convention they end .tf

Terrraform used plug in modules to communicate with a cloud provider, eg. AWS, the init phase loads these in a .terraform directory.

Phases of build:-

$ cd to target directory
$ terraform init
$ terraform plan
$ terraform apply
$ terraform destroy

init phase

The init phase sets up the terraform environment prior to being able to run the apply phase. If you need to run this under a different AWS profile, you need to set this as an env variable:-

$ AWS_PROFILE=myprofile terraform init

I needed to do this to initialise an s3 backend for a statefile, but my defaut AWS crededtials were for a different account, so it was trying to access a bucket I did not have permission for, setting the env var solved this.

The AWS profile info can be found here:- Amazon Web Services CLI

Another option is to include a profile in your provider statement:-

provider "aws" {
    region = "${var.AWS_REGION}"
    profile = "myprofile"
    default_tags {
      tags = {
        BuiltBy = "Terraform"
        }
    }

validate phase

validate -json
{
  "format_version": "1.0",
  "valid": true,
  "error_count": 0,
  "warning_count": 0,
  "diagnostics": []
}

plan phase

The plan phase will show the resources to be built or changed, you can also see resources to be destroyed.

As with init, you can run this as a different AWS profile:-

$ AWS_PROFILE=rainsbrook terraform plan


$ terraform plan -destroy

apply phase

apply phase will do a plan and then attempt to apply this plan, you will be prompted to respond yes to approve. You anc avoid this if you are sure by specifying -auto-approve.

$ terraform apply -auto-approve

destroy phase

destroy phase removes resources Terraform has built, you can use the plan option to see what is to be destroyed.

$ terraform plan -destroy

$ terraform destroy

$ terraform destroy -auto-approve

State files and Lock files

See Cloudformation for Terraform State Files and Lock Table, this is some Cloudformation code which runs to bootstrap Terraform by creating an S3 bucket, DynamoDB Lock Table and stores the resulting Bucket and Table name in parameter Store where Terraform can retreive it.

Use S3 and Dynamo db to store this info.

AWS Provisioner code

aws_provider.tf

# Export key and secret credentials first.
# run as -> $ terraform apply
# might need to do this if init times out:-
#  "export https_proxy=http://proxy.company.com:8080"
 
data "aws_ssm_parameter" "TerraformLockTable-SG" {
  name = "TerraformLockTable-SG"
}
 
data "aws_ssm_parameter" "TerraformStateBucket" {
  name = "TerraformStateBucket-SG"
}
 
 
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "3.74.1"
    }
  }
 
  backend "s3" {
    region = "eu-west-2"
    bucket = "vpc-ec2-statefiles-sg"
    key    = "training_vpc_ec2-statefiles-SG/prod/vpc-ec2.tfstate"
    dynamodb_table = "vpc-ec2-lockfiles-SG"
  }
}
 
provider "aws" {
  region = "eu-west-2"
}

Snippets

resource "aws_s3_bucket" "aws-vpn-vpc-prod-statefiles" {
  bucket = "aws-vpn-vpc-prod-statefiles"
  versioning {
    enabled = true
  }
}
 
resource "aws_dynamodb_table" "aws-vpn-vpc-prod-locks" {
  name = "aws-vpn-vpd-prod-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key = "LockID"
  attribute {
    name = "LockID"
    type = "S"
  }
}
 
terraform {
  backend "s3" {
    profile = "rainsbrook-terraform"
    bucket = "aws-vpn-vpc-prod-statefiles"
    key = "aws-vpn-vpc-prod/terraform.tfstate"
    region = "eu-west-1"
    dynamodb_table = "aws-vpn-vpc-prod-locks"
  }

Visualising what is going to be built

terraform graph will produce a block of text you can upload to http://webgraphviz.com to draw a diagram of dependencies.

digraph {
	compound = "true"
	newrank = "true"
	subgraph "root" {
		"[root] aws_iam_role.iam-key-age-role (expand)" [label = "aws_iam_role.iam-key-age-role", shape = "box"]
		"[root] provider[\"registry.terraform.io/hashicorp/aws\"]" [label = "provider[\"registry.terraform.io/hashicorp/aws\"]", shape = "diamond"]
		"[root] var.AWS_REGION" [label = "var.AWS_REGION", shape = "note"]
		"[root] aws_iam_role.iam-key-age-role (expand)" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"]"
		"[root] meta.count-boundary (EachMode fixup)" -> "[root] aws_iam_role.iam-key-age-role (expand)"
		"[root] provider[\"registry.terraform.io/hashicorp/aws\"] (close)" -> "[root] aws_iam_role.iam-key-age-role (expand)"
		"[root] provider[\"registry.terraform.io/hashicorp/aws\"]" -> "[root] var.AWS_REGION"
		"[root] root" -> "[root] meta.count-boundary (EachMode fixup)"
		"[root] root" -> "[root] provider[\"registry.terraform.io/hashicorp/aws\"] (close)"
	}
}

What's already built

terraform state list & show will show what Terraform knows about from the state files.

$ terraform state list
aws_iam_policy.iam-key-age-policy
aws_iam_policy_attachment.iam-key-age-attachment
aws_iam_role.iam-key-age-role
$ terraform state show aws_iam_role.iam-key-age-role
# aws_iam_role.iam-key-age-role:
resource "aws_iam_role" "iam-key-age-role" {
    arn                   = "arn:aws:iam::581230658448:role/iam-key-age-role"
    assume_role_policy    = jsonencode(

        ... edited...
        
    )
    create_date           = "2021-12-03T14:48:44Z"
    force_detach_policies = false
    id                    = "iam-key-age-role"
    managed_policy_arns   = []
    max_session_duration  = 3600
    name                  = "iam-key-age-role"
    path                  = "/"
    tags                  = {
        "name" = "iam-key-age-role"
    }
    tags_all              = {
        "BuiltBy" = "Terraform"
        "name"    = "iam-key-age-role"
    }
    unique_id             = "AROAYOVAMAOIMSEBWOIHN"

    inline_policy {}
}

Errors & fixes

No valid credential sources found for AWS Provider.

andrew@mac:prod$ terraform12 init
Initializing modules...

Initializing the backend...

Error: No valid credential sources found for AWS Provider.
	Please see https://terraform.io/docs/providers/aws/index.html for more information on
	providing credentials for the AWS Provider

Fix:-

AWS_PROFILE needed exporting.

andrew@mac:prod$ export AWS_PROFILE=MyProfileName
andrew@mac:prod$ terraform init
Initializing modules...

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

See also:- init phase to include the profile in the Provider section.

Testing something to show in a plan

resource "null_resource" "temporary" {
  triggers = {
    path = "${path.module}/../files"
  }
}

Removing a state lock

If you have interrupted terraform with a Ctrl-C, there may be a state lock left behind. This can be removed thus:-

$ terraform plan
2019/10/16 13:11:19 [INFO] Terraform version: 0.11.11  ac4fff416318bf0915a0ab80e062a99ef3724334

...edited...

Error: Error locking state: Error acquiring the state lock: ConditionalCheckFailedException: The conditional request failed
	status code: 400, request id: VKFDCAN5H28CSJ3NQKO1CUHT3BVV4KQNSO5AEMVJF66Q9ASUAAJG
Lock Info:
  ID:        cece51d7-ee71-0f70-d6cc-0830631ed672

...edited....

Terraform acquires a state lock to protect the state from being written
by multiple users at the same time. Please resolve the issue above and try
again. For most commands, you can disable locking with the "-lock=false"
flag, but this is not recommended.
$


$ terraform force-unlock cece51d7-ee71-0f70-d6cc-0830631ed672
2019/10/16 13:13:04 [INFO] Terraform version: 0.11.11  ac4fff416318bf0915a0ab80e062a99ef3724334
2019/10/16 13:13:04 [INFO] Go runtime version: go1.11.1
2019/10/16 13:13:04 [INFO] CLI args: []string{"/usr/local/bin/terraform", "force-unlock", "cece51d7-ee71-0f70-d6cc-0830631ed672"}

...edited...

Do you really want to force-unlock?
  Terraform will remove the lock on the remote state.
  This will allow local Terraform commands to modify this state, even though it
  may be still be in use. Only 'yes' will be accepted to confirm.

  Enter a value: yes

Terraform state has been successfully unlocked!

The state has been unlocked, and Terraform commands should now be able to
obtain a new lock on the remote state.
$ 

This page has been accessed for:-
Today: 1
Yesterday: 0
Until now: 477

 
terraform/start.txt · Last modified: 19/01/2024 16:36 by andrew