从 JSON 帐号数组构建 AWS IAM 策略

Posted

技术标签:

【中文标题】从 JSON 帐号数组构建 AWS IAM 策略【英文标题】:Build AWS IAM policy from JSON array of account numbers 【发布时间】:2020-02-15 08:24:58 【问题描述】:

我有一个 AWS 帐号列表,我想动态地使用它来构建 AWS 策略。这是我的例子:

resource "aws_s3_bucket" "splunk-config-bucket" 
  bucket        = "$var.config_bucket_name"
  force_destroy = true
  server_side_encryption_configuration 
    rule 
      apply_server_side_encryption_by_default 
        kms_master_key_id = "$data.aws_kms_alias.kms_name.arn"
        sse_algorithm     = "aws:kms"
      
    
  
  tags = 
    Product = "Splunk - AWS Config Logs"
    Service = "Security"
  

 lifecycle_rule 
    id      = "log"
    enabled = true

    prefix = "*"

    transition 
      days          = 7
      storage_class = "GLACIER"
    

    expiration 
      days = 14
    
  

  policy = <<POLICY

    "Version": "2012-10-17",
    "Statement": [
        
            "Sid": "AWSConfigAclCheck20150319",
            "Effect": "Allow",
            "Principal": 
                "Service": "config.amazonaws.com"
            ,
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::$var.config_bucket_name"
        ,
        
            "Sid": "AWSConfigWrite20150319",
            "Effect": "Allow",
            "Principal": 
                "Service": "config.amazonaws.com"
            ,
            "Action": "s3:PutObject",
            "Resource": [
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*”,
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*",
                "arn:aws:s3:::$var.config_bucket_name/AWSLogs/123456789/*"
            ],
            "Condition": 
                "StringEquals": 
                    "s3:x-amz-acl": "bucket-owner-full-control"
                
            
        
    ]

POLICY


resource "aws_s3_bucket_notification" "configlogs_bucketnotification" 
  bucket = "$var.config_bucket_name"

  queue 
    queue_arn     = "$aws_sqs_queue.splunk_configlogs_sqs_queue.arn"
    events        = ["s3:ObjectCreated:*"]
  

我在 envs.json 文件中有一个帐号列表,如下所示:


    "accounts": [
        "1234567890",
        "0987654321",
        "1029384756",
        "6574839201",
        "0192837465"
    ]

我正在尝试使用 AWS 帐号的动态列表生成策略。我们正试图摆脱对帐号的硬编码,并将其从我们拥有的“集中配置”项目中提取出来。

这是我尝试过的:

变量.tf:

locals 
    accounts = jsondecode(file("../configuration/envs.json")).accounts

Config.tf

            "Principal": 
                "Service": "config.amazonaws.com"
            ,
            "Action": "s3:PutObject",
            "Resource": $jsonencode(formatlist("arn:aws:s3:::$var.config_bucket_name/AWSLogs/%s/*", local.accounts))
            "Condition": 

当我运行“terraform plan”时,这给了我以下输出:

Error: "policy" contains an invalid JSON: invalid character '"' after object key:value pair

  on central_config.tf line 2, in resource "aws_s3_bucket" "splunk-config-bucket":
   2: resource "aws_s3_bucket" "splunk-config-bucket" 

我也试过这个:

变量.tf

locals 
  accounts = jsondecode(file("../configuration/envs.json")).accounts


output "example" 
  value = jsonencode(formatlist("arn:aws:s3:::$var.config_bucket_name/AWS/AWSLogs/%s/*", local.accounts))

config.tf

            "Principal": 
                "Service": "config.amazonaws.com"
            ,
            "Action": "s3:PutObject",
            "Resource": $local.accounts.output.value
            "Condition": 
                "StringEquals": 
                    "s3:x-amz-acl": "bucket-owner-full-control"
                

这是我得到的输出:

Error: Unsupported attribute

  on central_config.tf line 60, in resource "aws_s3_bucket" "splunk-config-bucket":
  60:             "Resource": $local.accounts.output.value
    |----------------
    | local.accounts is tuple with 35 elements

