AWS SAM - Apply policy template for a resource created conditionally

Viewed 558

I create a DynamoDb table conditionally:

 MyDynamoTable:
    Type: AWS::DynamoDB::Table
    Condition: IsDevAccount

and this is how IsDevAccount is defined using an input parameter:

Conditions:
  IsDevAccount: !Equals [ !Ref Stage, dev ]

Now I'm creating a Lambda function that accepts the table's name (amongst other things) as input through environment variables. This is done conditionally, too. Within the function's code, I'd check if the table name is passed (pass empty if condition isn't met). If so, I'd put some items into it.

However, I'm not sure how to apply policy templates to the function's role conditionally. Normally I do it like this:

MyFunction:
    Type: AWS::Serverless::Function
    Properties:
       Policies:
       - DynamoDBWritePolicy:
          TableName: !Ref MyDynamoTable

What happens to the function's execution role if the table isn't created because the condition isn't met (e.g.: in another account)? Can I apply this policy template conditionally, as well?

What I don't want to do is to blindly give write permission to all DynamoDB tables within the account.

1 Answers

Yes, you could add the condition to the DB write policy so that only when the condition is met, it will allow the write policy.

You're creating the table only if the environment is staging or development, you could apply a condition on the policy to check for your table name then apply the write policy. Example below

MyDynamoTable:
    Type: AWS::DynamoDB::Table
    Condition: IsDevAccount

Conditions:
  IsDevAccount: !Equals [ !Ref Stage, dev ]
       
MyFunction:
    Type: AWS::Serverless::Function
    Properties:
       Policies:
       - DynamoDBWritePolicy:
          Condition: !Equals [ !Ref MyDynamoTable, "myTableName" ],
          TableName: !Ref MyDynamoTable

Update in response to comments:

!Ref returns the value of the specified parameter or resource. We need parameters with allowed values for the environment and DBtable for the condition.

Parameters:
    Environment:
        Type: String
        Default: dev
        AllowedValues:
          - dev
          - stage
          - prod 

  MyDynamoTable: 
    Description: table name for the db
    Type: String
    AllowedValues:
       - tableOne
       - tableTwo
       - myTableName

 Conditions:
      IsDevAccount: !Equals [ !Ref Environment, "dev" ]
      TableExists:  !Equals [ !Ref MyDynamoTable, "myTableName" ]

MyFunction:
    Type: AWS::Serverless::Function
    Properties:
       Policies:
       - DynamoDBWritePolicy:
          Condition: !And [IsDevAccount, TableExists] // Only with TableExists condition, it'll work fine with the added parameters
          TableName: !Ref MyDynamoTable

Ref:- https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html

Update 2:

Agreed, I researched and confirmed, there is no way to check for resources created in the same stack template (That's why suggested parameter). Conditions are all parameter based.

However if the resource was created already in other stack, you could do this through Resource import. I don't think, resource import will be of help in your requirement.

However, a workaround would be to have Boolean parameters for TableExists condition and can pass the value through AWS CLI on the run like below,

MyDynamoTable: 
    Description: dynamo db table
    Type: String
    AllowedValues:
       - true
       - false
       
 Conditions:
      TableExists:  !Equals [ !Ref MyDynamoTable, "true" ]

 MyFunction:
    Type: AWS::Serverless::Function
    Properties:
       Policies:
       - DynamoDBWritePolicy:
          Condition: !Ref TableExists 
          TableName: !Ref MyDynamoTable

AWS CLI on deploy pass required parameters

aws cloudformation deploy --template templateName.yml --parameter-overrides MyDynamoTable="true" dynamoDBtableName="myTableName" (any parameter required)
Related