使用地图简化 Terraform 变量的编写方式

目录结构
我将terraform/provider/aws/env/stg设为如下。
将variables.tf作为变量文件和执行ec2.tf的主要处理文件都放在同一个目录下。
在每个环境中完全分离文件的概念中,tfstate文件也会分别放在每个环境中。(存储位置是S3)
无需进行模块化。
%tree
.
└── provider
└── aws
└── env
└── stg
├── backend.tf
├── ec2.tf
└── variables.tf
声明。
之前
到目前为止,我一直是这样写Terraform变量文件的。
variable "ami" {
default = "ami-4af5022c"
}
variable "instance_type" {
default = "t2.micro"
}
variable "instance_key" {
default = "id_rsa"
}
实际上,EC2参数不仅仅是这些。如果还考虑到AWS的其他资源,变量文件将会非常长。
作为特点,变量每次都会排列在一起,导致行数增加,读起来困难,看起来也不容易,不易找到。
我用map解决了这个问题。
一个映射值是一个从字符串键到字符串值的查找表。这对于根据其他提供的值选择一个值非常有用。
映射的一个常见用途是根据区域创建一张机器镜像表,如下所示:
variable "images" {
type = "map"
default = {
"us-east-1" = "image-1234"
"us-west-2" = "image-4567"
}
}
完成
可以将资源的值汇总到一个变量中。
声明映射类型可以省略。
variable "ec2_config" {
type = "map" #省略化
default = {
ami = "ami-4af5022c"
instance_type = "t2.micro"
instance_key = "id_rsa"
}
}
通过这样做,每个资源的值都被整合在一起,使得阅读更加简便。
现在让我们来查看执行主要处理的 ec2.tf 文件。
ec2.tf文件中map类型的写法如下。
在之前
这是至今资源定义的写法。
resource "aws_instance" "vtryo-web01" {
ami = "${var.ami}"
instance_type = "${var.instance_type}"
instance_key = "${var.instance_key}"
tags {
Name = "vtryo-web01"
}
}
这是对应于after版本的variable的资源定义方式。
由于使用了map类型来存储变量,因此可以使用lookup函数来引用值。
尽管看起来比以前稍微变长了一点,但只要记住规则(后述),它并不那么复杂,而且更加灵活。
resource "aws_instance" "vtryo-web" {
ami = "${lookup(var.ec2_config, "ami")}"
instance_type = "${lookup(var.ec2_config, "instance_type")}"
key_name = "${lookup(var.ec2_config, "instance_key")}"
tags {
Name = "vtryo-${format("web%02d", count.index + 1)}"
}
请注意
在资源定义中,ami、instance_type和key_name在terraform中具有固定的名称。在variables.tf文件中,变量名是任意的,但这里不能随意决定,所以请注意。
查询
以下是一种使用lookup函数直接指定键的方法。
我正在参考这个。
使用Terraform的output来使用map的方法。
例如,如果我们考虑以下情况:
ami = “${lookup(var.ec2_config, “ami”)}”
假设是这样的话
这句话的意思是要引用 variables.tf 文件中 ec2_config 对应的 ami 值。
请将以下内容用中文进行同义转述,只需提供一种选项:
格式
关于最终行的”vtryo-${format(“web%02d”, count.index + 1)}”,变量存储后的值将是vtryo-web01。
顺便提一下,如果将”web%02d”改为”web%01d”,则会变成vtryo-web1,所以要注意。
进行 Terraform 初始化
好,现在是行动的时间。
% terraform init
Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "aws" (1.7.0)...
The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.aws: version = "~> 1.7"
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
规划 Terraform
% 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.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ aws_instance.vtryo-web
id: <computed>
ami: "ami-4af5022c"
associate_public_ip_address: <computed>
availability_zone: <computed>
ebs_block_device.#: <computed>
ephemeral_block_device.#: <computed>
instance_state: <computed>
instance_type: "t2.micro"
ipv6_address_count: <computed>
ipv6_addresses.#: <computed>
key_name: "id_rsa"
network_interface.#: <computed>
network_interface_id: <computed>
placement_group: <computed>
primary_network_interface_id: <computed>
private_dns: <computed>
private_ip: <computed>
public_dns: <computed>
public_ip: <computed>
root_block_device.#: <computed>
security_groups.#: <computed>
source_dest_check: "true"
subnet_id: <computed>
tags.%: "1"
tags.Name: "vtryo-web01"
tenancy: <computed>
volume_tags.%: <computed>
vpc_security_group_ids.#: <computed>
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
变量已存储!
稍微应用一下
在之前的ec2.tf文件中,我在Name字段中写入了”vtryo-${format(“web%02d”, count.index + 1)}”。输出结果为vtryo-web01。我将在下面写下一种方法,将环境放在开头,形成stg-vtryo-web01。
变量 “env”。
在variables.tf文件中添加变量”env { }”。
variable "env" { }
variable "ec2_config" {
default = {
ami = "ami-4af5022c"
instance_type = "t2.micro"
instance_key = "id_rsa"
}
}
可以在 { } 中加入默认值。如果这样做,写法如下。
variable "env" {
default = "text message..."
}
${var.env}的中文释义是什么?
在ec2.tf文件中,可以写下以下内容。
resource "aws_instance" "vtryo-web" {
ami = "${lookup(var.ec2_config, "ami")}"
instance_type = "${lookup(var.ec2_config, "instance_type")}"
key_name = "${lookup(var.ec2_config, "instance_key")}"
tags {
Name = "${var.env}-vtryo-${format("web%02d", count.index + 1)}"
}
}
通过添加 ${var.env} ,您在进行 terraform plan 时将被要求输入。
输入的内容将存储在 ${var.env} 变量中。
terraform 计划
因为需要求一个值,所以我选择了输入一个值。
这样,字符串”stg”将会被存储。
% terraform plan
var.env
Enter a value: stg
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.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
+ aws_instance.vtryo-web
id: <computed>
ami: "ami-4af5022c"
associate_public_ip_address: <computed>
availability_zone: <computed>
ebs_block_device.#: <computed>
ephemeral_block_device.#: <computed>
instance_state: <computed>
instance_type: "t2.micro"
ipv6_address_count: <computed>
ipv6_addresses.#: <computed>
key_name: "id_rsa"
network_interface.#: <computed>
network_interface_id: <computed>
placement_group: <computed>
primary_network_interface_id: <computed>
private_dns: <computed>
private_ip: <computed>
public_dns: <computed>
public_ip: <computed>
root_block_device.#: <computed>
security_groups.#: <computed>
source_dest_check: "true"
subnet_id: <computed>
tags.%: "1"
tags.Name: "stg-vtryo-web01"
tenancy: <computed>
volume_tags.%: <computed>
vpc_security_group_ids.#: <computed>
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
顺便说一下,您可以使用 “terraform plan -var”env=stg” ” 来实现无需输入即可存储。
这次的补充或者说是闲谈
「Terraform的最佳实践,你觉得难懂吗?」
我知道开发者推荐的配置是最佳实践,但我有这样的直觉。我当然也读了在Qiita上发布的文章《2017年的Terraform最佳实践》。
文件中需要多次声明,让人感觉阅读困难。此外,据官方说法,“通过Environment进行环境分离”的结构。
工作空间可以用来管理开发、暂存和生产之间的小差异,但不应将其视为唯一的隔离机制。
根据我们的Google翻译,它的意思是这样的。
工作区可以用於管理开发、暂存和生产之间的小差异,但不应将其视为唯一的分离机制。
这是一个需要经验和技巧才能初次解读的内容。或许只有多次使用terraform才能理解吧。
嗯,可能更有可能的是我的技术力还跟不上(爆)。
然而,并不是每个人都拥有高超的Terraform技术能力,因此我认为写作更易读、更易维护是可以的。
考虑到一旦出错,terraform可能会意外删除基础设施,因此感到最好还是确保安全的心情。
因为在这次的情况下,Qiita的文章给了我很大的帮助,所以我决定在Qiita上写。
我想简单地表达我的欲望。
我认为Terraform具有可以进行模块化的功能,这增加了一些难度。
尽管将变量进行模块化有其优点,但若无法使用,则毫无意义。Terraform提供了模块注册表,但若不理解其机制,使用仍然会很困难。
如果学习成本太高,倒不如干脆不用,简单地编写代码来解决问题。
不执着于最佳实践
我认为重要的是可读性和易于上手,所以我故意不固执地构建了目录结构,考虑了如何更容易地编写terraform。
希望对他人有所帮助。
请提供更多的上下文,以便我可以为您提供中文的适当解释。
利用Map在Terraform的output中的方法 – Qiita
2017年Terraform最佳实践 – Qiita
分割Terraform中的AWS环境设置 – Qiita
更进一步享受Terraform的乐趣。使用模块简化IAM用户和策略管理。
本文是此处的克隆。