'Create role only if FindInMap value is present? / What to do if Mapping doesn't have values for some keys?

I have a role that I want to create only if there are entries for the given keys in the Mapping. The confusion comes when I have no values to put in for certain key / values (in this case, some regions don't have a value for "XClientPrincipals", so I get an error.

Example: The map:

  alpha: ...
  beta:
    Region1:
      YClientPrincipals:
        - 'arn:aws:iam::#########:root'
    Region3:
      XClientPrincipals:
        - 'arn:aws:iam::#########:root'
      YClientPrincipals:
        - 'arn:aws:iam::#########:root'
  prod:
    Region1:
      YClientPrincipals:
        - 'arn:aws:iam::#########:root'
    Region2:
      XClientPrincipals:
        - 'arn:aws:iam::#########:root'
        - 'arn:aws:iam::#########:root'
      YClientPrincipals:
        - 'arn:aws:iam::#########:root'
    Region3:
      XClientPrincipals:
        - 'arn:aws:iam::#########:root'
        - 'arn:aws:iam::#########:root'
      YClientPrincipals:
        - 'arn:aws:iam::#########:root'

The role:

  # Role definition for LMSortPlanningWorkflow - https://issues.amazon.com/LMCP-3533
  XClientRole:
    Type: AWS::IAM::Role
    Condition: IsBetaOrProd # Don't create external client roles in Alpha
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action: 'sts:AssumeRole'
            Effect: Allow
            Principal:
              AWS:
                'Fn::FindInMap': [ { Ref: 'Domain' }, { Ref: 'Region' }, 'XClientPrincipals' ]
      RoleName: { 'Fn::Sub': 'XClientRole' }
      ManagedPolicyArns:
        - Ref: SomeApiCallPolicy

The issue is that XClientPrincipals only applies to some Regions, and we only need to create the roles in those regions (and they're different between beta and prod). If I don't provide something in the map for every possible combination of domain and region we have, it will error when deploying for that domain + region because it can't find anything in the map. But if I specify an empty array for that domain + region, that's also an error because the principal needs a value. Is there a way to create a condition for only creating the role if it can find the value in the map first?

I'm pretty sure I could create a condition to only create the role and spell out acceptable domain and region values (1st and 2nd key) that should evaluate to true, but the actual mapping is a little more complicated than this, so it would be great to be able to just do a condition like:

  RunLambdaInVPC:
    Fn::Not: [ {Ref: FindInMap .... }, null]

but I don't think I've seen any examples of this or figured out a way to get something like this to work.



Solution 1:[1]

Yes, you can conditionally create resources based on the value of a certain mapping.
Here's an example that shows how the value of a region map can be used in a Condition, that can then be used to determine whether a resource should be created or not:

AWSTemplateFormatVersion: "2010-09-09"
Mappings: 
  RegionMap: 
    us-east-1: 
      "HVM64": "ami-0ff8a91507f77f867"
    us-west-1: 
      "HVM64": "ami-0bdb828fd58c52235"
  IsProdMapping: 
    us-east-1: 
      "prod": "True"
    us-west-1: 
      "prod": "False"

Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", HVM64]
      InstanceType: t3.nano
      SubnetId: !Ref Subnet
  RootRole:
    Type: 'AWS::IAM::Role'
    Condition: CreateIAMRole
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: '*'
                Resource: '*'
Conditions:
  CreateIAMRole: !Equals 
    - !FindInMap [IsProdMapping, !Ref "AWS::Region", "prod"]
    - "True"
Parameters:
  VPC:
    Type: AWS::EC2::VPC::Id
    Description: The VPC where you want to deploy
  Subnet:
    Type: AWS::EC2::Subnet::Id
    Description: The subnet where you want to deploy

If I deploy this template in us-east-1, RootRole is created.
If I deploy this template in us-west-1, RootRole is not created.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1