Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I supply different conditions based on a parameter in an IAM Role CloudFormation Template

I'm writing a CloudFormation template for an IAM role that I will assume through STS. I need to add a condition where a key equals a value, where both the key and value depends on a "Stage" parameter. The value I've been able to programmatically change depending on the parameter, but all my attempts to change the key based on Stage have failed.

I've tried both using a map and !FindInMap to get the correct key, as well as tried to construct a condition with both cases using !If.

In the first case...

Mappings:
  Constants:
    beta:
      Id: "beta.example.com"
      Endpoint: "beta-link.example.com/api/oauth2/v2:aud"
    prod:
      Id: "example.com"
      Endpoint: "link.example.com/api/oauth2/v2:aud"

AssumeRolePolicyDocument:
  Statement:
    Action:
      - "sts:AssumeRoleWithWebIdentity"
    Condition:
      StringEquals:
        !FindInMap [Constants, !Ref Stage, Endpoint]:
          - !FindInMap [Constants, !Ref Stage, Id]

... I got an error: map keys must be strings; received a map instead

In the second case...

AssumeRolePolicyDocument:
  Statement:
    Action:
      - "sts:AssumeRoleWithWebIdentity"
    Condition:
      !If
        - !Equals [!Ref Stage, prod]
        - StringEquals:
          "link.example.com/api/oauth2/v2:aud": "example.com"
        - StringEquals:
          "beta-link.example.com/api/oauth2/v2:aud": "beta.example.com"

...I got another error: Template format error: Conditions can only be boolean operations on parameters and other conditions

In short, how can I specify a condition where both key and value depend on a parameter?

like image 927
jaxuru Avatar asked Aug 30 '19 01:08

jaxuru


Video Answer


1 Answers

I have struggled around 8 hours continuously and finally found an answer to this problem. Just for the viewers I would like to summarise the problem:

Problem Statement : As a infrastructure engineer, I want to write a cloudformation resource for AWS::IAM::Role which defines a AssumeRolePolicyDocument with a Condition clause where the key needs to be parameterised. Also another question is can we use intrinsic functions with Condition keys?

Answer: Yes, it is possible using an out of the box style. AssumeRolePolicyDocument is a String Type as per the AWS Cloudformation documentation for 'AWS::IAM::Role'. So instead of using YAML style data to the AssumeRolePolicyDocument property, just pass a raw JSON formatted Assumerole policy using Fn::Sub and use variables to replace the key without any issues or warnings. Below is an example that you can use. Its a complex use case that i was trying to solve but it shows how you can replace keys under a IAM Condition clause using !SUB,!Select etc.

  ExampleAppRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Sub ${Environment}-ExampleAppRole
      AssumeRolePolicyDocument: 
                            Fn::Sub: 
                              - |
                                {
                                  "Version": "2012-10-17",
                                  "Statement": [
                                    {
                                      "Effect": "Allow",
                                      "Principal": {
                                        "Federated": "arn:aws:iam::${AWS::AccountId}:oidc-provider/oidc.eks.${AWS::Region}.amazonaws.com/id/${id}"
                                      },
                                      "Action": "sts:AssumeRoleWithWebIdentity",
                                      "Condition": {
                                        "StringEquals": {
                                          "${clusterid}": "${serviceaccount}"
                                        }
                                      }
                                    }
                                  ]
                                }
                              - 
                                clusterid: !Join ["",[!Sub "oidc.eks.${AWS::Region}.amazonaws.com/id/",!Select [1, !Split ["//", !Select [0, !Split [".", !GetAtt Cluster.Endpoint]]]],":sub"]]
                                id: !Select [1, !Split ["//", !Select [0, !Split [".", !GetAtt Cluster.Endpoint]]]]
                                Region: !Sub ${AWS::Region}
                                serviceaccount: system:serviceaccount:default:awsiamroleexample
      Path: /
      ManagedPolicyArns:
        - !Ref SnsPublishAccessPolicy

Let me know your thoughts in the comments section.

like image 95
Akash Avatar answered Sep 21 '22 15:09

Akash