Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Fn::If with array values in cloud formation templates

I am working on a cloud formation template for a KMS key. In the policy document I want to set the the principals depending on the stage (whether it is prod or test). I can use Fn:If easily if there is only one principal for both stages. But I have more than one principals for each stage and Fn:If only allows you to assign a value, not a collection according to my understanding (correct me if I am wrong).

I have tried assigning a collection to the value and it gives me "map keys must be strings; received a collection instead" error when validating the template using the CloudFormation designer in AWS accounnt.

"MyEncryptionKey": {
            "DeletionPolicy": "Retain",
            "Properties": {
                "Description": "MyEncryptionKey",
                "EnableKeyRotation": true,
                "Enabled": true,
                "KeyPolicy": {
                    "Statement": [
                        {
                            "Action": "kms:*",
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": "root"
                            },
                            "Resource": "*"
                        },
                        {
                            "Action": "kms:Decrypt",
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": [
                                    {
                                        "Fn::If": [
                                            "IsProd",
                                            {["arn1","arn2"]},
                                            "arn2"
                                        ]
                                    }
                                ]
                            },
                            "Resource": "*"
                        }
                    ]
                }
            },
            "Version": "2012-10-17",
            "Type": "AWS::KMS::Key"
        }

Ideally the second statement in key policy should have a two arn values if prod and one arn value if not prod.

I am also open to explore if there is any other way of achieving this instead of using Fn::If here

like image 475
SanD Avatar asked Jul 10 '19 12:07

SanD


People also ask

What is FN :: GetAtt in CloudFormation?

The Fn::GetAtt intrinsic function returns the value of an attribute from a resource in the template. For more information about GetAtt return values for a particular resource, refer to the documentation for that resource in the Resource and property reference.

How do you add condition in CloudFormation?

You define all conditions in the Conditions section of a template except for Fn::If conditions. You can use the Fn::If condition in the metadata attribute, update policy attribute, and property values in the Resources section and Outputs sections of a template.

How do you reference parameters in CloudFormation?

You use the Ref intrinsic function to reference a parameter, and AWS CloudFormation uses the parameter's value to provision the stack. You can reference parameters from the Resources and Outputs sections of the same template.

What is FN :: sub in AWS?

Fn::Sub. The intrinsic function Fn::Sub substitutes variables in an input string with values that you specify. In your templates, you can use this function to construct commands or outputs that include values that aren't available until you create or update a stack.


2 Answers

Instead of considering value evaluated from Fn::If as a single array item, consider it an array. Replace the Principal with the following and it will work.

JSON

{
  "Principal": {
    "AWS": {
      "Fn::If": [
        "IsProd",
        [
          "arn1"
        ],
        [
          "arn1",
          "arn2"
        ]
      ]
    }
  }
}

It will look simple in yaml

Principal:
  AWS:
    Fn::If:
    - IsProd
    - - arn1
    - - arn1
      - arn2
like image 75
chamikaw Avatar answered Nov 15 '22 06:11

chamikaw


Actually if you insert an empty element (AWS::NoValue) in your array, CloudFormation will ignore it. So you can do this instead of duplicating all common elements of the array:

Principal:
  AWS:
    - "arn1"
    - "Fn::If":
      - "IsProd"
      - Ref: "AWS::NoValue"
      - "arn2"

I'm not sure it works in your specific use case, but I just used it to conditionnaly add a CloudFront cache behavior and origin, and it worked.

like image 42
Maxime Rossini Avatar answered Nov 15 '22 07:11

Maxime Rossini