cloudformation-cli icon indicating copy to clipboard operation
cloudformation-cli copied to clipboard

readOnlyProperties not being saved

Open danieljarrett74 opened this issue 3 years ago • 0 comments

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:*"            ]
        }
    }
}

danieljarrett74 avatar Oct 12 '21 03:10 danieljarrett74