2
votes

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?

2

2 Answers

5
votes

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"]
  }
}
4
votes

An instance profile can contain only one IAM role, although a role can be included in multiple instance profiles. This limit of one role per instance profile cannot be increased. You can remove the existing role and then add a different role to an instance profile. You must then wait for the change to appear across all of AWS because of eventual consistency. To force the change, you must disassociate the instance profile and then associate the instance profile, or you can stop your instance and then restart it.Please refer the below documentation for further queries, https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2_instance-profiles.html