Referencing !Ref DynamoDB table name in a AWS CloudFormation template
Asked Answered
M

5

18

I am trying to locally test passing the table name of a DynamoDB table as declared in my CloudFormation template file.

From all the documentation I have read, I should be able to reference the the TableName property value of a DynamoDB resource using the !Ref intrinsic function. However when I test this locally the property is undefined.

Consider the following example:

Transform: 'AWS::Serverless-2016-10-31'
Resources:
  ServerlessFunction:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: nodejs10.x
      Handler: index.handler
      Environment: 
        Variables:
          TABLE_NAME: !Ref DynamoDBTable # <- returning undefined
      Events:
        GetCocktails:
          Type: Api
          Properties:
            Path: /
            Method: get
  DynamoDBTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: DynamoDBTableName
      AttributeDefinitions:
        - AttributeName: ID
          AttributeType: S
      KeySchema:
        - AttributeName: ID
          KeyType: HASH
      ProvisionedThroughput: 
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1

I expect the TABLE_NAME environment variable to be DynamoDBTableName however it returns undefined. How do I get the template to work as expected?

Multistage answered 6/8, 2019 at 3:23 Comment(4)
I just fixed your question to refer to CloudFormation, because your problem relates to CloudFormation, not SAM. That said after a brief look it should work as you expect it. Can you please also add the code of your AWS Lambda function where you try to access the environment variable?Keel
I was looking how to do the same and found your question here. The thing you did worked for me as is. Anyway thank you for asking it here.Ectomere
Could anyone provide the answer/solution for cloudformation templates in a JSON format?Trojan
As of 2022, and using sam, the op's original syntax is correct for getting the table name, re: !Ref DynamoDBTable. See Ref will return the DynamoDB table nameEndodermis
S
17

As stated by someone else the only attributes exposed by an AWS::DynamoDB::Table are: Arn and StreamArn (See AWS CloudFormation DynamoDB Documentation).

Provided the syntax of the DynamoDB's arns you can retrieve the table name in the following way:

      Environment: 
        Variables:
          TABLE_NAME: !Select [1, !Split ['/', !GetAtt DynamoDBTable.Arn]] 

which will return DynamoDBTableName.

Sinegold answered 26/12, 2019 at 2:44 Comment(3)
You are correct the attributes exposed are Arn and StreamArn. i.e calling !GetAttr but one calls !Ref the table name is returnedRadix
"For the resource with the logical ID myDynamoDBTable, Ref will return the DynamoDB table name. " i.e. Ref returns the name of the tableBekha
Thanks for the code snippet. On my current setup, it correctly works when deployed to AWS, where !GetAtt correctly returns a dynamoDB table arn similar to arn:aws:dynamodb:eu-central-1:739786912520:table/ ..., but when trying with sam local start-api, strangely the arn then points to a lambda, e.g. arn:aws:lambda:us-east-1:123456789012:function:.... I guess it's some kind of internal AWS proxy. SAM is quite disappointing on that front IMHO, Terraform (which also has its drawbacks) handles this kind of cross-reference in a much cleaner way.Congenial
M
5

I was struggling with the same, it looks like you can't access the TableName.

The workaround I'm doing is to call the resource in the tamplate.yml with the same table name...

In template.yml:

Globals:
  Function:
    Runtime: nodejs12.x
    ...
    Environment:
      Variables:
        DYNAMODB_TABLE: !Ref MyDynamoTable
MyDynamoTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: MyDynamoTable
      AttributeDefinitions:
        - AttributeName: PK
          AttributeType: S
        - AttributeName: SK
          AttributeType: S

Then, in my .js component:

const tableName = process.env.DYNAMODB_TABLE;

I hope they solve this soon.... this way is not ideal. Cheers!

Maryettamaryjane answered 31/3, 2021 at 23:21 Comment(0)
B
1

!Ref returns a name of the resource by "logical name" and should work for your situation. However resource (your table) should exists before you reference it, so you have 2 options:

  • declare you table resource earlier in the template (before you reference it)

  • use DependsOn property on your lambda like

ServerlessFunction:
    Type: AWS::Serverless::Function
    DependsOn: DynamoDBTable

As for using Fn::GetAtt:

Based on AWS Documentation when you create a resource of type AWS::DynamoDB::Table there are only 2 attributes available for Fn::GetAtt:

  • Arn
  • StreamArn

which means there is no way to get TableName attribute for DynamoDB table with Fn::GetAtt in current version of CloudFormation.

Bot answered 18/12, 2019 at 3:55 Comment(1)
FYI: The DependsOn attribute is implicit wen using !Ref (docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/…)Bradleigh
R
-2

Even though !Ref does return the Logical id of the resource, in this case the DyanmoDB table name, you need to make sure that the resource exist before referencing it.

As far as I can see in your code the function creation is not dependent on the Dynamo table beign created first

ServerlessFunction:
Type: AWS::Serverless::Function
Properties:
  Runtime: nodejs10.x
  Handler: index.handler
  Environment: 
    Variables:
      TABLE_NAME: !Ref DynamoDBTable 
  Events:
    GetCocktails:
      Type: Api
      Properties:
        Path: /
        Method: get

In this case CFN will create resources from top to bottom meaning the lambda function will be created before the DyanoDb table causing the !Ref to be undefined given the fact that the table does not exist yet. You can fix this by adding a dependency on the lambda resource like so:

ServerlessFunction:
DependsOn: DyanamoDBTable # <- will not be created before the dyanmoDB table
Type: AWS::Serverless::Function
Properties:
  Runtime: nodejs10.x
  Handler: index.handler
  Environment: 
    Variables:
      TABLE_NAME: !Ref DynamoDBTable
  Events:
    GetCocktails:
      Type: Api
      Properties:
        Path: /
        Method: get

This will make sure that no matter what, your table is created first and then the !Ref to the resource will not be undefined since the table already exist and can be referenced

Reckoning answered 26/9, 2019 at 18:30 Comment(1)
you can refer to the return values of the resource here docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/…Reckoning
N
-5

The !Ref DynamoDBTable returns the ARN from your resource so, you get your reference response like this:

arn:aws:dynamodb:us-east-1:123456789012:table/testddbstack-myDynamoDBTable-012A1SL7SMP5Q/stream/2015-11-30T20:10:00.000.

Instead you only need to provide the name DynamoDBTableName so, my suggestion is to use !GetAtt CloudFormation intrinsic function and get the name: !GetAtt DynamoDBTable.TableName

Need answered 24/9, 2019 at 18:55 Comment(2)
I'm getting An error occurred (ValidationError) when calling the CreateChangeSet operation: Template error: resource myDynamoDbTable does not support attribute type TableName in Fn::GetAttGeomorphology
Actually the !Ref function for an AWS DynamoDB resource returns the table name, not the ARN. I was getting the same error as the comment above when trying to use !GetAtt. I'm not working with environment variables, but I can get the table name of my DynamoDB resource with !Ref.Unit

© 2022 - 2024 — McMap. All rights reserved.