momo's Blog.

TerraForm学习笔记

字数统计: 1.5k阅读时长: 6 min
2021/12/27 Share

前言

这里记录TerraForm学习笔记

语法

注释

  • # 单行注释
  • // 单行注释
  • /* */ 多行注释

Resource

语法

1
2
3
4
resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}

provider

声明模块使用哪些provider

1
2
3
4
5
6
7
8
terraform {
required_providers {
mycloud = {
source = "mycorp/mycloud"
version = "~> 1.0"
}
}
}

Meta-Arguments

超时

1
2
3
4
5
6
7
8
resource "aws_db_instance" "example" {
# ...

timeouts {
create = "60m"
delete = "2h"
}
}

Behavior

Terraform 自身维护了一个 state的数据库, 来映射真实存在的物理资源。通过与数据库中的资源对比,随后提供相应资源curd的功能。 同样,我们也可以通过 <RESOURCE TYPE>.<NAME>.<ATTRIBUTE> 方法来引用对应的资源。

Provisioners

Provisioners可以在本地主机或者远程主机上执行特定的操作。

TerraForm不建议使用此方式,因为这些为TerraForm增加了相当多的负责性和不确定性。

如何使用?

1
2
3
4
5
6
7
resource "aws_instance" "web" {
# ...

provisioner "local-exec" {
command = "echo The server's IP address is ${self.private_ip}"
}
}

self 对象

provisioner 块中不能通过名称引用父资源,不过可以通过 self 对象。

比如上方实例,通self.private_ip 引用了一个aws_instanceprivate_ip 属性。

Provisioner 连接设置

在我们使用的过程,肯定是需要指定远程服务器的连接方式,tf提供了相关的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Copies the file as the root user using SSH
provisioner "file" {
source = "conf/myapp.conf"
destination = "/etc/myapp.conf"

connection {
type = "ssh"
user = "root"
password = "${var.root_password}"
host = "${var.host}"
}
}

# Copies the file as the Administrator user using WinRM
provisioner "file" {
source = "conf/myapp.conf"
destination = "C:/App/myapp.conf"

connection {
type = "winrm"
user = "Administrator"
password = "${var.admin_password}"
host = "${var.host}"
}
}
  • connection 在resource中,作用域在resource中所有的provisioners
  • connection 在provisioner 中,作用域仅仅在当前的provisioner

相关属性配置详解

没有resource的provisioner

如果您需要运行不与特定资源直接关联的provisioner,您可以将它们与null_resource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
resource "aws_instance" "cluster" {
count = 3

# ...
}

resource "null_resource" "cluster" {
# Changes to any instance of the cluster requires re-provisioning
triggers = {
cluster_instance_ids = "${join(",", aws_instance.cluster.*.id)}"
}

# Bootstrap script can run on any instance of the cluster
# So we just choose the first in this case
connection {
host = "${element(aws_instance.cluster.*.public_ip, 0)}"
}

provisioner "remote-exec" {
# Bootstrap script called with private_ip of each node in the cluster
inline = [
"bootstrap-cluster.sh ${join(" ", aws_instance.cluster.*.private_ip)}",
]
}
}

file provisioner

执行文件copy操作

示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
resource "aws_instance" "web" {
# ...

# Copies the myapp.conf file to /etc/myapp.conf
provisioner "file" {
source = "conf/myapp.conf"
destination = "/etc/myapp.conf"
}

# Copies the string in content into /tmp/file.log
provisioner "file" {
content = "ami used: ${self.ami}"
destination = "/tmp/file.log"
}

# Copies the configs.d folder to /etc/configs.d
provisioner "file" {
source = "conf/configs.d"
destination = "/etc"
}

# Copies all files and folders in apps/app1 to D:/IIS/webapp1
provisioner "file" {
source = "apps/app1/"
destination = "D:/IIS/webapp1"
}
}

注意,目录写法有一个规范。

  1. 当源目录以/号结尾的时候,比如/a/b//c, 则会把 /b 下所有的文件copy到 /c.
  2. 当源目录不移/号结尾,比如/a/b/c, 则会把整个目录复制过去,最终会是 /c/b

详细参数说明

local-exec Provisioner

tf会调用本地的命令去执行,而不是远程主机

1
2
3
4
5
6
7
resource "aws_instance" "web" {
# ...

provisioner "local-exec" {
command = "echo ${self.private_ip} >> private_ips.txt"
}
}

详细参数说明

