cloudformation-cli
cloudformation-cli copied to clipboard
readOnlyProperties not being saved
I have a few readOnlyProperties that aren't being set or returned when performing a delete.
My create handler looks as follows and I'm outputting the progress event before returning it:
@resource.handler(Action.CREATE)
def create_handler(
session: Optional[SessionProxy],
request: ResourceHandlerRequest,
callback_context: MutableMapping[str, Any],
) -> ProgressEvent:
model = request.desiredResourceState
awsAccountId = request.awsAccountId
region = request.region
logicalResourceIdentifier = request.logicalResourceIdentifier
LOG.debug("CREATE")
LOG.debug(callback_context)
LOG.debug(request.desiredResourceState)
try:
if isinstance(session, SessionProxy):
progress = handle_create(
aws_account_id=awsAccountId,
region=region,
agw_client=session.client("apigateway"),
lambda_client=session.client("lambda"),
dynamodb_resource=session.resource("dynamodb"),
iam_client=session.client("iam"),
model=model,
callback_context=callback_context
)
LOG.debug(progress)
return progress
else:
LOG.error("No session available")
raise TypeError
except TypeError as e:
raise exceptions.InternalFailure(f"was not expecting type {e}")
And in my logs I can see that I am setting my readOnlyProperties. EG ApiId
ProgressEvent(status=<OperationStatus.SUCCESS: 'SUCCESS'>, errorCode=None, message='', callbackContext={}, callbackDelaySeconds=0, resourceModel=ResourceModel(DefaultSender=DefaultSender(EmailAddress='[email protected]', Name=None), SenderMode='MULTIPLE', ApiId='is6k89ctyh', Templates=None, CognitoTemplates=None, Endpoint='https://1234567.execute-api.us-east-1.amazonaws.com/test', CognitoCustomMessageArn='arn:aws:lambda:us-east-1:1234567890:function:CustomMessagesLambda', Tags=None), resourceModels=None, nextToken=None)
When I DELETE the stack containing the resource I get an error because ApiId is set to None. Somewhere along the way it's being forgotten.
@resource.handler(Action.DELETE)
def delete_handler(
session: Optional[SessionProxy],
request: ResourceHandlerRequest,
callback_context: MutableMapping[str, Any],
) -> ProgressEvent:
model = request.previousResourceState
awsAccountId = request.awsAccountId
region = request.region
logicalResourceIdentifier = request.logicalResourceIdentifier
LOG.debug("DELETE")
LOG.debug(request)
try:
if isinstance(session, SessionProxy):
progress = handle_delete(
aws_account_id=awsAccountId,
region=region,
agw_client=session.client("apigateway"),
lambda_client=session.client("lambda"),
dynamodb_client=session.client("dynamodb"),
iam_client=session.client("iam"),
model=model,
callback_context=callback_context
)
return progress
else:
LOG.error("No session available")
raise TypeError
except TypeError as e:
raise exceptions.InternalFailure(f"was not expecting type {e}")
As you can see above I'm logging the entire request, which you can see here:
BaseResourceHandlerRequest(clientRequestToken='e3aa83a2-9bc8-4a35-d8b2-1234567890', desiredResourceState=ResourceModel(DefaultSender=DefaultSender(EmailAddress='[email protected]', Name=None), SenderMode='MULTIPLE', ApiId=None, Templates=None, CognitoTemplates=None, Endpoint='https://1234567.execute-api.us-east-1.amazonaws.com/test', CognitoCustomMessageArn=None, Tags=None), previousResourceState=None, desiredResourceTags=None, previousResourceTags=None, systemTags=None, previousSystemTags=None, awsAccountId='1234567890', logicalResourceIdentifier='NotificationsApi', typeConfiguration=None, nextToken=None, region='us-east-1', awsPartition='aws', stackId='arn:aws:cloudformation:us-east-1:1234567890:stack/drj-notifications-api-test/5ddc1250-2b04-11ec-9b2e-1234567890')
My Configuration JSON file looks as follows:
{
"typeName": "DRJ::Notifications::Api",
"description": "DRJ Notifications Api Resource",
"sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-rpdk.git",
"definitions": {
"DefaultSender": {
"type": "object",
"properties": {
"EmailAddress": {
"type": "string"
},
"Name": {
"type": "string"
}
},
"required": [
"EmailAddress"
],
"additionalProperties": false
},
"Template": {
"type": "object",
"properties": {
"TemplateId": {
"type": "string"
},
"TemplateDescription": {
"type": "string"
},
"Subject": {
"type": "string"
},
"HtmlPart": {
"type": "string"
},
"TextPart": {
"type": "string"
}
},
"required": [
"Subject",
"HtmlPart",
"TextPart",
"TemplateId"
],
"additionalProperties": false
},
"CognitoTemplate": {
"type": "object",
"properties": {
"Subject": {
"type": "string"
},
"HtmlPart": {
"type": "string"
},
"TextPart": {
"type": "string"
}
},
"required": [
"Subject",
"HtmlPart",
"TextPart"
],
"additionalProperties": false
},
"CognitoTemplates": {
"type": "object",
"properties": {
"SignUp": {
"$ref": "#/definitions/CognitoTemplate"
},
"AdminCreateUser": {
"$ref": "#/definitions/CognitoTemplate"
},
"ResendCode": {
"$ref": "#/definitions/CognitoTemplate"
},
"ForgotPassword": {
"$ref": "#/definitions/CognitoTemplate"
},
"UpdateUserAttribute": {
"$ref": "#/definitions/CognitoTemplate"
},
"VerifyUserAttribute": {
"$ref": "#/definitions/CognitoTemplate"
}
},
"required": [],
"additionalProperties": false
},
"Tag": {
"description": "A key-value pair to associate with a resource.",
"type": "object",
"properties": {
"Key": {
"type": "string",
"description": "The key name of the tag. You can specify a value that is 1 to 128 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -.",
"minLength": 1,
"maxLength": 128
},
"Value": {
"type": "string",
"description": "The value for the tag. You can specify a value that is 0 to 256 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -.",
"minLength": 0,
"maxLength": 256
}
},
"required": [
"Key",
"Value"
],
"additionalProperties": false
}
},
"properties": {
"DefaultSender" : {
"$ref": "#/definitions/DefaultSender"
},
"SenderMode": {
"type": "string",
"enum": [
"MULTIPLE",
"SINGLE"
]
},
"ApiId" : {
"type": "string"
},
"Templates": {
"type": "array",
"items": {
"$ref": "#/definitions/Template"
}
},
"CognitoTemplates": {
"$ref": "#/definitions/CognitoTemplates"
},
"Endpoint": {
"type": "string"
},
"CognitoCustomMessageArn": {
"type": "string"
},
"Tags": {
"description": "An array of key-value pairs to apply to this resource.",
"type": "array",
"uniqueItems": true,
"insertionOrder": false,
"items": {
"$ref": "#/definitions/Tag"
}
}
},
"additionalProperties": false,
"required": [
"DefaultSender"
],
"readOnlyProperties": [
"/properties/Endpoint",
"/properties/ApiId",
"/properties/CognitoCustomMessageArn"
],
"primaryIdentifier": [
"/properties/Endpoint"
],
"handlers": {
"create": {
"permissions": [
"iam:*",
"dynamodb:*",
"apigateway:*",
"lambda:*"
]
},
"read": {
"permissions": [
"iam:*",
"dynamodb:*",
"apigateway:*",
"lambda:*" ]
},
"update": {
"permissions": [
"iam:*",
"dynamodb:*",
"apigateway:*",
"lambda:*" ]
},
"delete": {
"permissions": [
"iam:*",
"dynamodb:*",
"apigateway:*",
"lambda:*" ]
},
"list": {
"permissions": [
"iam:*",
"dynamodb:*",
"apigateway:*",
"lambda:*" ]
}
}
}