使用自己的能力编写tfstate,并将不支持Terraforming的现有资源纳入Terraform管理
首先
我使用Terraform来管理AWS云资源的代码。
虽然Terraform可用于创建新资源,但使用Terraform时,人们往往也想管理现有资源。当前,Terraform本身没有这个功能,但有一个补充工具Terraforming。
关于Terraforming的使用方法,我之前写过如下的文章。
将现有的IAM用户纳入Terraform的管理范围中
然而,尽管Terraforming并非完美,也并非适用于Terraform所支持的所有资源类型。
我正在使用最新版本的 Terraforming v0.8.0 编写,我想要使用 aws_iam_policy_attachment,但它不支持(´・ω・`)
最近我开始习惯于修改tfstate,所以我决定自己写tfstate并尝试操作,结果成功了,现在分享一些心得。
(2016/06/12更新) aws_iam_policy_attachment已在Terraforming v0.9.0中得到支持。感谢dtan4先生。
在这篇文章中要做的事
将现有的aws_iam_policy_attachment资源通过Terraform的管理方式加入
把要操作的资源用tf文件来描述的话,会像这样。
resource "aws_iam_policy_attachment" "aws_codedeploy_role_attachment" {
name = "AWSCodedeployRoleAttachment"
roles = [
"appProductionCodeDeployRole",
"appStagingCodeDeployRole"
]
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole"
}
流动
-
- Terraformのドキュメントを眺めつつダミーのtfファイルを作る
-
- ダミーのリソースを terraform apply してtfstateの雛形作成
-
- AWSコンソールを眺めつつターゲットとなるリソースのtfファイルを作る
-
- tfstateの雛形をコピーしてターゲットとなるリソースのtfstateの枠を作る
terraform refresh して terraformでtfstateを既存リソースの状態に合わせる
ダミーリソースを削除して terraform plan で差分がなくなれば完成
请注意,对于手动编辑tfstate文件,每个人都要自行承担责任。另外,在我写下这句话时,terraform的最新版本是v0.6.16。
制作一个虚拟的TF文件。
首先,创建目标资源类型的tf文件。
Terraform的官方文档在这里
https://www.terraform.io/docs/providers/aws/r/iam_policy_attachment.html
暫時先製作一個類似的虛擬資源。這次我們順便為測試創建了IAM角色。
(註:最終目標是將所需的AWS托管策略附加到目標資源上,但由於aws_iam_policy_attachments的特性,如果在管理策略上進行測試,將會對現有設定進行更改,因此也創建了測試角色)
resource "aws_iam_role" "test_role" {
name = "test_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_policy" "test_policy" {
name = "test_policy"
path = "/"
description = "My test policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ec2:Describe*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_policy_attachment" "test_attachment" {
name = "test_attachment"
roles = ["${aws_iam_role.test_role.name}"]
policy_arn = "${aws_iam_policy.test_policy.arn}"
}
创建tfstate的模板
执行 terraform plan,然后执行 apply,创建 tfstate 的模板。
$ terraform plan
+ aws_iam_policy.test_policy
arn: "" => "<computed>"
description: "" => "My test policy"
name: "" => "test_policy"
path: "" => "/"
policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": [\n \"ec2:Describe*\"\n ],\n \"Effect\": \"Allow\",\n \"Resource\": \"*\"\n }\n ]\n}\n"
+ aws_iam_policy_attachment.test_attachment
name: "" => "test_attachment"
policy_arn: "" => "${aws_iam_policy.test_policy.arn}"
roles.#: "" => "1"
roles.1376821413: "" => "test_role"
+ aws_iam_role.test_role
arn: "" => "<computed>"
assume_role_policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": \"sts:AssumeRole\",\n \"Principal\": {\n \"Service\": \"ec2.amazonaws.com\"\n },\n \"Effect\": \"Allow\",\n \"Sid\": \"\"\n }\n ]\n}\n"
name: "" => "test_role"
path: "" => "/"
unique_id: "" => "<computed>"
Plan: 3 to add, 0 to change, 0 to destroy.
$ terraform apply
省略部分はありますが、以下のような形でtfstateを作成しました。
ダミーリソースで生成されたtfstateを詳しく観察します。
{
"version": 1,
"serial": 54,
"remote": {
(略)
},
"modules": [
{
"path": [
"root"
],
"outputs": {},
"resources": {
(略)
"aws_iam_policy_attachment.test_attachment": {
"type": "aws_iam_policy_attachment",
"depends_on": [
"aws_iam_policy.test_policy",
"aws_iam_role.test_role"
],
"primary": {
"id": "test_attachment",
"attributes": {
"groups.#": "0",
"id": "test_attachment",
"name": "test_attachment",
"policy_arn": "arn:aws:iam::XXXXXXXXXXX:policy/test_policy",
"roles.#": "1",
"roles.1376821413": "test_role",
"users.#": "0"
}
}
},
(略)
}
}
]
}
创建目标资源的tf文件
在浏览AWS控制台的同时,我会创建目标资源的tf文件。
resource "aws_iam_policy_attachment" "aws_codedeploy_role_attachment" {
name = "AWSCodedeployRoleAttachment"
roles = [
"appProductionCodeDeployRole",
"appStagingCodeDeployRole"
]
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole"
}
创建目标资源的tfstate框架
刚刚创建虚拟资源时生成的 tfstate,请将其复制并创建一个类似的 tfstate。
这时候要重点考虑以下两个要点
-
- tfstateを手動で編集する場合はserialの値をインクリメントする
- 配列を受け取る場所、この例だとattributes.roles.# のところにattachするロールの数だけを入れ、roles.1376821413 のようにIDが採番されてる子リソースの行を削除する。
当ID部分似乎已经被编号时,稍后可以使用terraform refresh命令来生成terraform。
{
"version": 1,
"serial": 55,
"remote": {
(略)
},
"modules": [
{
"path": [
"root"
],
"outputs": {},
"resources": {
(略)
"aws_iam_policy_attachment.aws_codedeploy_role_attachment": {
"type": "aws_iam_policy_attachment",
"primary": {
"id": "aws_codedeploy_role_attachment",
"attributes": {
"groups.#": "0",
"id": "aws_codedeploy_role_attachment",
"name": "AWSCodedeployRoleAttachment",
"policy_arn": "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole",
"roles.#": "2",
"users.#": "0"
}
}
},
"aws_iam_policy_attachment.test_attachment": {
"type": "aws_iam_policy_attachment",
"depends_on": [
"aws_iam_policy.test_policy",
"aws_iam_role.test_role"
],
"primary": {
"id": "test_attachment",
"attributes": {
"groups.#": "0",
"id": "test_attachment",
"name": "test_attachment",
"policy_arn": "arn:aws:iam::XXXXXXXXXXX:policy/test_policy",
"roles.#": "1",
"roles.1376821413": "test_role",
"users.#": "0"
}
}
},
(略)
}
}
]
}
使用terraform refresh将现有资源与其状态相匹配。
执行terraform refresh命令将更新tfstate的状态,将资源的实体视为正确。通过这样做,将正确地获取之前未分配ID的部分的状态。
$ terraform refresh
{
"version": 1,
"serial": 56,
"remote": {
(略)
},
"modules": [
{
"path": [
"root"
],
"outputs": {},
"resources": {
(略)
"aws_iam_policy_attachment.aws_codedeploy_role_attachment": {
"type": "aws_iam_policy_attachment",
"primary": {
"id": "aws_codedeploy_role_attachment",
"attributes": {
"groups.#": "0",
"id": "aws_codedeploy_role_attachment",
"name": "AWSCodedeployRoleAttachment",
"policy_arn": "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole",
"roles.#": "2",
"roles.2660578725": "appStagingCodeDeployRole",
"roles.3320175550": "appProductionCodeDeployRole",
"users.#": "0"
}
}
},
"aws_iam_policy_attachment.test_attachment": {
"type": "aws_iam_policy_attachment",
"depends_on": [
"aws_iam_policy.test_policy",
"aws_iam_role.test_role"
],
"primary": {
"id": "test_attachment",
"attributes": {
"groups.#": "0",
"id": "test_attachment",
"name": "test_attachment",
"policy_arn": "arn:aws:iam::XXXXXXXXXXX:policy/test_policy",
"roles.#": "1",
"roles.1376821413": "test_role",
"users.#": "0"
}
}
},
(略)
}
}
]
}
在这个阶段,进行一次terraform plan,并确认差异已经消失。
$ terraform plan
有时候以为是默认值的东西,实际上需要明确定义,所以如果有差异的情况下,可以参考官方文档中资源的定义以及AWS控制台上的实际资源,适当进行调整。
删除不再需要的虚拟资源。
从tf文件中删除不再需要的虚拟资源并计划以及应用。
$ terraform plan
- aws_iam_policy.test_policy
- aws_iam_policy_attachment.test_attachment
- aws_iam_role.test_role
Plan: 0 to add, 0 to change, 3 to destroy.
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 3 destroyed.
在这个阶段,没有虚拟资源,只剩下目标资源,所以应该通过执行terraform plan,得到没有差异的状态,这样就完成了。
$ terraform paln
总结
-
- tfstateなんてただのJSON
- 既存リソースもTerraformで管理するんだ、という熱いハートと気合が大事
这样一来,即使现有资源没有进行 Terraforming 处理,也可以任意管理 Terraform 了呢~