This value does not have any attributes.

谢谢,

【问题讨论】:

您是否正在寻找类似于此处描述的内容?我不使用 AWS,所以我需要更多的东西来继续。 learn.hashicorp.com/terraform/aws/iam-policy 嘿- 感谢您的参与,我添加了更多详细信息。谢谢你:) 【参考方案1】:

您可以使用file function 加载文件,然后通过使用jsondecode 解码JSON 并选择accounts 键来提取您需要的帐户列表。

举个例子:

locals 
  accounts = jsondecode(file("accounts.json")).accounts


output example 
  value = local.accounts

这将返回以下内容:

example = [
  "1234567890",
  "0987654321",
  "1029384756",
  "6574839201",
  "0192837465",
]

如果您想将其放入您的策略中,您会希望使用 formatlist function 将帐户列表传递为单个字符串格式:

output "example" 
  value = formatlist("arn:aws:s3:::bucket_name/AWSLogs/%s/*", local.accounts)

这个输出:

example = [
  "arn:aws:s3:::bucket_name/AWSLogs/1234567890/*",
  "arn:aws:s3:::bucket_name/AWSLogs/0987654321/*",
  "arn:aws:s3:::bucket_name/AWSLogs/1029384756/*",
  "arn:aws:s3:::bucket_name/AWSLogs/6574839201/*",
  "arn:aws:s3:::bucket_name/AWSLogs/0192837465/*",
]

如果您仔细注意到,Terraform 在列表中使用尾随逗号,这是无效的 JSON,因此会为您的 IAM 策略创建无效的 JSON 结构。为了解决这个问题,我们可以使用 jsonencode 将其重新编码为 JSON:

output "example" 
  value = jsonencode(formatlist("arn:aws:s3:::bucket_name/AWSLogs/%s/*", local.accounts))

然后输出:

example = ["arn:aws:s3:::bucket_name/AWSLogs/1234567890/*","arn:aws:s3:::bucket_name/AWSLogs/0987654321/*","arn:aws:s3:::bucket_name/AWSLogs/1029384756/*","arn:aws:s3:::bucket_name/AWSLogs/6574839201/*","arn:aws:s3:::bucket_name/AWSLogs/0192837465/*"]

没有尾随逗号。

总而言之,您可以像这样创建您的 IAM 策略:

locals 
  accounts = jsondecode(file("accounts.json")).accounts


resource aws_iam_policy policy 
  name        = "example"
  path        = "/"

  policy = <<EOF

  "Version": "2012-10-17",
  "Statement": [
    
      "Action": [
        "s3:PutObject"
      ],
      "Effect": "Allow",
      "Resource": $jsonencode(formatlist("arn:aws:s3:::bucket_name/AWSLogs/%s/*", local.accounts))
    
  ]

EOF

【讨论】:

这太棒了。感谢您花时间回复此问题。我似乎遇到的唯一问题是在运行时尝试将输出作为输入返回到我的另一个“file.tf”策略中。我可能错过了重点。这是可能的还是我应该从不同的角度解决它? 我遇到了这个问题:错误:资源“aws_s3_bucket”“splunk-config-bucket”中的 central_config.tf 第 61 行不支持的属性:61:“$local.accounts.formatted_list " |---------------- | local.accounts 是包含 35 个元素的元组 此值没有任何属性。 对不起,输出更多用于演示。我添加了一个创建 IAM 策略的更完整的示例。 更完整的示例在这里有帮助吗?如果没有看到您的问题的minimal reproducible example,很难说出您遇到了什么问题。完整工作的示例肯定会满足您的需要,但您可能会在这里遇到另一个问题,因此可能值得编辑您的问题以包含相关内容,或者如果您遇到与此无关的另一件事,甚至打开另一个问题具体问题。 明白了——我是对的。感觉离我很近!我现在得到以下信息:“错误:“策略”包含无效的 JSON:对象键:值对之后的无效字符“””【参考方案2】:

