I'm trying to create an AWS Role which prevents CloudFormation delete a table. For instance, I created my table as follows:
UsersDynamoDBTable:
Type: AWS::DynamoDB::Table
Description: Users DynamoDB Table
Properties:
AttributeDefinitions:
- AttributeName: hashKey
AttributeType: S
- AttributeName: rangeKey
AttributeType: S
KeySchema:
- AttributeName: hashKey
KeyType: HASH
- AttributeName: rangeKey
KeyType: RANGE
BillingMode: PAY_PER_REQUEST
GlobalSecondaryIndexes:
- IndexName: index-rangeKey
KeySchema:
- AttributeName: rangeKey
KeyType: HASH
- AttributeName: hashKey
KeyType: RANGE
Projection:
ProjectionType: ALL
Now suppose that a develop accidentally delete this lines and update stack. This way the table with all its data would be removed. So I'd like to create a role which prevents CloudFormation delete DynamoDB tables. My first attempt was creating the Role below, but it didn't work.
PreventCloudFormationDeleteTableIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- cloudformation.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: PreventTableDeletePolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Deny
Action:
- dynamodb:DeleteTable
Resource:
- !Join
- '/'
- - !Join [':', ['arn:aws:dynamodb', !Sub '${AWS::Region}', '*', 'table']]
- !Join ['', [!Sub '${StackName}', '*']]
Am I missing some Role configuration?
Thank you.
You can use a DeletionPolicy of RETAIN to prevent the table from being deleted when the stack is removed or table is removed from the template. Also the new UpdateReplacePolicy will prevent CloudFormation from deleting the table when it needs to do so due to primary key changes.
Considering that the role is properly attached to the invoking user/principal, is it possible that the policy Arn from that join does not match the table Arn?
Also consider retaining the resource instead of denying the operation:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html
Related
I configured my AWS stack using AWS CloudFormation as follow
Resources:
DynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
-
AttributeName: "PK"
AttributeType: "S"
-
AttributeName: "SK"
AttributeType: "S"
-
AttributeName: "GSI1_PK"
AttributeType: "S"
-
AttributeName: "GSI2_PK"
AttributeType: "S"
-
AttributeName: "GSI1_SK"
AttributeType: "S"
-
AttributeName: "GSI2_SK"
AttributeType: "S"
KeySchema:
-
AttributeName: "PK"
KeyType: "HASH"
-
AttributeName: "SK"
KeyType: "RANGE"
GlobalSecondaryIndexes:
-
IndexName: "line_tickets"
KeySchema:
-
AttributeName: "GSI1_PK"
KeyType: "HASH"
-
AttributeName: "GSI1_SK"
KeyType: "RANGE"
Projection:
ProjectionType: "ALL"
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
-
IndexName: "users_tickets"
KeySchema:
-
AttributeName: "GSI2_PK"
KeyType: "HASH"
-
AttributeName: "GSI2_SK"
KeyType: "RANGE"
Projection:
ProjectionType: "ALL"
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
TableName: !Ref TableName
Tags:
- Key: ENV
Value: !Ref ENVName
DependsOn:
- "LambdaExecutionRole"
GetAccountLambdaFun:
Type: AWS::Lambda::Function
Properties:
FunctionName: GetAccountLambdaFun
Description: Retrive account From DB.
Role: !GetAtt LambdaExecutionRole.Arn
Handler: index.handler
MemorySize: 128
Runtime: !Ref LambdaRuntime
Environment:
Variables:
DB_END_POINT: !Ref LambdaDBEndPoint
TABLE_NAME: !Ref TableName
DB_API_VERSION: !Ref LambdaDBApiVersion
DB_AWS_REGION: !Ref "AWS::Region"
Code:
S3Bucket: !Ref LambdaHostS3BucketName
S3Key: !Sub
- "${Folder}/account-get-lambda-fun.zip"
- Folder: !Ref ENVName
Tags:
- Key: ENV
Value: !Ref ENVName
DependsOn:
- "DynamoDBTable"
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
RoleName: **LambdaExecutionRole**
Path: /
ManagedPolicyArns:
- !Ref LambdaLogGroupPolicy
- !Ref DynamoFullAccessPolicy
LambdaLogGroupPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: Lambda log groupt policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*
- Effect: Allow
Action:
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*:*
DynamoFullAccessPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description:dynamo full access policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Sid: "DynamoDBIndexAndStreamAccess"
Action:
- 'dynamodb:GetShardIterator'
- 'dynamodb:Scan'
- 'dynamodb:Query'
- 'dynamodb:DescribeStream'
- 'dynamodb:GetRecords'
- 'dynamodb:ListStream'
Resource:
- !Sub
- arn:aws:dynamodb:*:*:table/*/index/*
- tableName: !Ref TableName
- !Sub
- arn:aws:dynamodb:*:*:table/*/stream/*
- tableName: !Ref TableName
- Effect: Allow
Sid: "DynamoDBTableAccess"
Action:
- dynamodb:BatchGetItem'
- dynamodb:BatchWriteItem'
- dynamodb:ConditionCheckItem'
- dynamodb:PutItem'
- dynamodb:DescribeTable'
- dynamodb:DeleteItem'
- dynamodb:GetItem'
- dynamodb:Scan'
- dynamodb:Query'
- dynamodb:UpdateIte'
Resource:
- !Sub
- arn:aws:dynamodb:*:*:table/*
- tableName: !Ref TableName
- Effect: Allow
Sid: "DynamoDBDescribeLimitsAccess"
Action: 'dynamodb:DescribeLimits'
Resource:
- !Sub
- arn:aws:dynamodb:*:*:table/*
- tableName: !Ref TableName
- !Sub
- arn:aws:dynamodb:*:*:table/*/index/*
- tableName: !Ref TableName
when i try to test the lambda function via AWS console i get an error:
ERROR DynamoDB GET REQUEST failed AccessDeniedException: User:
arn:aws:sts::XXXX:assumed-role/LambdaExecutionRole/GetAccountLambdaFun
is not authorized to perform: dynamodb:Query on resource:
arn:aws:dynamodb:eu-central-1:XXXX:table/TableName
I am not sure why is that as i configure a DynamoFullAccessPolicy that should grant the lambda access to this DB.
Deployment of DynamoDB stack rollbacks with error:
CREATE_FAILED Encountered unsupported property AttributeType
I tried various modifications of attributes configuration, used quotes, also checked similar question.
My template:
Resources:
checkpointsTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: sf_instance_table_key
AttributeType: S
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: sf_instance_table_key
AttributeType: HASH
TableName: !Ref tableName
Tags:
- Key: "Division"
Value: !Ref division
Not many lines here, but cannot figure it out. Thanks for help!
It should be like this:
Resources:
checkpointsTable:
Type: AWS::DynamoDB::Table
Properties:
AttributeDefinitions:
- AttributeName: sf_instance_table_key
AttributeType: S
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: sf_instance_table_key
KeyType: HASH
TableName: !Ref tableName
Tags:
- Key: "Division"
Value: !Ref division
I am trying access my DynamoDB via a user that has all access to the table.
However I am failing to query the LSI for the same table. It says user doesn't have permissions to query index.
I checked the documentation and it shows that index needs to be define separately like arn:aws:dynamodb:region:account-id:table/table-name/index/index-name
However I am not sure how to define this in cloudformation yml file.
BooksTable:
Type: AWS::DynamoDB::Table
DeletionPolicy: Retain
Properties:
TableName:
Fn::Sub: ${SamStackPrefix}${Stage}-BooksTable
BillingMode: PAY_PER_REQUEST
KeySchema:
- AttributeName: hashKey
KeyType: HASH
- AttributeName: rangeKey
KeyType: RANGE
LocalSecondaryIndexes:
- IndexName: LSI1
KeySchema:
- AttributeName: hashKey
KeyType: HASH
- AttributeName: clientToken
KeyType: RANGE
Projection:
ProjectionType: ALL
AttributeDefinitions:
- AttributeName: hashKey
AttributeType: S
- AttributeName: rangeKey
AttributeType: S
- AttributeName: clientToken
AttributeType: S
StreamSpecification:
StreamViewType: NEW_AND_OLD_IMAGES
TimeToLiveSpecification:
AttributeName: expirationTime
Enabled: true
Outputs:
BooksTableName:
Description: books table.
Value:
!Ref BooksTable
Export:
Name:
Fn::Sub: ${SamStackPrefix}${Stage}-BooksTableName
BooksTableArn:
Description: Arn for books DynamoDB Table
Value:
Fn::GetAtt: [ BooksTable, Arn ]
Export:
Name:
Fn::Sub: ${SamStackPrefix}${Stage}-BooksTableArn
BooksTableStreamArn:
Description: The DDB stream for the books table.
Value:
Fn::GetAtt: [BooksTable, StreamArn]
Export:
Name:
Fn::Sub: ${SamStackPrefix}${Stage}-BooksStreamArn
IAM policy right now
Policies:
- PolicyDocument:
Statement:
- Action: ['dynamodb:PutItem', 'dynamodb:ConditionCheckItem', 'dynamodb:Query', 'dynamodb:GetItem', 'dynamodb:UpdateItem']
Effect: Allow
Resource:
- Fn::GetAtt: [BooksTable, Arn]
How to add LSI to the list of resources so that I can use that ARN to add permissions in the policy document.
Update IAM policy
this should work
Policies:
- PolicyDocument:
Statement:
- Action: ['dynamodb:PutItem', 'dynamodb:ConditionCheckItem', 'dynamodb:Query', 'dynamodb:GetItem', 'dynamodb:UpdateItem']
Effect: Allow
Resource:
- Fn::GetAtt: [BooksTable, Arn]
- "arn:aws:dynamodb:{region}:{account}:table/{tableName}/index/{indexName}"
I've seen this site about DynamoDB On-demand and I updated my tables, created by CloudFormation, to On-demand. Now, when I try to update my Stack, I get this error:
One or more parameter values were invalid: Neither ReadCapacityUnits nor WriteCapacityUnits can be specified when BillingMode is PAY_PER_REQUEST
Is there a way to set DynamoDB Read/write capacity mode to On-demand on CloudFormation?
EDIT:
I've updated to On-demand on AWS Console.
EDIT 2:
My template:
DynamoDBUsersTable:
Type: AWS::DynamoDB::Table
Description: Users table
Properties:
TableName: !Sub ${StackName}-users-table
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 10
WriteCapacityUnits: 10
Thank you.
You need to add BillingMode: PAY_PER_REQUEST to properties and remove ProvisionedThroughput both from table properties and from all GlobalSecondaryIndexes if they specified. So finally your template have to look like:
DynamoDBUsersTable:
Type: AWS::DynamoDB::Table
Description: Users table
Properties:
TableName: !Sub ${StackName}-users-table
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
I am writing the following for a db schema:
resources:
Resources:
DynamoDbTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: EmployeeType
AttributeDefinitions:
- AttributeName: timeoffgroupid
AttributeType: S
- AttributeName: timeOffGroup
AttributeType: S
- AttributeName: timeOffGroupColor
AttributeType: S
KeySchema:
- AttributeName: timeoffgroupid
KeyType: HASH
- AttributeName: timeOffGroup
KeyType: HASH
- AttributeName: timeOffGroupColor
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
DynamoDBIamPolicy:
Type: AWS::IAM::Policy
DependsOn: DynamoDbTable
Properties:
PolicyName: lambda-dynamodb
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: arn:aws:dynamodb:*:*:table/moviesTwo
Roles:
- Ref: IamRoleLambdaExecution
But I copied that from a few tutorials and figured out how to post to a db, but I am not sure what all the options are. For example, KeyType: HASH - what are the other options, what do they do? I saw one tutorial tell me this creates a table and sets up the properties with required keys, but I am not sure how to call one primary or how to reference these Attributes and keyTypes. I understand the S is for String, but I am not sure what the other Properties do? I have looked in the documentation but came up empty.
Thanks in advance.
The resources schema used by serverless.yml is the CloudFormation schema. For DynamoDB take a look here.
To understand DynamoDB concepts and terms I'd suggest to start here:
Intro
Core components
Sample