使用 Terraformer 将现有基础设施资源导入 Terraform

目录
我叫寺冈,是一名基础设施工程师。
在上一篇文章中,我介绍了 terraform import 命令,它是一种将现有资源导入 Terraform 的方法
正如本文摘要中所述,
导入命令只会重写 tfstate 文件,因此
您需要在检查与 tfstate 文件差异的同时,手动编写 tf 文件。
如果文件数量庞大,这将耗费大量时间,成为一个问题。
我有个好消息要告诉大家:
一款名为 Terraformer 的工具已经作为开源软件发布了。
https://github.com/GoogleCloudPlatform/terraformer
用于从现有基础设施生成 Terraform 文件的 CLI 工具(反向 Terraform)。基础设施到代码
如上所述,它似乎是一个命令行工具,可以根据现有基础设施自动生成 Terraform 文件。
使用说明也已在 GitHub 上提供,让我们来试用一下。
安装
在 Mac 上,您可以使用 brew 命令进行安装。
$ brew install terraformer $ terraformer version Terraformer v0.8.7
目前为止一切顺利。
我们将使用 v0.8.7 版本。
基础架构配置
我使用 Terraformer 预先创建了要导入的基础设施。
https://github.com/beyond-teraoka/terraform-aws-multi-environment-sample
配置图