在 ydaetskcoR 的帮助下,我设法完成了这项工作:

这是我的变量文件:

locals 
  accounts = jsondecode(file("../configuration/envs.json")).accounts
  config = jsonencode(formatlist("arn:aws:s3:::$var.config_bucket_name/AWSLogs/%s/*", local.accounts))

我可以使用 Terraform 控制台测试输出:

> jsonencode(formatlist("arn:aws:s3:::$var.config_bucket_name/AWSLogs/%s/*", local.accounts))
["arn:aws:s3:::bby-central-configlogs-splunk/AWSLogs/123456789/*","arn:aws:s3:::bby-central-configlogs-splunk/AWSLogs/123456789/*"]

如您所见,它们周围有 []: 我将以下内容纳入我的政策:

            "Principal": 
                "Service": "config.amazonaws.com"
            ,
            "Action": "s3:PutObject",
            "Resource": $local.config,
            "Condition": 
                "StringEquals": 
                    "s3:x-amz-acl": "bucket-owner-full-control"
                

我想我会回答:)

【讨论】:

【参考方案3】:

使用字符串模板正确地进行 JSON 编码可能会令人沮丧。除非有充分的理由以特定方式格式化 JSON,否则我们可以通过直接构建所需的数据结构然后将其传递给 jsonencode 来避免模板化 JSON,如下所示:

  policy = jsonencode(
    Version = "2012-10-17"
    Statement = [
      
        Sid    = "AWSConfigAclCheck20150319"
        Effect = "Allow"
        Principal = 
          Service = "config.amazonaws.com"
        
        Action   = "s3:GetBucketAcl"
        Resource = "arn:aws:s3:::$var.config_bucket_name"
      ,
      # etc, etc
    ]
  )

然后,您可以直接使用 Terraform 的运算符和函数来构造该对象的各个部分。例如,要生成您想要的 ARN 列表:

  policy = jsonencode(
    Version = "2012-10-17"
    Statement = [
      # ...
      
        Sid    = "AWSConfigWrite20150319"
        Effect = "Allow"
        Principal = 
          Service = "config.amazonaws.com"
        
        Action   = "s3:PutObject"
        Resource = [for acct in local.accounts : "arn:aws:s3:::$var.config_bucket_name/AWSLogs/$acct/*"]
      ,
      # ...
    ]
  )

如果策略变得足够复杂以至于您想将其分解到单独的模板文件中,您仍然可以通过将整个模板设置为 jsonencode 调用来使用 jsonencode 函数:

$jsonencode(
  # ...
)

同时对整个结构使用jsonencode 意味着结果可以保证是有效的 JSON,而无需像将 JSON 构建为模板时那样担心逗号和其他分隔符的确切位置。

【讨论】:

【参考方案4】:

您可以在代码中重新加载动态配置,以更新帐户列表并在配置文件更改时调用 S3 API。

为了查看配置文件(帐户列表),您可以使用一些库。我相信“毒蛇”是一个不错的选择。

此外,如果您担心由于 API 调用失败或应用程序崩溃而导致文件中的此类事件丢失,您可以尝试使更新操作列表具有幂等性,并在应用程序启动时或间隔更新。

这些链接可能会派上用场:

关于幂等性:https://nordicapis.com/understanding-idempotency-and-safety-in-api-design/

毒蛇:https://github.com/spf13/viper

【讨论】:

谢谢你 - 我会调查并尝试让它工作:)

以上是关于从 JSON 帐号数组构建 AWS IAM 策略的主要内容,如果未能解决你的问题,请参考以下文章

Terraform 依赖于 aws_iam_policy

从 AWS 组织导出所有 IAM 用户、角色和策略

json AWS for Packer要求的最小IAM策略.https://github.com/mitchellh/packer权限由API函数分解

带有委托人的动态 AWS IAM 策略文档

Cloudformation 的最低权限 AWS IAM 策略

Terraform aws_iam_role_policy 中的 JSON 语法错误