When creating a new IAM role resource using Cloudformation, AWS::IAM::Role.
There is an optional RoleName parameter, and I am not sure when if it is better practice to include the RoleName parameter or exclude it.
The following are the differences that are specified in the documentation
If you don't specify a name, AWS CloudFormation generates a unique physical ID and uses that ID for the role name.
If you specify a name, you must specify the CAPABILITY_NAMED_IAM value to acknowledge your template's capabilities.
Naming an IAM resource can cause an unrecoverable error if you reuse the same template in multiple Regions.
Are there any other functional differences betweeen using or excluding the RoleName parameter?
This will simply generate a name for you if you don't specify it (constructed from the stack and resource name with a random string at the end).
The only difference is re-usability. If you want to recreate the stack multiple times, it would be better to pass in this IAM role ARN as a parameter.
I'd recommend not naming, which will allow you the freedom to create the stack as many times as you want without the fear of breaking additional resources.
If you really must the recommendation from AWS is below:
Naming an IAM resource can cause an unrecoverable error if you reuse the same template in multiple Regions. To prevent this, we recommend using Fn::Join and AWS::Region to create a Region-specific name, as in the following example: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, {"Ref": "MyResourceName"}]]}.
The documentation explains that it may be dangerous to name your roles:
Naming an IAM resource can cause an unrecoverable error if you reuse the same template in multiple Regions. To prevent this, we recommend using Fn::Join and AWS::Region to create a Region-specific name, as in the following example: {"Fn::Join": ["", [{"Ref": "AWS::Region"}, {"Ref": "MyResourceName"}]]}.
Related
I'm using serverless framework to deploy a lambda, and optionally a dynamo db table the lambda accesses.
In development stages sls creates the table as a full fledged resource (and therefore I have a reference to the table)
Alternately, in production stages sls does not create the table, but a custom variable knows the precreated table's ARN
Having problems crafting the Resource clause of the lambda's iam policy document, probably because I'm not understanding the allowable mix between Cloudformation IF statements and sls dynamic variables. Here's as far as I've gotten (abbreviated):
provider:
... unrelated stuff
iam:
role:
statements:
- Effect: 'Allow'
Action:
- dynamodb:DescribeTable
- ..more DynDB permissions
Resource:
- !If [ ${self:custom.slsMadeTheTable}, Fn::GetAtt:[MyDynDBTable, Arn], ${self:custom.staticArnOfPrecreatedTable} ]
Where:
${self:custom.slsMadeTheTable} refers to a boolean valued string from custom variables that defines whether sls made the table (so get its reference and use it in resource) or not (so use a known static Arn)
${self:custom.staticArnOfPrecreatedTable} is a custom variable whose value is a literal ARN string of a precreated DDB table
In this particular configuration I get a syntax error suggesting I've incorrectly combined the Cloudformation if with sls expressions.
Cannot parse "serverless.yml": missed comma between flow collection entries in "../serverless.yml" (64:22)
64 | - !If [ ${self:custom.slsMadeTheTable}, Fn::Ge ...
---------------------------^
Tried many other variants, none successful.
Looking for help crafting a policy that will either handle a created DDB table or a precreated ARN of a table depending on a determining variable (tied to stage of course)
--- Another failing approach ---
The Resource is happy to accept an ARN string in a custom variable, e.g.
Resource: ${self:custom.productionTableArn}
So you'd think a simple defaulted expression would do the trick, along the lines of
Resource:
${self:custom.devTableArn,
self:custom.productionTableArn}
Where the self:custom.devTableArn contained the contents of the created DDB table.
But the reference to the sls ddb table arn: "Fn::GetAtt": [ TunesTable, Arn ] which works perfectly fine as a direct entry for the resource, fails when set as a value for self:custom.devTableArn:
devTableArn: "Fn::GetAtt": [ TunesTable, Arn ]
with the error:
Cannot parse "serverless.yml": bad indentation of a mapping entry in "/home/rpc/tune-library-tunes/serverless.yml" (24:28)
24 | devTableArn: "Fn::GetAtt": [ TunesTable, Arn ]
The key to the problem was realizing that SLS is, as often stated, mainly a wrapper to CloudFormation (CFN). More than I'd thought. CFN scripting is executed during CFN deployment, not SLS interpretation.
So any association between a DynDB SLS resource and a custom expression is purely syntactic. Inserting "Fn::GetAtt": [ TunesTable, Arn ] in our IAM resource isn't in a context that is aware of a TuneTable in SLS, it doesn't calculate and substitute, it literally sticks the string into the CFN template, to be executed later for good or ill.
Knowing that narrowed the problem to 'what expressions in CFN will work and can SLS generate them' which led to a plugin solution from https://www.npmjs.com/package/serverless-plugin-ifelse. I hope the functionality is included in a future base SLS release (but with a simpler syntax).
I've got a CloudFormation stack that creates several IAM roles. I have the somewhat unusual-sounding requirement to include a statement in the role's AssumeRole policy preventing the role from assuming itself. The role names are constructed using Fn::Join with the value of a parameter, so in order to reproduce the role name and construct its ARN in the policy statement, I have to duplicate that expression.
Is there any way to cache the result of the expression so that I don't have to repeat it? There are several of these roles and each one has the same issue. Can I define a parameter whose default value includes the value of another parameter and reference that? Or can I use Fn:GetAtt inside the definition of a resource to access other attributes of the resource being defined (such as its name)?
If not, is there another solution besides repeating the name-construction expressions? This is something I would put in the locals block in Terraform...
Since this wound up not being possible, I ran this shell one-liner to update each role after creation, to prevent it from assuming itself:
aws iam update-assume-role-policy \
--role-name "$role" \
--policy-document file://<(
aws iam get-role --role-name "$role" |
jq '.Role | .Arn as $arn | .AssumeRolePolicyDocument |
.Statement+=[{"Effect": "Deny",
"Action": "sts:AssumeRole",
"Principal": { "AWS": $arn }}]')
Is there any way to cache the result of the expression so that I don't have to repeat it?
Sadly, there is no such way in plain CloudFormation.
Can I define a parameter whose default value includes the value of another parameter and reference that?
Unfortunately, parameters can't refer other parameters.
Or can I use Fn:GetAtt inside the definition of a resource to access other attributes of the resource being defined (such as its name)?
You can't get any return values of a resource before it is fully constructed.
Having sad that, if you know the role name up-front, you could construct its ARN yourself. Role ARNs have known format. However, you want be able to use it as a Principal in the same role, as principles must be valid. Thus, you can't use ARN of a role as a principle before it actually exists.
If you want to add ARN of a role to be its own Principal, you would have to construct a custom resource in CloudFormation. Thus, you first create your role normally, and then use the custom resource (in a form of lambda function) to update it's Principal element.
Also since you have to duplicate a lot of elements in your templates, a general solution to this is by means of CloudFormation macros.
Either way, it seems to me that because your requirements regarding templates are not normally supported out of the box, you will have to develop some custom resources and/or macros to fully satisfy them. With custom resources and macros you can basically achieve what ever you want, but it will require extra development.
The situation
I am generating a KMS Key in CloudFormation. According to the KMS policy documentation, it is crucial to create a policy where the Principal is the account itself, in order for IAM policies to be able to grant access to the key.
The question
How can I create the ARN for the account root in CloudFormation?
For those who use YAML for their CloudFormation templates:
!Sub arn:aws:iam::${AWS::AccountId}:root
The answer
{
"Fn::Join":[
":",
[
"arn:aws:iam:",
{
"Ref":"AWS::AccountId"
},
"root"
]
]
}
Why does this work?
First, let's examine the line, "Ref":"AWS::AccountId". This is a pseudo parameter reference, which is a fancy way of saying that it is a parameter that comes out of the box with CloudFormation. There are many such parameters. This one happens to give us the account ID, which is crucial for constructing the ARN.
Now, the rest is just the creation of an ARN using this account ID. Fn::Join is simply a CloudFormation built-in that allows concatenation of strings. This is crucial when combining references with string constants (or other references) as we are doing here.
The result is something like...
arn:aws:iam::123456789012:root
i am trying to launch a CFT using the AWS Cloud Trail API. The problem is, when i launch the CFT directly into the AWS console it presents me with a IAM capability check screen
how can i handle this check box while using the API.
Using the API
Capabilities.member.N
A list of values that you must specify before AWS CloudFormation can create certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter.
The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. The following resources require you to specify this parameter: AWS::IAM::AccessKey, AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, and AWS::IAM::UserToGroupAddition. If your stack template contains these resources, we recommend that you review all permissions associated with them and edit their permissions if necessary.
If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM. If you don't specify this parameter, this action returns an InsufficientCapabilities error.
For more information, see Acknowledging IAM Resources in AWS CloudFormation Templates.
Type: Array of strings
Valid Values: CAPABILITY_IAM | CAPABILITY_NAMED_IAM
http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html
Using the AWS CLI
--capabilities (list)
A list of values that you must specify before AWS CloudFormation can create certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter.
The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM . The following resources require you to specify this parameter: AWS::IAM::AccessKey , AWS::IAM::Group , AWS::IAM::InstanceProfile , AWS::IAM::Policy , AWS::IAM::Role , AWS::IAM::User , and AWS::IAM::UserToGroupAddition . If your stack template contains these resources, we recommend that you review all permissions associated with them and edit their permissions if necessary.
If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM . If you don't specify this parameter, this action returns an InsufficientCapabilities error.
http://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-stack.html
I'm trying to make AutoScalingGroup names on cloud formation templates dynamic. I was thinking if this is possible via parameters, or any other way?
"DynamicASGName": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
...properties here...
}
}
CloudFormation uses two sets of names: the logical resource name, to identify a resource within the stack, and the physical name which uniquely identifies it across the whole region.
CloudFormation doesn't support setting the logical name dynamically, but with certain types, you can set the physical name in the template with the Name property. For example:
MyUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref MyUserPool
ClientName: !Sub '${AppName}-userpoolclient'
GenerateSecret: false
Unfortunately, AutoScalingGroup doesn't support this.
A better solution is probably to use Tags on your resources. Most AWS resource types (including AutoScalingGroup) support Tags and they can be set dynamically in a CloudFormation template.