我如何将 Terraform 用于 maintain/manage IAM 用户
How do I use Terraform to maintain/manage IAM users
Terraform 版本
Terraform v0.7.8
Terraform v0.7.11
受影响的资源
- aws_iam_user
Terraform 配置文件
我正在尝试使用列表来管理 IAM 用户:
variable "iam_user_list" { default = "aaa,bbb,ccc,ddd,eee,fff" }
resource "aws_iam_user" "iam_user" {
count = "${length(split(",", var.iam_user_list))}"
name = "${element(split(",", var.iam_user_list), count.index)}"
force_destroy = true
}
output "user_list" {
value = "VPC IAM Base Users: ${var.iam_user_list}"
}
当AWS账户为空时,用户按预期创建
当我从列表末尾删除用户时,即 fff,将按预期删除。
但是当我删除列表中间的用户,即bbb时,出现错误:
Modifying...
name: "bbb" => "ccc"
Error applying plan:
1 error(s) occurred:
* aws_iam_user.iam_user.1: Error updating IAM User bbb: EntityAlreadyExists: User with name ccc already exists.
status code: 409, request id: ed0b4447-abf3-11e6-9b38-0fb23af37c82
terraform 中似乎没有用户存在性检查 aws_iam_user,在 terraform 中管理 IAM 用户的解决方法/正确方法是什么?
感谢 Martin Atkins 在 hashicorp-terraform
Gitter room 中的 answer/suggestion:
The issue discussed here is that when you use "count" with an array variable Terraform doesn't really "see" the relationships between the items in the array and the resources, so when a value is deleted from the middle of the list everything after that point is suddenly "off by one" and Terraform will want to replace them all.
This is the sort of use-case that would benefit from a first-class iteration feature in Terraform, but sadly we don't have that yet
I would suggest that instead of trying to pass the user list in as variables, the most robust approach for now is to have a separate program that reads the user list from somewhere and writes out a .tf.json file containing a separate aws_iam_user block for each user. That way Terraform will understand which block belongs to which user because the local identifier can be the username or some sort of user id, allowing the correlation to be maintained.
Terraform 0.12.6 在资源上添加了 for_each,事实证明这对解决所描述的问题非常有用。
假设我们写了一个 terraform.tfvars,其中包含一些 IAM 用户名和组成员身份,如下所示:
user_names = {
"test-user-1" = {
path = "/"
force_destroy = true
tag_email = "nobody@example.com"
}
"test-user-2" = {
path = "/"
force_destroy = true
tag_email = "nobody@example.com"
}
}
group_memberships = {
"test-user-1" = [ "SomeGroup", "AnotherGroup" ]
"test-user-2" = [ "AndYetAnotherGroup" ]
}
然后我们可以使用 for_each 元参数来提取键和值,如下所示:
resource "aws_iam_user" "user" {
for_each = "${var.user_names}"
name = each.key
path = each.value["path"]
force_destroy = each.value["force_destroy"]
tags = "${map("EmailAddress", each.value["tag_email"])}"
}
resource "aws_iam_user_group_membership" "group_membership" {
for_each = "${var.group_memberships}"
user = each.key
groups = each.value
depends_on = [ "aws_iam_user.user" ]
}
请注意,user_names 和 group_memberships 的地图变量中的每个项目都必须属于同一类型。我们不能混合映射和列表。这就是我将 group_memberships 与 user_names 分开的原因。如果不是这种情况,我们可以为每个用户定义另一个属性来指定组成员身份,但 Terraform 目前不支持。
如果您要以此为基础制作一个模块,并希望输出有关每个用户的一些有用属性,您可以按如下方式完成此操作:
output "user_ids" {
value = {
for user_name in aws_iam_user.user:
user_name.name => user_name.unique_id
}
}
output "user_arns" {
value = {
for user_name in aws_iam_user.user:
user_name.name => user_name.arn
}
}
生成的输出将是 user_name 的地图。
user_arns = {
"test-user-1" = "arn:aws:iam::123456789012:user/test-user-1"
"test-user-2" = "arn:aws:iam::123456789012:user/test-user-2"
}
user_ids = {
"test-user-1" = "AB2DEDN2O2NMLMNT4KI7G"
"test-user-2" = "AB1DEFG2O2NNLJQ9YKH7J"
}
有了这个,我们可以添加、更新或删除用户 and/or 组成员身份,而不会影响 tfstate 中已知的 terraform 资源与代码中的配置之间的映射。这是因为每个资源的名称现在包含用户名作为资源名称本身的一部分,并允许我们引用特定的资源,而当使用 "count =" 时我们有一个 count.index,但没办法轻松地将特定用户映射到特定资源。
Terraform 版本
Terraform v0.7.8 Terraform v0.7.11
受影响的资源
- aws_iam_user
Terraform 配置文件
我正在尝试使用列表来管理 IAM 用户:
variable "iam_user_list" { default = "aaa,bbb,ccc,ddd,eee,fff" }
resource "aws_iam_user" "iam_user" {
count = "${length(split(",", var.iam_user_list))}"
name = "${element(split(",", var.iam_user_list), count.index)}"
force_destroy = true
}
output "user_list" {
value = "VPC IAM Base Users: ${var.iam_user_list}"
}
当AWS账户为空时,用户按预期创建
当我从列表末尾删除用户时,即 fff,将按预期删除。
但是当我删除列表中间的用户,即bbb时,出现错误:
Modifying...
name: "bbb" => "ccc"
Error applying plan:
1 error(s) occurred:
* aws_iam_user.iam_user.1: Error updating IAM User bbb: EntityAlreadyExists: User with name ccc already exists.
status code: 409, request id: ed0b4447-abf3-11e6-9b38-0fb23af37c82
terraform 中似乎没有用户存在性检查 aws_iam_user,在 terraform 中管理 IAM 用户的解决方法/正确方法是什么?
感谢 Martin Atkins 在 hashicorp-terraform
Gitter room 中的 answer/suggestion:
The issue discussed here is that when you use "count" with an array variable Terraform doesn't really "see" the relationships between the items in the array and the resources, so when a value is deleted from the middle of the list everything after that point is suddenly "off by one" and Terraform will want to replace them all.
This is the sort of use-case that would benefit from a first-class iteration feature in Terraform, but sadly we don't have that yet I would suggest that instead of trying to pass the user list in as variables, the most robust approach for now is to have a separate program that reads the user list from somewhere and writes out a .tf.json file containing a separate aws_iam_user block for each user. That way Terraform will understand which block belongs to which user because the local identifier can be the username or some sort of user id, allowing the correlation to be maintained.
Terraform 0.12.6 在资源上添加了 for_each,事实证明这对解决所描述的问题非常有用。
假设我们写了一个 terraform.tfvars,其中包含一些 IAM 用户名和组成员身份,如下所示:
user_names = {
"test-user-1" = {
path = "/"
force_destroy = true
tag_email = "nobody@example.com"
}
"test-user-2" = {
path = "/"
force_destroy = true
tag_email = "nobody@example.com"
}
}
group_memberships = {
"test-user-1" = [ "SomeGroup", "AnotherGroup" ]
"test-user-2" = [ "AndYetAnotherGroup" ]
}
然后我们可以使用 for_each 元参数来提取键和值,如下所示:
resource "aws_iam_user" "user" {
for_each = "${var.user_names}"
name = each.key
path = each.value["path"]
force_destroy = each.value["force_destroy"]
tags = "${map("EmailAddress", each.value["tag_email"])}"
}
resource "aws_iam_user_group_membership" "group_membership" {
for_each = "${var.group_memberships}"
user = each.key
groups = each.value
depends_on = [ "aws_iam_user.user" ]
}
请注意,user_names 和 group_memberships 的地图变量中的每个项目都必须属于同一类型。我们不能混合映射和列表。这就是我将 group_memberships 与 user_names 分开的原因。如果不是这种情况,我们可以为每个用户定义另一个属性来指定组成员身份,但 Terraform 目前不支持。
如果您要以此为基础制作一个模块,并希望输出有关每个用户的一些有用属性,您可以按如下方式完成此操作:
output "user_ids" {
value = {
for user_name in aws_iam_user.user:
user_name.name => user_name.unique_id
}
}
output "user_arns" {
value = {
for user_name in aws_iam_user.user:
user_name.name => user_name.arn
}
}
生成的输出将是 user_name 的地图。
user_arns = {
"test-user-1" = "arn:aws:iam::123456789012:user/test-user-1"
"test-user-2" = "arn:aws:iam::123456789012:user/test-user-2"
}
user_ids = {
"test-user-1" = "AB2DEDN2O2NMLMNT4KI7G"
"test-user-2" = "AB1DEFG2O2NNLJQ9YKH7J"
}
有了这个,我们可以添加、更新或删除用户 and/or 组成员身份,而不会影响 tfstate 中已知的 terraform 资源与代码中的配置之间的映射。这是因为每个资源的名称现在包含用户名作为资源名称本身的一部分,并允许我们引用特定的资源,而当使用 "count =" 时我们有一个 count.index,但没办法轻松地将特定用户映射到特定资源。