hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.74k stars 9.1k forks source link

[Enhancement]: Allow sts:assumeRole of actually managed IAM Role in its trust policy #29417

Open afischer-opentext-com opened 1 year ago

afischer-opentext-com commented 1 year ago

Description

This ticket is in regards to the AWS announcement https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/

The issue basically is that the newly proposed workflow to enable a self-assume for any IAM Role currently cannot be implemented via Terraform.

The announcement states that a self assume must be explicitely defined in the trust policy of the IAM Role as seen here:

resource "aws_iam_role" "my_role" {
  assume_role_policy = data.aws_iam_policy_document.my_role_trust_policy.json
  managed_policy_arns = []
  name                = "MyIamRole"
}

data "aws_iam_policy_document" "my_role_trust_policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      identifiers = [
        "codebuild.amazonaws.com"
      ]
      type = "Service"
    }
    principals {
      identifiers = [
        "arn:aws:iam::${var.account_number}:role/MyIamRole"
      ]
      type = "AWS"
    }
  }
}

This leads to the situation that the AWS Api then complains about:

Error: creating IAM Role (MyIamRole): MalformedPolicyDocument: Invalid principal in policy: "AWS":"arn:aws:iam::1234567890:role/MyIamRole"

Because obviously the IAM api cannot evaluate the Role as it is not present while being created.

The request is to support this workflow.

A proposal would be to have another flag/parameter at aws_iam_role resource level, let's say

resource "aws_iam_role" "my_role" {
  assume_role_policy = data.aws_iam_policy_document.my_role_trust_policy.json
  assume_self = true
  managed_policy_arns = []
  name                = "MyIamRole"
}

data "aws_iam_policy_document" "my_role_trust_policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      identifiers = [
        "codebuild.amazonaws.com"
      ]
      type = "Service"
    }
  }
}

and the resource then manages to add the missing principal after initially creating the trust policy.

Affected Resource(s) and/or Data Source(s)

Potential Terraform Configuration

No response

References

https://aws.amazon.com/blogs/security/announcing-an-update-to-iam-role-trust-policy-behavior/

Would you like to implement a fix?

No

github-actions[bot] commented 1 year ago

Community Note

Voting for Prioritization

Volunteering to Work on This Issue

matthewlynden commented 1 month ago

I must say it's a bit ridiculous that there has been no further word on this issue since early 2023. My problems with that aside, I've obviously run into this issue today and here is my workaround, however inelegant it may be it has worked for me. Your mileage my vary as you might need to specify depends_on for other resources that reference a role you're updating for self trust (like I did). Hopefully it gets some of you out of a very frustrating situation until such times as an officially supported method.

[EDIT]: I've added tags to the IAM role declaration as a means to trigger the null_resource consistently on subsequent runs. The previous iteration of this workaround would be reverted when re-run as the assume_role_policy would overwrite the intended changes made by the null_resource.

The initial role declaration.

resource "aws_iam_role" "service" {
  name = var.service_id
  path = "/${replace(var.suite_id, ".", "-")}/"

  assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json

  tags = {
    Modified = timestamp() # ! Ensures a consistent trigger on each run for update_assume_role_policy null_resource
  }
}

A dummy policy document to satisfy the required assume_role_policy parameter in the role declaration.

# ! The assume role policy will be overridden as a result of the null_resource below
data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    effect = "Allow"

    actions = [
      "sts:AssumeRole"
    ]

    principals {
      type       = "Service"
      identifiers = ["codebuild.amazonaws.com"]
    }
  }
}

A null_resource with a local-exec provisioner to run the aws iam update-assume-role-policy with the desired 'AWS' self-trusting principal. Note: I tried with and without specifying the sleep command and with values of 3, 5, and 10 with the latter (10) being the only effective choice that alleviated any race condition where the IAM role wasn't ready yet.

# ! This is a workaround, see: https://github.com/hashicorp/terraform-provider-aws/issues/29417
resource "null_resource" "update_assume_role_policy" {
  depends_on = [aws_iam_role.service]

  triggers = {
    modified_date = aws_iam_role.service.tags["Modified"]
  }

  provisioner "local-exec" {
    command = <<EOF
sleep 10 && \
aws iam update-assume-role-policy \
  --role-name ${aws_iam_role.service.name} \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "${aws_iam_role.service.arn}",
          "Service": "codebuild.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'
    EOF
  }
}

As an example, I had some modules that passed the value of aws_iam_role.server.arn into the module as a variable. These needed depends_on added to them. Note: Module support for depends_on was added in Terraform version 0.13, and prior versions can only use it with resources.

module "terraform" {
  depends_on = [null_resource.update_assume_role_policy]

  source     = "./managed_image"
  suite_id   = var.suite_id
  service_id = var.service_id
  name       = "terraform"
  role       = aws_iam_role.service.arn
}