我在同一个 AWS 账户中有三个环境。
- 发展
- 生产
- 管理
此外,每个环境都具有以下资源:
- 专有网络
- 子网
- 路线表
- 互联网网关
- NAT网关
- 安全组
- VPC 对等互连
- EIP
- EC2
- 白蛋白
- RDS
虽然图中未显示,但每个环境的资源都带有 Environment 标签,
其值分别设置为 dev、prod 和 mng。
准备认证信息
请准备您的 AWS 凭证。
请根据您的环境进行准备。
$ cat /Users/yuki.teraoka/.aws/credentials [beyond-poc] aws_access_key_id = XXXXXXXXXXXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [beyond-poc-admin] role_arn = arn:aws:iam::XXXXXXXXXXXX:role/XXXXXXXXXXXXXXXXXXXXXX source_profile = beyond-poc
运行 Terraformer
首先,请尝试按照 GitHub 上的示例运行它。
$ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin 2020/05/05 18:29:14 aws 正在导入区域 ap-northeast-1 2020/05/05 18:29:14 aws 正在导入... vpc 2020/05/05 18:29:15 打开 /Users/yuki.teraoka/.terraform.d/plugins/darwin_amd64:没有该文件或目录
看来在初始化 Terraform 时需要插件目录。
请准备好 init.tf 文件并进行初始化。
$ echo 'provider "aws" {}' > init.tf $ terraform init
我再试一次。
$ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin
导入似乎成功了。
系统创建了一个名为 generated 的目录,该目录之前并不存在。
目录结构
$ 树。 └── aws ├── alb │ ├── lb.tf │ ├── lb_listener.tf │ ├── lb_target_group.tf │ ├── lb_target_group_attachment.tf │ ├── outputs.tf │ ├── provider.tf │ ├── terraform.tfstate │ └── variables.tf ├── ebs │ ├── ebs_volume.tf │ ├── outputs.tf │ ├── provider.tf │ └── terraform.tfstate ├── ec2_instance │ ├── instance.tf │ ├── outputs.tf │ ├── provider.tf │ ├── terraform.tfstate │ └── variables.tf ├── eip │ ├── eip.tf │ ├── outputs.tf │ ├── provider.tf │ └── terraform.tfstate ├── igw │ ├── internet_gateway.tf │ ├── outputs.tf │ ├── provider.tf │ ├── terraform.tfstate │ └── variables.tf ├── nat │ ├── nat_gateway.tf │ ├── outputs.tf │ ├── provider.tf │ └── terraform.tfstate ├── rds │ ├── db_instance.tf │ ├── db_parameter_group.tf │ ├── db_subnet_group.tf │ ├── outputs.tf │ ├── provider.tf │ ├── terraform.tfstate │ └── variables.tf ├── route_table │ ├── main_route_table_association.tf │ ├── outputs.tf │ ├── provider.tf │ ├── route_table.tf │ ├── route_table_association.tf │ ├── terraform.tfstate │ └── variables.tf ├── sg │ ├── outputs.tf │ ├── provider.tf │ ├── security_group.tf │ ├── security_group_rule.tf │ ├── terraform.tfstate │ └── variables.tf ├── subnet │ ├── outputs.tf │ ├── provider.tf │ ├── subnet.tf │ ├── terraform.tfstate │ └── variables.tf ├── vpc │ ├── outputs.tf │ ├── provider.tf │ ├── terraform.tfstate │ └── vpc.tf └── vpc_peering ├── outputs.tf ├── provider.tf ├── terraform.tfstate └── vpc_peering_connection.tf 13 个目录,63 个文件
请注意目录结构。Terraformer
默认导入目录结构为“{output}/{provider}/{service}/{resource}.tf”。GitHub
上也有相关文档说明。
Terraformer 默认将每个资源拆分成一个文件,并将其放入指定的服务目录中。.
资源文件的默认路径为 {output}/{provider}/{service}/{resource}.tf,每个提供程序的路径可能有所不同。.
这种结构存在以下问题:
- 因为 tfstate 是针对每个 Terraform 资源拆分的,所以即使是微小的更改也需要多次 Apply。
- 所有环境资源都记录在同一个 tfstate 中,因此一个环境中的更改会影响所有环境。
理想情况下,我希望将 tfstate 拆分为开发、生产和管理等环境,以便将
每个环境的所有资源记录在同一个 tfstate 中。
我研究了这是否可行,发现如下:
- 您可以使用 --path-pattern 选项显式指定层次结构。
- 您只能导入带有 --filter 选项中指定的标签的资源。
似乎将这两者结合起来就能实现这个目标,所以我们来试一试。
$ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin --path-pattern {output}/{provider}/develop/ --filter="Name=tags.Environment;Value=dev" $ terraformer import aws --resources=alb,ec2_instance,eip,ebs,igw,nat,rds,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin --path-pattern {output}/{provider}/production/ --filter="Name=tags.Environment;Value=prod" $ terraformer import aws --resources=ec2_instance,eip,ebs,igw,route_table,sg,subnet,vpc,vpc_peering --regions=ap-northeast-1 --profile=beyond-poc-admin --path-pattern {output}/{provider}/manage/ --filter="Name=tags.Environment;Value=mng"
导入完成后,目录结构将如下所示:
目录结构
$ 树。 └── aws ├── develop │ ├── db_instance.tf │ ├── db_parameter_group.tf │ ├── db_subnet_group.tf │ ├── eip.tf │ ├── instance.tf │ ├── internet_gateway.tf │ ├── lb.tf │ ├── lb_target_group.tf │ ├── nat_gateway.tf │ ├── outputs.tf │ ├── provider.tf │ ├── route_table.tf │ ├── security_group.tf │ ├── subnet.tf │ ├── terraform.tfstate │ ├── variables.tf │ └── vpc.tf ├── manage │ ├── instance.tf │ ├── internet_gateway.tf │ ├── outputs.tf │ ├── provider.tf │ ├── route_table.tf │ ├── security_group.tf │ ├── subnet.tf │ ├── terraform.tfstate │ ├── variables.tf │ ├── vpc.tf │ └── vpc_peering_connection.tf └── production ├── db_instance.tf ├── db_parameter_group.tf ├── db_subnet_group.tf ├── eip.tf ├── instance.tf ├── internet_gateway.tf ├── lb.tf ├── lb_target_group.tf ├── nat_gateway.tf ├── outputs.tf ├── provider.tf ├── route_table.tf ├── security_group.tf ├── subnet.tf ├── terraform.tfstate ├── variables.tf └── vpc.tf 4 个目录,45 个文件
资源按环境划分。
如果您查看 develop 目录下的 vpc.tf 文件,会发现其中只导入了对应环境的 VPC。
开发/vpc.tf
resource "aws_vpc" "tfer--vpc-002D-0eea2bc99da0550a6" { assign_generated_ipv6_cidr_block = "false" cidr_block = "10.1.0.0/16" enable_classiclink = "false" enable_classiclink_dns_support = "false" enable_dns_hostnames = "true" enable_dns_support = "true" instance_tenancy = "default" tags = { Environment = "dev" Name = "vpc-terraformer-dev" } }
至于 tfstate,每个环境的所有资源都记录在一个文件中,所以这似乎也不是问题。
文件内容很长,这里我就不赘述了。
担忧
有些地方资源 ID 值是硬编码的。
有几个地方资源 ID 值是硬编码的,例如下面的 aws_security_group 的 vpc_id
resource "aws_security_group" "tfer--alb-002D-dev-002D-sg_sg-002D-00d3679a2f3309565" { description = "for ALB" egress { cidr_blocks = ["0.0.0.0/0"] description = "Outbound ALL" from_port = "0" protocol = "-1" self = "false" to_port = "0" } ingress { cidr_blocks = ["0.0.0.0/0"] description = "allow_http_for_alb" from_port = "80" protocol = "tcp" self = "false" to_port = "80" } name = "alb-dev-sg" tags = { Environment = "dev" Name = "alb-dev-sg" } vpc_id = "vpc-0eea2bc99da0550a6" }
编写新的 HCL 时,您可以像这样动态引用它:“vpc_id = aws_vpc.vpc.id”,但
导入时似乎仍然难以实现。
这部分信息已记录在 tfstate 中,因此您只需修改 tf 文件即可。
terraform_remote_state 的描述与 0.12 版本不符。
有些地方的 HCL 描述来自 Terraform 0.11,例如下面的 aws_subnet 的 vpc_id。
resource "aws_subnet" "tfer--subnet-002D-02f90c599d4c887d3" { assign_ipv6_address_on_creation = "false" cidr_block = "10.1.2.0/24" map_public_ip_on_launch = "true" tags = { Environment = "dev" Name = "subnet-terraformer-dev-public-1c" } vpc_id = "${data.terraform_remote_state.local.outputs.aws_vpc_tfer--vpc-002D-0eea2bc99da0550a6_id}" }
如果在 0.12 系列上以这种状态应用,它会运行,但会生成警告。
此外,如果您将 `--path-pattern` 选项更改为为每个环境使用单独的目录,则
tfstate 本身将输出到单个文件中,但
引用 tf 文件中的资源时,它们仍然会使用 `terraform_remote_state`。
查看 GitHub,有如下说明,因此这是一个规范。
使用 terraform_remote_state(本地和存储桶)连接资源。.
对于上述 vpc_id,aws_vpc 和 aws_subnet 记录在同一个 tfstate 中,因此
您可以直接使用“vpc_id = aws_vpc.tfer--vpc-002D-0eea2bc99da0550a6.id”来引用它。
看来您还需要自行修复这部分。
概括
你觉得怎么样?
看来用 Terraform 导入现有基础设施会更容易。
虽然还有一些问题,但解决起来并不难,所以我觉得都在可接受的范围内。
我一直觉得 Terraform 导入很麻烦,所以
我非常佩服 Waze SRE 团队开发出这么一款工具,能把这个问题解决得这么好。我强烈建议
大家试用一下。
1