I have a DynamoDB table and want to create a new GSI using CFN. But the CFN fails and throwing error message that the table already exists.
Tried creating GSI using on existing table, it worked fine.
Is it not possible to create GSI on an existing using CFN?
`AWSTemplateFormatVersion: "2010-09-09" # This is latest as of Oct 2023
Transform: [AWS::Serverless-2016-10-31]
Description: Mira Actions Service
Metadata:
cfn-lint:
# Configure the template linter (sam validate --lint uses cfn-lint)
config:
ignore_checks:
# W3002 is the warning: "This code may only work with package cli command as the property"
# It shows up for AWS::Serverless::Application resources below and is fine to ignore
- W3002
# W2001 is the warning: "UserPoolId not used". However we are using it in the included
# api.yaml file, so we can ignore this warning.
- W2001
Globals:
Doesn't seem like globals bubble down to nested stacks so defining them in each nested stack
Function:
Timeout: 60
Parameters:
MicroserviceClientId:
Description: The Client ID for the Microservice App Client
Type: String
Environment:
Description: The environment name (e.g., prod, dev, staging, gh ref, your user name).
Type: String
Default: dev
UserPoolId:
Description: The Cognito User Pool ID
Type: String
RootStackName:
Description: The root stack name used for cross-stack references.
Type: String
ApiVersion:
Type: String
Description: The version of the API
Default: v1 # NOTE This sets the API version which is reflected in the API URL.
UtilityLayerArn:
Description: The ARN of the Mira Utilities Layer
Type: "AWS::SSM::Parameter::Value<String>"
DataPlatformEventsStreamArn:
Description: The ARN of the Data Platform Kinesis Events Stream
Type: String
ApplicationLogLevel:
Description: Application logs are the logs that python function code generates.
Type: String
SystemLogLevel:
Description: System logs are the logs that Lambda generates.
Type: String
ConfigBucketName:
Description: The name of the S3 bucket to store the OpenTelemetry Collector configuration file.
Type: String
SignalPolicyTableArn:
Description: The ARN of the signal policy table
Type: String
MiraNotificationsApi:
Type: String
Description: The Mira Notifications API Gateway endpoint URL
MiraSignalPolicyTable:
Description: The name of the signal policy table
Type: String
MiraAgentClientTable:
Description: The DynamoDB table for storing Mira Client Agent Xref Data
Type: String
MiraAgentClientTableArn:
Description: The ARN of the Mira Client Agent Xref Data table
Type: String
ActionsTableName:
Description: The name of the actions table
Type: String
ActionsGSIName:
Description: The name of GSI
Type: String
Conditions:
IsProductionEnvironment: !Equals [!Ref Environment, "Production"]
IsTestingStagingProduction: !Or
- !Equals [!Ref Environment, "Testing"]
- !Equals [!Ref Environment, "Staging"]
- !Equals [!Ref Environment, "Production"]
Mappings:
OtelConfigServiceMap:
Testing:
"ServiceName": "actions-testing"
Staging:
"ServiceName": "actions-staging"
Production:
"ServiceName": "actions-production"
Development:
"ServiceName": "no-op"
Resources:
MiraServiceActionProcessRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: "/"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CognitoDescribePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- cognito-idp:DescribeUserPool
- cognito-idp:DescribeUserPoolClient
Resource: !Sub arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${UserPoolId}
- PolicyName: DynamoDBMiraActionTablePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "dynamodb:DescribeStream"
- "dynamodb:GetRecords"
- "dynamodb:GetShardIterator"
- "dynamodb:ListStreams"
- "dynamodb:GetItem"
- "dynamodb:Query"
Resource:
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${MiraActionsTable}
- !Sub arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${MiraActionsTable}/stream/*
- PolicyName: LambdaCloudWatchPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "*"
- PolicyName: LambdaInvokePolicies
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: lambda:InvokeFunction
Resource:
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${MiraAWSActions.Outputs.MiraAwsActionProcessFunctionName}
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${MiraSatelliteActions.Outputs.MiraSatelliteActionsServerPowerOffFunctionName}
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${MiraAzureActions.Outputs.MiraAzureActionsDetachedIpFunctionName}
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${MiraAzureActions.Outputs.MiraAzureActionsServerFunctionName}
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${MiraAzureActions.Outputs.MiraAzureActionsTagFunctionName}
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${MiraGCPActions.Outputs.MiraGCPActionsTagFunctionName}
- PolicyName: SQSSendMessagePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: sqs:SendMessage
Resource: !GetAtt MiraActionsProcessorDeadLetterQueue.Arn
- PolicyName: SSMReadPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
Resource: !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${RootStackName}/internal/signals/ServiceSignalTableArn
- PolicyName: SelfManagePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- iam:PutRolePolicy
- iam:ListRolePolicies
Resource: "*"
MiraActionsApi:
Type: AWS::Serverless::Api
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html
Properties:
MinimumCompressionSize: 1024
EndpointConfiguration:
# Use REGIONAL for production and EDGE for testing and staging because of AWS Quotas per Account for EDGE
Type: !If [IsTestingStagingProduction, "EDGE", "REGIONAL"]
# Using the StageName as a versioning mechanism
StageName: !Ref ApiVersion
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: ./api.yaml
MiraActionsLayer:
Type: AWS::Serverless::LayerVersion
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-layerversion.html
Properties:
Description: Shared code for the Actions service
ContentUri: layers/
Metadata:
BuildMethod: python3.11
Stores events handled by the EventProcessor in DynamoDB
MiraActionsTable:
Type: AWS::DynamoDB::Table
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
Properties:
TableName: !Ref ActionsTableName
BillingMode: PAY_PER_REQUEST
# Enable Point-in-time Recovery (Backup)
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
AttributeDefinitions: # These properties must be defined in order to use as sort keys in the primary and secondary key schemas
- AttributeName: action_id
AttributeType: S
- AttributeName: tenant_id
AttributeType: S
- AttributeName: status
AttributeType: S
- AttributeName: provider
AttributeType: S
- AttributeName: action
AttributeType: S
- AttributeName: process_type
AttributeType: S
#used in GSI
- AttributeName: policy_id
AttributeType: S
DeletionProtectionEnabled: !If [IsProductionEnvironment, "true", "false"]
KeySchema: #https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-keyschema.html
- AttributeName: tenant_id
KeyType: HASH # Partition key
- AttributeName: action_id
KeyType: RANGE # Sort Key
# As the table does not have a sort key on policy, we need the GSI.
# This GSI is used only with Action Read API we included only those attributes specific to the API
GlobalSecondaryIndexes:
- IndexName: !Ref ActionGSIName
KeySchema:
- AttributeName: tenant_id # Partition Key
KeyType: HASH
- AttributeName: policy_id # Sort Key
KeyType: RANGE
Projection:
ProjectionType: INCLUDE
NonKeyAttributes:
- action
- created_at
- resource_ids
- status
- policy_run_id
- result
- provider
- event_timestamp
# The following Local Secondary Indexes allow sorting on attributes other than the action_id.
# https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html
LocalSecondaryIndexes:
- IndexName: tenant_id-status-index
KeySchema:
- AttributeName: tenant_id
KeyType: HASH
- AttributeName: status
KeyType: RANGE
Projection:
ProjectionType: ALL
- IndexName: tenant_id-provider-index
KeySchema:
- AttributeName: tenant_id
KeyType: HASH
- AttributeName: provider
KeyType: RANGE
Projection:
ProjectionType: ALL
- IndexName: tenant_id-action-index
KeySchema:
- AttributeName: tenant_id
KeyType: HASH
- AttributeName: action
KeyType: RANGE
Projection:
ProjectionType: ALL
- IndexName: tenant_id-process_type-index
KeySchema:
- AttributeName: tenant_id
KeyType: HASH
- AttributeName: process_type
KeyType: RANGE
Projection:
ProjectionType: ALL
StreamSpecification:
StreamViewType: NEW_IMAGE
KinesisStreamSpecification:
StreamArn: !Ref DataPlatformEventsStreamArn
MiraActionOptionsTable:
Type: AWS::DynamoDB::Table
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
Properties:
BillingMode: PAY_PER_REQUEST
# Enable Point-in-time Recovery (Backup)
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
AttributeDefinitions: # These properties must be defined in order to use as sort keys in the primary and secondary key schemas
- AttributeName: id
AttributeType: S
- AttributeName: resource
AttributeType: S
- AttributeName: action
AttributeType: S
- AttributeName: provider
AttributeType: S
DeletionProtectionEnabled: !If [IsProductionEnvironment, "true", "false"]
KeySchema: #https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-keyschema.html
- AttributeName: id
KeyType: HASH # Partition key
- AttributeName: resource
KeyType: RANGE # Sort Key
# The following Local Secondary Indexes allow sorting on attributes other than the action_id.
# https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html
LocalSecondaryIndexes:
- IndexName: sort-by-action
KeySchema:
- AttributeName: id
KeyType: HASH
- AttributeName: action
KeyType: RANGE
Projection:
ProjectionType: ALL
- IndexName: sort-by-provider
KeySchema:
- AttributeName: id
KeyType: HASH
- AttributeName: provider
KeyType: RANGE
Projection:
ProjectionType: ALL
Lambda function to create a new action via POST /actions endpoint
MiraActionsCreate:
Type: AWS::Serverless::Function
Properties:
CodeUri: actions_create/
Handler: app.handler
Runtime: python3.11
Layers:
- !Ref MiraActionsLayer
- !Ref UtilityLayerArn
- !Sub "arn:aws:lambda:${AWS::Region}:901920570463:layer:aws-otel-python-amd64-ver-1-21-0:1"
Environment:
Variables:
MIRA_ACTIONS_TABLE: !Ref MiraActionsTable
AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-instrument
OPENTELEMETRY_COLLECTOR_CONFIG_FILE: !Sub s3://${ConfigBucketName}.s3.${AWS::Region}.amazonaws.com/otel-collector-config.yaml
HONEYCOMB_API_KEY: "{{resolve:secretsmanager:honeycomb_api_key:SecretString:HONEYCOMB_API_KEY}}"
OTEL_SERVICE_NAME: !FindInMap [OtelConfigServiceMap, !Ref Environment, ServiceName]
OTEL_PROPAGATORS: tracecontext
OTEL_ENDPOINT: 'https://api.honeycomb.io:443'
# Advanced logging controls for AWS Lambda
LoggingConfig:
LogFormat: JSON # Application logs format, defaults to JSON (TEXT is optional)
ApplicationLogLevel: !Ref ApplicationLogLevel # Application log level, defaults to INFO
SystemLogLevel: !Ref SystemLogLevel # System log level, defaults to INFO
Connectors:
DatabaseConnector:
Properties:
Destination:
Id: MiraActionsTable
Permi`