6

In my AWS account, I am building a new Cloudformation template that creates new policies, and I want to attach those to a few existing roles in the account. Here is how I have been trying to reference them:

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Names of existing roles to which the restrictive policies need added",
    "Parameters": {
        "AdditionalExecutionRoleParameter": {
            "Type": "AWS::IAM::Role",
            "Default": "CloudOps"
        }
    },
    "Resources": { (and so on)

Then down in the section below the new policies, I have been trying to reference these existing roles ("AdditionalExecutionRoleParameter" in this case) and attach the policies to them using the Roles parameter. However, I keep getting a "failed to retrieve external values" error when trying to deploy the CloudFormation template... I've tried inputting "CloudOps", which is the role name, as the parameter "Default", and I've also tried inputting the role ARN there... nothing is working.

lorena
  • 386
  • 1
  • 2
  • 15
  • 1
    Could you provide the relevant section of the template shownig how and where you are referencing `AdditionalExecutionRoleParameter`? – Marcin Apr 01 '20 at 00:25
  • Sure... I'm referencing it down with the other roles: }, "Roles": [ { "Ref": "NewRoleNoProblem" }, { "Ref": "AdditionalExecutionRoleParameter" } ] } }, There's no issue with attaching the policies to the new roles that I'm creating within the template; the issue is how to attach to existing roles in the account that my template isn't creating – lorena Apr 01 '20 at 00:27
  • Ah i see. @jarmod already pointed out the issue. Can use `String` data type instead of `AWS::IAM::Role` to pass the role. – Marcin Apr 01 '20 at 00:34
  • Sorry, I should have mentioned I already tried that... and I got this error when I tried to deploy: The specified value for roleName is invalid. It must contain only alphanumeric characters and/or the following: +=,.@_- (Service: AmazonIdentityManagement; Status Code: 400; Error Code: ValidationError; Request ID: f2958ace-4cf8-4079-a447-eb3fe3bbe8e0) What I'd inputted was the role ARN, using * as the account ID so I can deploy to multiple accounts. Any other ideas? – lorena Apr 01 '20 at 00:39
  • Usually you use `!Sub ` with `${AWS::AccountId}` to provide account specific id in your templates when you use role arns as strings. – Marcin Apr 01 '20 at 01:01
  • Parameter don't support intrinsic values such as !Sub or ${AWS::AccountId} – lorena Apr 01 '20 at 01:54
  • What about macros or custom resources? Seems to me that you trying to do what cloudformation simply does not support. For such scenarios you can define your macros or custom resources. – Marcin Apr 01 '20 at 01:57
  • 1
    That may be... thank you for the advice! I am pretty familiar with CFTs but not so much with macros, etc., or how I would implement that in a useful way with my templates. I'll have to look into it. – lorena Apr 01 '20 at 02:09
  • The other alternative I see is to redesign how you use your templates. For macro a basic find-and-replace type could be worth considering. – Marcin Apr 01 '20 at 02:25

2 Answers2

6

Not all AWS resource types are supported in the parameter type field.

The full list is at AWS-Specific Parameter Types. It includes, for example:

  • AWS::EC2::VPC::Id
  • List<AWS::EC2::VPC::Id>
  • AWS::SSM::Parameter::Name

This list does not include AWS::IAM::Role (or any IAM resources).

If you're simply trying to associate a new IAM policy with an existing named IAM role, then note that the AWS::IAM::Policy construct has a Roles property and you should supply a list of role names to apply the policy to. It requires role names, not role ARNs, so you don't need the account ID.

If you do ever need the account ID then it's available as a pseudo-parameter and you can get it from "Ref" : "AWS::AccountId", but I don't think you need it here.

Here's a JSON example of how to create a new IAM policy (allowing s3:Get* on mybucket/*) and associate it with an existing IAM role, whose name you supply as a parameter to the stack:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "Add policy to role test",
  "Parameters": {
    "TheRoleName": {
      "Type": "String",
      "Default": "CloudOps",
      "Description": "Name of role to associate policy with"
    }
  },
  "Resources": {
    "ThePolicy": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyName": "s3-read",
        "PolicyDocument": {
          "Statement": {
            "Effect": "Allow",
            "Action": [
              "s3:Get*"
            ],
            "Resource": ["arn:aws:s3:::mybucket/*"]
          }
        },
        "Roles": [
          {
            "Ref": "TheRoleName"
          }
        ]
      }
    }
  }
}
jarmod
  • 71,565
  • 16
  • 115
  • 122
  • 1
    So is there some other way to reference my existing roles and attach new policies to them? I'm pretty sure there has to be a way to do that... I cannot be the first ever to create new policies via CloudFormation and want to attach them to existing roles... – lorena Apr 01 '20 at 00:34
  • You should, for example, be able to create a policy using `AWS::IAM::Policy` and set its `Roles` property to a list including the name(s) of the roles to apply the policy to. That name can come from a parameter. Note: I haven't tested this exact case. – jarmod Apr 01 '20 at 00:37
  • That is what I have been trying unsuccessfully to do. :) The error with "String" data type and * for account ID is due to the account ID being left as *, but I really need to find a way to create a parameter that doesn't have to call out the account ID, as it's going to change across various accounts. – lorena Apr 01 '20 at 00:41
  • You can use `AWS::AccountId` as a pseudo parameter anywhere in a template e.g. `!Ref "AWS:: AccountId"` or `!Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${vpc}'`. But, that said, the policy simply needs a role name, not an ARN, afaik. – jarmod Apr 01 '20 at 01:11
  • I'm not following... I'm looking for how to use it in a parameter, and Params don't support intrinsic values such as !Sub or !Ref (though I can use that down in the policy to reference them). Where does AWS::AccountId fit into the below? { "AWSTemplateFormatVersion": "2010-09-09", "Description": "Names of existing roles to which the restrictive policies need added", "Parameters": { "AdditionalExecutionRoleParameter": { "Type": "String", "Default": "arn:aws:iam::aws*:role/CloudOperationsExecutionRole" } – lorena Apr 01 '20 at 01:53
  • The parameter should be the role name, not an attempt at an ARN. You don't need the ARN. I've added a complete, working JSON example. Hope this helps. – jarmod Apr 01 '20 at 14:00
2

Well... what I ended up doing is something as simple as this, which works fine...

"Parameters": {
    "RoleNameRoleParameter": {
        "Type": "String",
        "Default": "RoleNameRole"
lorena
  • 386
  • 1
  • 2
  • 15
  • 1
    Well yes, of course, you can always provide a parameter value explicitly. My answer relates to a feature that a) validates the parameter value is of a specific resource type as opposed to merely a string and b) provides you with a convenient dropdown list of said resource type in the CloudFormation console. – jarmod Jan 08 '21 at 22:41
  • Where this is referred in EC2 Instance? Could you please add that? – Hary Sep 08 '22 at 12:09