Skip to content
This repository has been archived by the owner on Jul 26, 2020. It is now read-only.

Latest commit

 

History

History
361 lines (301 loc) · 10.9 KB

terraform.org

File metadata and controls

361 lines (301 loc) · 10.9 KB

Terraform

Table of Contents

Configurations

path

source_file = "${path.cwd}/main.py"   # Path to the current working directory
source_file = "${path.root}/main.py"  # Path to the root module
source_file = "${path.module}/main.py" # Path to the current module

element

vars {
  ip = "${element(aws_instance.cluster.*.private_ip, count.index)}"
}

ignore_changes

There was a problem when I defined multiple aws_eip s which are associated to aws_instance s.

resource "aws_instance" "foo" {
  count = 10
  ..
}

resource "aws_eip" "bar" {
  count = 10
  instance = "${element(aws_instance.foo.*.i, count.index}"
}

Terraform plans to change the all association when I only change the count. To work around this, use ignore_changes

resource "aws_eip" "bar" {
  count = 10
  instance = "${element(aws_instance.foo.*.i, count.index}"
  lifecycle {
    ignore_changes = ["instance"]
  }
}

null_resource

local-exec

provisioner "local-exec" {
  command = "run.sh ${var.args}"
}

remote-exec

connection {
  type        = "ssh"
  user        = "ubuntu"
  private_key = "${file(var.key_path)}"
}

provisioner "remote-exec" {
  inline = [
    "curl -sSL https://get.docker.com/ | sh",
  ]
}

archive_file

data "archive_file" "code" {
  type        = "zip"
  source_file = "${path.module}/main.py"
  output_path = "${path.module}/lambda.zip"
}
resource "aws_lambda_function" "main" {
  function_name    = "foo"
  filename         = "${data.archive_file.code.output_path}"
  source_code_hash = "${data.archive_file.code.output_base64sha256}"
  ...
}

template_file

data "template_file" "curl" {
  count    = "${var.count}"
  template = "curl http://$${ip}"
  vars {
    ip = "${element(aws_instance.cluster.*.private_ip, count.index)}"
  }
}

Commands

plan

terraform plan
terraform plan -var 'access_key=foo' -var 'secret_key=bar'
terraform plan -var 'amis={us-east-1 = "foo", us-west-2 = "bar"}'
terraform plan -out=my.plan

apply

terraform apply
terraform apply 'my.plan'

import

terraform import aws_instance.main i-abcd1234

state

mv

# from ./terraform.tfstate:aws_instance.main
# to new/terraform.tfstate:aws_instance.server
terraform state mv -state-out new/terraform.tfstate \
          aws_instance.main \
          aws_instance.server

taint

terraform taint aws_instance.main                                                                              1 ↵
terraform taint -module=my_module aws_instance.main                                                                              1 ↵

Concepts

Terraform files

  • All .tf files are loaded
  • .tf files are declarative, so the order of loading files doesn’t matter, except for Override files
  • Override files are .tf files named as override.tf or {name}_override.tf
  • Override files are loaded last in alphabetical order
  • Configurations in override files are merged into the existing configuration, not appended.

Resources and Data Sources

  • Resources are infrastructures managed by terraform
  • Data sources are not managed by terraform

The use case of these things are following:

You can provision servers by defining them as resources.
For specifying server configurations, you can reference existing security groups, VPCs, and the like by defining them as data sources.

State file

  • State about the real managed infrastructure
  • terraform.tfstate by default
  • Formatted in json
  • While terraform files are about to be, state file is about as is
  • State is refreshed before performing most of operations like terraform plan, terraform apply
  • Basic modifications can be done through terraform state [sub] commands
  • Importing existing infrastructures can be done using terraform state import
    • Importing is related to resources, not data sources
    • Which means terraform can destroy the existing infrastructures once they are imported

Variable files

  • A file named terraform.tfvars is automatically loaded
  • Use -var-file flag to specify other .tfvars files

Resource Addressing

[module path][resource spec]
module.A.module.B.module.C...
resource_type.resource_name[N]
resource "aws_instance" "web" {
  # ...
  count = 4
}
aws_instance.web[3]
aws_instance.web

Interpolation

${self.private_ip_address}  # attributes of their own
${aws_instance.web.id}
${aws_instance.web.0.id}    # a specific one when the resource is plural('count' attribute exists)
${aws_instance.web.*.id}    # this is a list
${module.foo.bar}           # outputs from module
.. and many more including some functions

Modules

  • https://www.terraform.io/docs/modules/create.html
  • When you run terraform apply, the current working directory holding the Terraform files is called the root module.
  • With Local File Paths, Terraform will create a symbolic link to the original directory. Therefore, any changes are automatically available.

Limitations

No dynamic interpolation over count

For now, you can’t use interpolation for referencing other resources to specify count because of the way that terraform handles count.

variable my_count {
  default = 10
}

resource "something" "foo" {
  count = "${var.my_count}"   # ok
}

resource "something" "bar" {
  count = "${something.foo.count}"  # error
}

We should definitely do this, the tricky part comes from the fact that count expansion is currently done statically, before the primary graph walk, which means we can’t support “computed” counts right now. (A “computed” value in TF is one that’s flagged as not known until all its dependencies are calculated.)

No integer variable

No list of maps

variable "cluster_config" {
  type = "map"
}

resource aws_elasticsearch_domain "main" {
  cluster_config = "${var.cluster_config}"  # Not supported
}

Because the actual schema is:

"cluster_config": {
	  Type:     schema.TypeList,
	  Optional: true,
	  Computed: true,
	  Elem: &schema.Resource{
		  Schema: map[string]*schema.Schema{

Use Cases

Migrate existing AWS Infra with terraforming

Change an AWS ec2 instance type without destroying-recreating it

Terraform currently doesn’t support changing the instance type without destroying-recreating it.

We should change it manually, and sync it with the terraform state to change the instance type without destroying the instance.


For syncing, you have to remove its state from tfstate and import it again. Follow the steps below:

  1. Stop the target instance and change its instance type to what you desire. img/screenshot_2017-01-31_13-41-13.png
  2. Update your tf file as you changed:
    resource "aws_instance" "my_instance" {
      (...)
      instance_type = "t2.mirco"  # as you changed at step 1
      (...)
    }
        
  3. Verify it with terraform plan
    $ terraform plan
    (...)
    
    No changes. Infrastructure is up-to-date. This means that Terraform
    could not detect any differences between your configuration and
    the real physical resources that exist. As a result, Terraform
    doesn't need to do anything.
        

If plan shows some unexpected changes, you can just remove the instance from tfstate and re-import it.

  1. Remove the instance from terraform.tfstate:
    $ terraform state rm aws_instance.my_instance
        
  2. Import your instance
    $ terraform import aws_instance.my_instance i-abcdefg012345678