remote-exec Provisioner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
resource "aws_instance" "web" {
# ...

# Establishes connection to be used by all
# generic remote provisioners (i.e. file/remote-exec)
connection {
type = "ssh"
user = "root"
password = var.root_password
host = self.public_ip
}

provisioner "remote-exec" {
inline = [
"puppet apply",
"consul join ${aws_instance.web.private_ip}",
]
}
}

详细参数说明

Data Sources

详细参数说明

Variables and Outputs

输入变量

声明变量

文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
variable "image_id" {
type = string
}

variable "availability_zone_names" {
type = list(string)
default = ["us-west-1a"]
}

variable "docker_ports" {
type = list(object({
internal = number
external = number
protocol = string
}))
default = [
{
internal = 8300
external = 8300
protocol = "tcp"
}
]
}

使用变量

可以通过var.<NAME>来使用。

1
2
3
4
resource "aws_instance" "example" {
instance_type = "t2.micro"
ami = var.image_id
}

为变量赋值的方法

  1. 单独使用-var 命令行选项
  2. 在变量定义(.tfvars)文件中,在命令行指定或自动加载
  3. 作为环境变量

命令行上的变量

1
2
3
terraform apply -var="image_id=ami-abc123"
terraform apply -var='image_id_list=["ami-abc123","ami-def456"]' -var="instance_type=t2.micro"
terraform apply -var='image_id_map={"us-east-1":"ami-abc123","us-east-2":"ami-def456"}'

变量定义(.tfvars)文件

这些文件放置的是已经声明过的变量赋值

1
terraform apply -var-file="testing.tfvars"

.tfvars

1
2
3
4
5
image_id = "ami-abc123"
availability_zone_names = [
"us-east-1a",
"us-west-1c",
]

如果存在这些文件,tf会自动加载许多定义的变量文件

  • terraform.tfvarsterraform.tfvars.json命名的文件
  • .auto.tfvars.auto.tfvars.json 结尾的任何文件

环境变量的方式

TerraForm会搜索以TF_VAR_ 开头的所有环境变量

比如:

1
export TF_VAR_image_id=ami-abc123

变量优先级

  • 环境变量
  • terraform.tfvars文件
  • terraform.tfvars.json文件
  • 按文件名的词法顺序处理的任何.auto.tfvars或.auto.tfvars.json文件
  • 命令行上的任何-var和-var-file选项,按提供的顺序排列。

Output Values

文档
Output values 有多种用途

  • 子模块可以使用输出将其资源属性的子集公开给父模块。
  • 根模块可以在运行后使用输出在 CLI 输出中打印某些值 terraform apply
1
2
3
output "instance_ip_addr" {
value = aws_instance.server.private_ip
}

local values

声明一个本地value

1
2
3
4
locals {
service_name = "forum"
owner = "Community Team"
}
1
2
3
4
5
6
7
8
9
10
11
12
locals {
# Ids for multiple sets of EC2 instances, merged together
instance_ids = concat(aws_instance.blue.*.id, aws_instance.green.*.id)
}

locals {
# Common tags to be assigned to all resources
common_tags = {
Service = local.service_name
Owner = local.owner
}
}

使用一个local values

1
2
3
4
5
resource "aws_instance" "example" {
# ...

tags = local.common_tags
}

Modules

表达式

文档

for

  • 生成列表
1
2
3
4
5
6
7
8
9
// 列表
[for s in var.list : upper(s)]


// 对于字典类型
[for k, v in var.map : length(k) + length(v)]

// 对于列表类型
[for i, v in var.list : "${i} is ${v}"]
  • 生成字典(map)
1
{for s in var.list : s => upper(s)}
1
2
3
4
5
{
foo = "FOO"
bar = "BAR"
baz = "BAZ"
}
CATALOG
  1. 1. 前言
  2. 2. 语法
    1. 2.1. 注释
  3. 3. Resource
    1. 3.1. 语法
    2. 3.2. provider
    3. 3.3. Meta-Arguments
    4. 3.4. 超时
    5. 3.5. Behavior
    6. 3.6. Provisioners
      1. 3.6.1. 如何使用?
      2. 3.6.2. self 对象
      3. 3.6.3. Provisioner 连接设置
      4. 3.6.4. 没有resource的provisioner
      5. 3.6.5. file provisioner
      6. 3.6.6. local-exec Provisioner
      7. 3.6.7. remote-exec Provisioner
  4. 4. Data Sources
  5. 5. Variables and Outputs
    1. 5.1. 输入变量
      1. 5.1.1. 声明变量
      2. 5.1.2. 使用变量
      3. 5.1.3. 为变量赋值的方法
    2. 5.2. Output Values
    3. 5.3. local values
  6. 6. Modules
  7. 7. 表达式
    1. 7.1. for