Terraform + Terragrunt Basics
Note Repository
Basics
Commands
-
terraform initcreates the Remote State: configures your local settings, downloads provider requirements, similar to activating a Python virtualenv -
terraform plancompares current state with desired state -
terraform applymakes the necessary API calls to create/delete/update the resources as needed, then polls them periodically until it is in its desired state -
terraform graphcreates a dependency graph with all resources -
terraform destroyremoves everything in the state file
Variables

- since there is no value declared, if you run
terraform plan, it will prompt for input - you can declare a value via CLI with
terraform apply -var name=foo, or inside terraform.tfvars:name = "foo"
Dependencies
- reference a resource by their resource type and their name, eg:
aws_instance.example1

-
aws_eiprequires an instance’s ID to be specified - ID is an attribute of
aws_instancethat Terraform discovers, sets, and exports on creation - terraform creates a dependency graph under the hood to figure out the order to create each resource based on dependencies, which can be viewed with
terraform graph
State
- by default, state is stored locally in
.tfstatefiles, but state can be stored remotely (for collaboration) using S3


-
terraform initconfigures and connects to remote state storage - most remote backends support locking and encryption
- locking: while using Terraform, you can request a lock and no one will be able to change the state
- lock is stored in DynamoDB
- encryption: terraform stores secrets in plaintext inside state file
Modules
- collection of Terraform code that you can reuse, configure, and version control — like blueprints
- module = folder with Terraform files
- convention for Gruntwork modules:
-
vars.tf- specify module inputs
-
main.tf- create resources
-
outputs.tf- specify outputs (can be used as input for other modules)
-

- a source can be a directory or URL
- reference module outputs as attributes of that module
- the
moduleblock needs to specify values for variables that don’t have default value — it cannot be passed via CLI and will not be prompted during plan/apply
Example

-
./main.tf- only three block types: terraform (version), provider “aws”, and module blocks w/ the module’s variables specified inside the block -
./vars.tf- declaration (no values) of variables used insidemain.tf. Since there’s noterraform.tfvars, the values will need to be passed via CLI or prompt -
./outputs.tf- outputs values from a module’s output viamodule.module_label.output_label -
./rails-module/main.tf- has terraform (version) and resource blocks, no provider block needed -
./rails-module/vars.tf- variables used in[./rails-module/main.tf](http://main.tf)and declared here need to be addressed inside module block (see./main.tf) -
./rails-module/outputs.tf- outputs values from./rails-module/main.tfviaresource_type.resource_label_attribute
Module Community Standards
-
variables.tfinstead of vars.tf - description property to each variable and output + a README file
Best Practices
-
planbeforeapply - stage before prod
-
isolated environments

- there is a special data source called
terraform_remote_statethat shares state between environments — you can read outputs from one environment into another
- there is a special data source called
- use versioned modules
-
if modules are defined in a separate repository, different environments can use different versioned URLs

-
the
sourceURL supports versioning using therefparameter to point to a tag or a commit ID
-
- use remote state
- you have to create the bucket yourself — enabling versioning is recommended
- the lock table is automatically created
- you have to create the bucket yourself — enabling versioning is recommended