Hashicorp Configuration Language: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(63 intermediate revisions by the same user not shown)
Line 4: Line 4:
* [[Terraform_Concepts#Hashicorp_Configuration_Language_.28HCL.29|Terraform Concepts]]
* [[Terraform_Concepts#Hashicorp_Configuration_Language_.28HCL.29|Terraform Concepts]]
=Overview=
=Overview=
HCL (HashiCorp Configuration Language) is a specialized language for building infrastructure-as-code. It is not a fully featured programming language, so it requires understanding specific patterns to perform certain tasks, like conditional resources. The language supports re-usable modules.
=Comments=
# This is a comment


=Block=
=Block=
=Interpolation Expression=
==Terraform Block==
 
<syntaxhighlight lang='json'>
terraform {
  required_version = ">= 0.12"
}
</syntaxhighlight>
 
===Backend Configuration===
 
[[Terraform_Concepts#Backend|Backends]] are configured in Terraform files in the [[#Terraform_Block|terraform block]]. Only one backend may be specified and the configuration may not contain interpolations.
 
<syntaxhighlight lang='json'>
terraform {
  backend "local" {
    path = ".../my-state.tfstate"
  }
}
</syntaxhighlight>
 
==Resource Block==
 
Declares a [[Terraform Concepts#Resource|resource]].
 
==Data Block==
 
Declares a [[Terraform Concepts#Data_Source|data source]].
 
data "''data-source-name''" "''local-name''" {
  ...
}
 
<syntaxhighlight lang='json'>
data "aws_ami" "example" {
 
  most_recent = true
 
  owners = ["self", "0000000"]
 
  filter {
    name  = "name"
    values = ["consul-ubuntu-*"]
  }
 
  tags = {
    Name  = "app-server"
    Tested = "true"
  }
}
</syntaxhighlight>
 
<syntaxhighlight lang='json'>
data "aws_eks_cluster" "cluster" {
  name = module.eks.cluster_id
}
</syntaxhighlight>
 
===Data Sources===
====terraform_remote_state====
{{External|https://www.terraform.io/docs/providers/terraform/d/remote_state.html}}
This data source retrieves [[Terraform_Concepts#Remote_State|remote state]] from a Terraform [[Terraform_Concepts#Backend|backend]].
 
==Module Block==
 
Represents a [[Terraform_Concepts#Using_a_Module|module call]], referring to a dependency module that is going to be provisioned within the context of the current module.
 
<syntaxhighlight lang='json'>
module "some-module" {
 
  source = "terraform-aws-modules/eks/aws"
  version = "~> 9.0"
 
  cluster_name = local.cluster_name
  vpc_id = var.aws_vpc_id
  ...
}
</syntaxhighlight>
 
More more details: {{Internal|Terraform Module Block Syntax|Module Block Syntax}}
 
=<span id='Interpolation_Expression'></span>References to Named Values or Interpolation Expression=
 
{{External|https://www.terraform.io/docs/configuration/expressions.html#references-to-named-values}}
 
Resource elements can be used by other resources via interpolation expressions:
 
<syntaxhighlight lang='text'>
resource "something" "something_else" {
 
  some_id = "something-${aws_vpc.main.id}"
  ...
}
</syntaxhighlight>
 
Terraform 0.11 and earlier required all non-constant expressions to be provided via interpolation syntax ${...}, but this pattern is now deprecated in templates that consist entirely of a single interpolation sequence:
 
<syntaxhighlight lang='text'>
resource "aws_subnet" "something" {
 
  ami = var.k8s_node_ami_id
  ...
}
</syntaxhighlight>
 
Template interpolation syntax is still used to construct strings from expressions when the template includes multiple interpolation sequences or a mixture of literal strings and interpolations


=Input Variable=
=Input Variable=
{{External|https://www.terraform.io/docs/configuration/variables.html}}
Input variables are conventionally declared in the [[Writing_a_Terraform_Module#variables.tf|variables.tf]] file of the module:


<syntaxhighlight lang='text'>
<syntaxhighlight lang='json'>
variable "region" {
variable "region" {
  description = "This is the region"
  type = string
   default = "us-east-1"
   default = "us-east-1"
}
}
</syntaxhighlight>
</syntaxhighlight>
Each variable can have a default value specified with "default". "default" may be "null". If there is no "default" declaration, Terraform will signal an error when terraform apply is executed, and it will initiate an interactive CLI that will inquire for the value of the missing variable:
'''var.environment_name'''
  The name of the environment
  '''Enter a value:'''
<syntaxhighlight lang='text'>
variable "masters" {
  description = "The number of master nodes"
  type = number
  default = 1
}
</syntaxhighlight>
A default value of 0 for the case where that input variable specifies a count of some sort, is valid. For a module that creates EC2 instances, specifying 0 for the default count value simply does not create any instance of that kind.
Variables can be used via [[#Interpolation_Expression|interpolation expression]], prefixed with “var.”


To use the variable:
To use the variable:
<syntaxhighlight lang='text'>
<syntaxhighlight lang='text'>
region= var.region
...
Name = "${var.playground_name}-playground-vpc"
...
</syntaxhighlight>
</syntaxhighlight>
This is also an [[#Interpolation_Expression|interpolation expression]], prefixed with “var.”


Variables can be assigned in multiples way, and this is the descending order of precedence:
Variables can be assigned in multiples way, and this is the descending order of precedence:
* command-line flags (-var = ‘region=something’)
* command-line flags: <code>-var = region=something)</code> It will complain if single quotes are used, they will be interpreted as part of the variable name.
* from a file (.tfvars, -var-file=…) terraform.tfvars or *.auto.tfvars in the current directory are automatically loaded. Multiple -var-file can be used.
* from a file (.tfvars, -var-file=…) terraform.tfvars or *.auto.tfvars in the current directory are automatically loaded. Multiple -var-file can be used.
* From environment variables: TF_VAR_name. This can only be used for string variables.
* From environment variables: TF_VAR_name. This can only be used for string variables.
Line 30: Line 163:
* Variable defaults (“default” keyword).
* Variable defaults (“default” keyword).


==Lists==
==<span id='Lists'><span>Input Lists==
{{External|https://learn.hashicorp.com/terraform/getting-started/variables#lists}}
{{External|https://learn.hashicorp.com/terraform/getting-started/variables#lists}}


==Maps==
<syntaxhighlight lang='json'>
variable "security_group_ids" {
  description = "The list of security groups"
  type        = list
  default    = ["sg-a41f9d51704199e97"]
}
</syntaxhighlight>
 
==<span id='Maps'><span>Input Maps==
{{External|https://learn.hashicorp.com/terraform/getting-started/variables#maps}}
{{External|https://learn.hashicorp.com/terraform/getting-started/variables#maps}}


=Output Variable=
=Output Variable=
{{External|https://www.terraform.io/docs/configuration/outputs.html}}
{{External|https://learn.hashicorp.com/terraform/getting-started/outputs}}
Output variables are a way to organize data to be easily queried and shown back to the Terraform user. Terraform usually stores hundred or thousands of attribute values, but only a few of those are important. Output variables have several uses: a child [[Terraform Concepts#Module|module]] can use outputs to expose a subset of its resource attributes to a parent module, a root module ca use outputs to print certain values in the CLI output after running <code>terraform apply</code> and when using [[Terraform_Concepts#Remote_State|remote state]], module outputs can be accessed by other configurations via a <code>terraform_remote_state</code> data source.
"Output value" and "output" are semantically equivalent.
An output is declared as:
<syntaxhighlight lang='text'>
output "bastion_public_ip" {
  value = aws_instance.bastion.public_ip
  description = "The public IP address of the bastion"
}
</syntaxhighlight>
Outputs can be delegated to other teams via [[Terraform_Concepts#Remote_State|remote state]].
Also see: {{Internal|Terraform Operations#Output|terraform output}}
==Output Lists==
<syntaxhighlight lang='text'>
output "master_node_private_ips" {
  value = aws_instance.master-node.*.private_ip
  description = "The private IP of the master nodes"
}
</syntaxhighlight>
Result:
<syntaxhighlight lang='text'>
Outputs:
master_node_private_ips = [
    "10.1.13.51",
]
worker_node_private_ips = [
    "10.1.13.153",
    "10.1.13.61",
]
</syntaxhighlight>
==Output Maps==
=Local Values=
A local value assigns a name to an expression, allowing it to be used multiple times within a [[Terraform_Concepts#Module_Local_Values|module]] without repeating it.
<font color=darkgray>TODO: https://www.terraform.io/docs/configuration/locals.html</font>
<syntaxhighlight lang='json'>
locals {
  cluster_name = var.cluster_name != null ? var.cluster_name : var.environment_name
  elb_tag = "Key=kubernetes.io/cluster/${local.cluster_name},Value=shared"
  asg_enabled  = length([for wg in var.worker_groups: wg.name if wg.autoscaling_enabled]) != 0
}
</syntaxhighlight>
=Functions=
{{External|https://www.terraform.io/docs/configuration/functions.html}}
{{External|https://www.terraform.io/docs/configuration/expressions.html#function-calls}}
==Function Categories==
===String===
====substr====
{{External|https://www.terraform.io/docs/configuration/functions/substr.html}}
<syntaxhighlight lang='text'>
...
cidr_block = "${substr("${var.my_cidr_block}", 0, 5)}.1.0/24"
...
</syntaxhighlight>
===IP Network===
====cidrsubnet====
{{External|https://www.terraform.io/docs/configuration/functions/cidrsubnet.html}}
The following turns "10.10.0.0/16" into its first /24 subnet "10.10.1.0/24"
<syntaxhighlight lang='text'>
...
cidr_block = "${cidrsubnet("${var.vpc_cidr_block}", 8, 1)}"
...
</syntaxhighlight>

Latest revision as of 23:18, 14 December 2021

External

Internal

Overview

HCL (HashiCorp Configuration Language) is a specialized language for building infrastructure-as-code. It is not a fully featured programming language, so it requires understanding specific patterns to perform certain tasks, like conditional resources. The language supports re-usable modules.

Comments

# This is a comment

Block

Terraform Block

terraform {
  required_version = ">= 0.12"
}

Backend Configuration

Backends are configured in Terraform files in the terraform block. Only one backend may be specified and the configuration may not contain interpolations.

terraform {
  backend "local" {
    path = ".../my-state.tfstate"
  }
}

Resource Block

Declares a resource.

Data Block

Declares a data source.

data "data-source-name" "local-name" {
  ...
}
data "aws_ami" "example" {

  most_recent = true

  owners = ["self", "0000000"]

  filter {
    name   = "name"
    values = ["consul-ubuntu-*"]
  }

  tags = {
    Name   = "app-server"
    Tested = "true"
  }
}
data "aws_eks_cluster" "cluster" {
  name = module.eks.cluster_id
}

Data Sources

terraform_remote_state

https://www.terraform.io/docs/providers/terraform/d/remote_state.html

This data source retrieves remote state from a Terraform backend.

Module Block

Represents a module call, referring to a dependency module that is going to be provisioned within the context of the current module.

module "some-module" {

  source = "terraform-aws-modules/eks/aws"
  version = "~> 9.0"

  cluster_name = local.cluster_name
  vpc_id = var.aws_vpc_id
  ...
}

More more details:

Module Block Syntax

References to Named Values or Interpolation Expression

https://www.terraform.io/docs/configuration/expressions.html#references-to-named-values

Resource elements can be used by other resources via interpolation expressions:

resource "something" "something_else" {

  some_id = "something-${aws_vpc.main.id}"
  ...
}

Terraform 0.11 and earlier required all non-constant expressions to be provided via interpolation syntax ${...}, but this pattern is now deprecated in templates that consist entirely of a single interpolation sequence:

resource "aws_subnet" "something" {

  ami = var.k8s_node_ami_id
  ...
}

Template interpolation syntax is still used to construct strings from expressions when the template includes multiple interpolation sequences or a mixture of literal strings and interpolations

Input Variable

https://www.terraform.io/docs/configuration/variables.html

Input variables are conventionally declared in the variables.tf file of the module:

variable "region" {
  description = "This is the region"
  type = string
  default = "us-east-1"
}

Each variable can have a default value specified with "default". "default" may be "null". If there is no "default" declaration, Terraform will signal an error when terraform apply is executed, and it will initiate an interactive CLI that will inquire for the value of the missing variable:

var.environment_name
  The name of the environment

  Enter a value:
variable "masters" {
  description = "The number of master nodes"
  type = number
  default = 1
}

A default value of 0 for the case where that input variable specifies a count of some sort, is valid. For a module that creates EC2 instances, specifying 0 for the default count value simply does not create any instance of that kind.

Variables can be used via interpolation expression, prefixed with “var.”

To use the variable:

...
Name = "${var.playground_name}-playground-vpc"
...

Variables can be assigned in multiples way, and this is the descending order of precedence:

  • command-line flags: -var = region=something) It will complain if single quotes are used, they will be interpreted as part of the variable name.
  • from a file (.tfvars, -var-file=…) terraform.tfvars or *.auto.tfvars in the current directory are automatically loaded. Multiple -var-file can be used.
  • From environment variables: TF_VAR_name. This can only be used for string variables.
  • UI Input
  • Variable defaults (“default” keyword).

Input Lists

https://learn.hashicorp.com/terraform/getting-started/variables#lists
variable "security_group_ids" {
  description = "The list of security groups"
  type        = list
  default     = ["sg-a41f9d51704199e97"]
}

Input Maps

https://learn.hashicorp.com/terraform/getting-started/variables#maps

Output Variable

https://www.terraform.io/docs/configuration/outputs.html
https://learn.hashicorp.com/terraform/getting-started/outputs

Output variables are a way to organize data to be easily queried and shown back to the Terraform user. Terraform usually stores hundred or thousands of attribute values, but only a few of those are important. Output variables have several uses: a child module can use outputs to expose a subset of its resource attributes to a parent module, a root module ca use outputs to print certain values in the CLI output after running terraform apply and when using remote state, module outputs can be accessed by other configurations via a terraform_remote_state data source.

"Output value" and "output" are semantically equivalent.

An output is declared as:

output "bastion_public_ip" {
  value = aws_instance.bastion.public_ip
  description = "The public IP address of the bastion"
}

Outputs can be delegated to other teams via remote state.

Also see:

terraform output

Output Lists

output "master_node_private_ips" {
  value = aws_instance.master-node.*.private_ip
  description = "The private IP of the master nodes"
}

Result:

Outputs:

master_node_private_ips = [
    "10.1.13.51",
]
worker_node_private_ips = [
    "10.1.13.153",
    "10.1.13.61",
]

Output Maps

Local Values

A local value assigns a name to an expression, allowing it to be used multiple times within a module without repeating it.

TODO: https://www.terraform.io/docs/configuration/locals.html

locals {
  cluster_name = var.cluster_name != null ? var.cluster_name : var.environment_name
  elb_tag = "Key=kubernetes.io/cluster/${local.cluster_name},Value=shared"
  asg_enabled  = length([for wg in var.worker_groups: wg.name if wg.autoscaling_enabled]) != 0
}

Functions

https://www.terraform.io/docs/configuration/functions.html
https://www.terraform.io/docs/configuration/expressions.html#function-calls


Function Categories

String

substr

https://www.terraform.io/docs/configuration/functions/substr.html
...
cidr_block = "${substr("${var.my_cidr_block}", 0, 5)}.1.0/24"
...

IP Network

cidrsubnet

https://www.terraform.io/docs/configuration/functions/cidrsubnet.html

The following turns "10.10.0.0/16" into its first /24 subnet "10.10.1.0/24"

...
cidr_block = "${cidrsubnet("${var.vpc_cidr_block}", 8, 1)}"
...