Search code examples
amazon-ec2terraformamazon-iamterraform-provider-aws

How to attach multiple IAM roles to instance profile on AWS?


I'm using Terraform to create IAM and EC2 as below.

I want to attach a role named ec2_role to the EC2 instance profile. But it seems it only can attach one that created by aws_iam_instance_profile.

resource "aws_instance" "this" {
  # ..
  iam_instance_profile    = aws_iam_instance_profile.this.name
}

resource "aws_iam_instance_profile" "this" {
  name = "ec2-profile"
  role = aws_iam_role.ec2_role.name
}

About the ec2_role, it uses an ec2_role_policy. But if I set source_json = data.aws_iam_policy.amazon_ssm_managed_instance_core.policy to data "aws_iam_policy_document" "ec2_role_policy" {, it throws an error.

resource "aws_iam_role" "ec2_role" {
  name               = "ec2-role"
  assume_role_policy = data.aws_iam_policy_document.ec2_role_policy.json
}

resource "aws_iam_policy" "ec2_policy" {
  name   = "ec2-policy"
  policy = data.aws_iam_policy_document.ec2_use_role_policy.json
}

resource "aws_iam_role_policy_attachment" "attach" {
  role       = aws_iam_role.ec2_role.name
  policy_arn = aws_iam_policy.ec2_policy.arn
}

data "aws_iam_policy" "amazon_ssm_managed_instance_core" {
  arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

data "aws_iam_policy_document" "ec2_role_policy" {
  source_json = data.aws_iam_policy.amazon_ssm_managed_instance_core.policy

  statement {                                   # Doc A
    effect = "Allow"
    principals {
      identifiers = ["ec2.amazonaws.com"]
      type        = "Service"
    }
    actions = ["sts:AssumeRole"]
  }
}

data "aws_iam_policy_document" "ec2_use_role_policy" {
  statement {
    effect    = "Allow"
    actions   = ["sts:AssumeRole"]
    resources = ["arn:aws:iam::12313113231:role/s3-role"]
  }
}

The error message is:

Error: Error creating IAM Role ec2-role: MalformedPolicyDocument: Has prohibited field Resource
    status code: 400, request id: 1111111-3333-2222-4444-2131331312

  with aws_iam_role.ec2_role,
  on main.tf line 10, in resource "aws_iam_role" "ec2_role":
   10: resource "aws_iam_role" "ec2_role" {

If I remove the source_json = data.aws_iam_policy.amazon_ssm_managed_instance_core.policy from the ec2_role_policy, it works. But how to set it with Doc A together?


Solution

  • As @hars34 mentioned in their answer, an instance profile can only contain one role but that role can have multiple policies attached to it. But that's not what you're doing there or what the error is complaining about.

    Instead, you seem to be confused with the assume_role_policy (also known as a "trust policy", this controls what IAM principals are allowed to use the role such as other AWS services or different AWS accounts etc) of a role and the role's permissions policy for what the role is allowed to do (eg read and write to an S3 bucket).

    In the assume_role_policy/trust policy document you must specify a valid trust policy which must include a Principal block and cannot include a Resource block which is what your error message is complaining about:

    Error: Error creating IAM Role ec2-role: MalformedPolicyDocument: Has prohibited field Resource
        status code: 400, request id: 1111111-3333-2222-4444-2131331312
    

    Because you've concatenated your trust policy allowing EC2 instances to assume the role with a permission policy that looks like this:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "ssm:DescribeAssociation",
                    "ssm:GetDeployablePatchSnapshotForInstance",
                    "ssm:GetDocument",
                    "ssm:DescribeDocument",
                    "ssm:GetManifest",
                    "ssm:GetParameter",
                    "ssm:GetParameters",
                    "ssm:ListAssociations",
                    "ssm:ListInstanceAssociations",
                    "ssm:PutInventory",
                    "ssm:PutComplianceItems",
                    "ssm:PutConfigurePackageResult",
                    "ssm:UpdateAssociationStatus",
                    "ssm:UpdateInstanceAssociationStatus",
                    "ssm:UpdateInstanceInformation"
                ],
                "Resource": "*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "ssmmessages:CreateControlChannel",
                    "ssmmessages:CreateDataChannel",
                    "ssmmessages:OpenControlChannel",
                    "ssmmessages:OpenDataChannel"
                ],
                "Resource": "*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "ec2messages:AcknowledgeMessage",
                    "ec2messages:DeleteMessage",
                    "ec2messages:FailMessage",
                    "ec2messages:GetEndpoint",
                    "ec2messages:GetMessages",
                    "ec2messages:SendReply"
                ],
                "Resource": "*"
            }
        ]
    }
    

    which contains Resource blocks.

    If you want the role to be able to use the arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore policy and also be able to assume the arn:aws:iam::12313113231:role/s3-role role (although it would be more normal to give the permissions directly to the role rather than use role chaining and if this involves cross account access to use the S3 bucket policy to allow that role instead) then you should instead do this:

    resource "aws_iam_role" "ec2_role" {
      name               = "ec2-role"
      assume_role_policy = data.aws_iam_policy_document.ec2_assume_role_policy.json
    }
    
    resource "aws_iam_policy" "ec2_permission_policy" {
      name   = "ec2-policy"
      policy = data.aws_iam_policy_document.ec2_permission_policy.json
    }
    
    resource "aws_iam_role_policy_attachment" "attach" {
      role       = aws_iam_role.ec2_role.name
      policy_arn = aws_iam_policy.ec2_permission_policy.arn
    }
    
    data "aws_iam_policy" "amazon_ssm_managed_instance_core" {
      arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
    }
    
    data "aws_iam_policy_document" "ec2_assume_role_policy" {
      statement {
        effect = "Allow"
        principals {
          identifiers = ["ec2.amazonaws.com"]
          type        = "Service"
        }
        actions = ["sts:AssumeRole"]
      }
    }
    
    data "aws_iam_policy_document" "ec2_permission_policy" {
      source_json = data.aws_iam_policy.amazon_ssm_managed_instance_core.policy
    
      statement {
        effect    = "Allow"
        actions   = ["sts:AssumeRole"]
        resources = ["arn:aws:iam::12313113231:role/s3-role"]
      }
    }