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

我叫寺冈,是一名基础设施工程师。

在上一篇文章中,我介绍了 terraform import 命令,它是一种将现有资源导入 Terraform 的方法

如何在 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 账户中有三个环境。

  1. 发展
  2. 生产
  3. 管理

此外,每个环境都具有以下资源:

  1. 专有网络
  2. 子网
  3. 路线表
  4. 互联网网关
  5. NAT网关
  6. 安全组
  7. VPC 对等互连
  8. EIP
  9. EC2
  10. 白蛋白
  11. 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,每个提供程序的路径可能有所不同。.

这种结构存在以下问题:

  1. 因为 tfstate 是针对每个 Terraform 资源拆分的,所以即使是微小的更改也需要多次 Apply。
  2. 所有环境资源都记录在同一个 tfstate 中,因此一个环境中的更改会影响所有环境。

理想情况下,我希望将 tfstate 拆分为开发、生产和管理等环境,以便将
每个环境的所有资源记录在同一个 tfstate 中。
我研究了这是否可行,发现如下:

  1. 您可以使用 --path-pattern 选项显式指定层次结构。
  2. 您只能导入带有 --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
加载中...
1 票,平均:1.00 / 11
19,187
X Facebook 哈特纳书签 口袋

写这篇文章的人

关于作者

寺冈由纪

于 2016 年加入 Beyond,目前是他担任基础设施工程师
MSP 的第六个年头,他负责排除故障,同时
使用 AWS 等公共云设计和构建基础设施。
最近,我
一直在使用 Terraform 和 Packer 等 Hashicorp 工具作为构建 Docker 和 Kubernetes 等容器基础设施以及自动化操作的一部分,并且我
还扮演了在外部学习小组和研讨会上发言的传播者的角色。

・GitHub
https://github.com/nezumisannn

・演示历史
https://github.com/nezumisannn/my-profile

・演示材料(SpeakerDeck)
https://speakerdeck.com/nezumisannn

・认证:
AWS认证解决方案架构师-
谷歌云专业云架构师