Simulating infrastructure changes using Terraform

In an earlier recipe, you learned how to manage different environments with Terraform, which is great. But how do we test for changes before applying them?

Terraform has a great internal mechanism that allows us to plan for changes by comparing what our infrastructure code wants and what the remote state includes. That way, we can safely check whether what we thought was a minor modification in our code has in fact a destructive impact (sometimes, some parameters in a resource trigger a full destruction of the resource!).

We'll cover different ways of anticipating, simulating, and targeting changes in our infrastructure, as an added safety check before applying the changes for good.

Getting ready

To step through this recipe, you will need the following:

  • A working Terraform installation
  • An AWS account with an SSH key configured in Terraform (refer to the Chapter 2, Provisioning IaaS with Terraform recipes)
  • An Internet connection

How to do it…

Let's start with a simple CoreOS machine on AWS. We know the AMI ID, we want a single t2.micro host. Let's put that information in the variables.tf file:

variable "aws_coreos_ami" {
  default = "ami-85097ff6"
}

variable "cluster_size" {
  default     = "1"
  description = "Number of nodes in the cluster"
}

variable "aws_instance_type" {
  default     = "t2.micro"
  description = "Instance type"
}

The simplest aws_instance resource we can make is the following in instances.tf:

resource "aws_instance" "coreos" {
  count                       = "${var.cluster_size}"
  ami                         = "${var.aws_coreos_ami}"
  instance_type               = "${var.aws_instance_type}"
  key_name                    = "${aws_key_pair.admin_key.key_name}"
  associate_public_ip_address = true

  tags {
    Name = "coreos_${count.index+1}"
  }
}

Planning

Until now, we've used terraform apply for immediate action. There's another command: terraform plan. It does what it says. It plans for changes, but doesn't apply them:

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.
The Terraform execution plan has been generated and is shown below.
[...]

+ aws_instance.coreos
    ami:                         "ami-85097ff6"
    [...]

+ aws_key_pair.admin_key
    [...]

Plan: 2 to add, 0 to change, 0 to destroy.

So, by planning before applying, we can know what's about to happen to our infrastructure. We're happy about an instance with the right AMI being created, so let's terraform apply.

Now the infrastructure is created, if you run a plan again, it will say nothing should be modified:

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.

aws_key_pair.admin_key: Refreshing state... (ID: admin_key)
aws_instance.coreos: Refreshing state... (ID: i-0f9106905e74a29f7)

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.

A normally operating infrastructure should always be in a state where a terraform plan doesn't want to change anything.

Now let's say we need our infrastructure to evolve, and create an S3 bucket. That would look like this in a file named s3.tf:

resource "aws_s3_bucket" "bucket" {
  bucket = "iacbook"

  tags {
    Name = "IAC Book Bucket"
  }
}

We're not sure about what's about to happen, so let's plan with Terraform, so it's telling us exactly what it's intending to do:

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
[...]

aws_key_pair.admin_key: Refreshing state... (ID: admin_key)
aws_instance.coreos: Refreshing state... (ID: i-0f9106905e74a29f7)

[...]

+ aws_s3_bucket.bucket
    bucket:              "iacbook"
    tags.Name:           "IAC Book Bucket"
    [...]

Plan: 1 to add, 0 to change, 0 to destroy.

The plan looks good—it seems to want to create an S3 bucket named the way we want! Let's terraform apply this and move on.

Quickly simulating changes

We now wonder what would happen if we were to change the number of instances. That's the cluster_size variable, currently set to 1. Instead of messing with the code, we can test the impact of changing that value directly from the command line:

$ terraform plan -var 'cluster_size="2"'
[...]
+ aws_instance.coreos.1
    ami:                         "ami-85097ff6"
    instance_type:               "t2.micro"
    tags.Name:                   "coreos_2"
    [...]
Plan: 1 to add, 0 to change, 0 to destroy.

Good news! It looks like increasing the cluster_size value has the intended effect: creating a new instance.

Now, we wonder legitimately what would be the effect of changing the instance type, from t2.micro to t2.medium:

$ terraform plan -var aws_instance_type="t2.medium"
[...]
-/+ aws_instance.coreos
    [...]
    instance_type:               "t2.micro" => "t2.medium" (forces new resource)

Plan: 1 to add, 0 to change, 1 to destroy.

Ouch! Changing the instance type seems to be a destructive action. Let's work on that later, and add the change to a new file named plan.tfvars:

aws_instance_type="t2.medium"

We know we'd like to propose to change the number of instances to 2, so let's add that to the same file:

aws_instance_type="t2.medium"
cluster_size="2"

We can now test against this file containing all our changes, using the -var-file option:

$ terraform plan -var-file=plan.tfvars
-/+ aws_instance.coreos.0
    instance_type:               "t2.micro" => "t2.medium" (forces new resource)
    tags.Name:                   "coreos_1" => "coreos_1"
    [...]

+ aws_instance.coreos.1
    instance_type:               "t2.medium"
    tags.Name:                   "coreos_2"
    [...]
Plan: 2 to add, 0 to change, 1 to destroy.

Good! You learn that our first instance will be destroyed and recreated to move from t2.micro to t2.medium, and that a second instance will be created with the same values. Let's not apply this, as added fees will be incurred.

Targeting for a specific change

Our colleague asks us if we're sure our proposed changes have no impact specifically on the S3 bucket. Terraform allows us to get an answer to that question very specifically by targeting the resource directly in the planning phase:

$ terraform plan -var-file=plan.tfvars -target="aws_s3_bucket.bucket"
[...]
aws_s3_bucket.bucket: Refreshing state... (ID: iacbook)
[...]
No changes. Infrastructure is up-to-date.
[...]

Our colleague is happy, and we're now sure that this change will do exactly what's intended. We can submit this change for review.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.184.3