5

We are trying create a AWS::WAFv2::IPSet in our SAM template.

WhitelistedIPAddressesIPSet:
    Type: AWS::WAFv2::IPSet
    Properties:
        Description: 'Merchant IPs'
        Scope: REGIONAL
        IPAddressVersion: IPV4
        Addresses: [0.0.0.0/32, 0.0.10.0/32]

The creation of the IP sets is done successfully. Once creating the AWS::WAFv2::WebACLAssociation.

WAFApiAssociation:
    Type: AWS::WAFv2::WebACLAssociation
    DependsOn:
        - ApiGateway
        - WAFWebAcl
    Properties:
        ResourceArn: !Sub 'arn:aws:apigateway:${AWS::Region}::/restapis/${ApiGateway}/stages/${EnvType}'
        WebACLArn: !GetAtt WAFWebAcl.Arn

The CloudFormation failes and does a rollback. Error displayed is as follows:

Resource handler returned
ion message: "AWS WAF couldn?t
perform the operation
because your resource
doesn?t exist. (Service:
Wafv2, Status Code: 400,
Request ID: e337720a-e32c-
4c29-acde-1896855405c9,
Extended Request ID:
null)" (RequestToken: f24d
0488-3016-4030-3a3b-bbb246
66f130, HandlerErrorCode:
NotFound)

We tried different formatting the SAM template of the IP set, to see if that causes the issues, without any success.

Anyone that could share some helpful insights to this issue?

Pontus Espe
  • 51
  • 1
  • 2

2 Answers2

5

A) You don't need DependsOn if your resource already directly depends on those other resources. In this case it does, so you can remove this property.

B) You'll need to share your whole stack here, not just what is shared because there is likely a problem with your APIGW configuration. Because that failed to be created, it's possible you get this subsequent problem showing up.

Creating the APIGW isn't enough, you need to make sure to actually attach the WAF after the APIGW stage was created and not just the APIGW. In this case replace the ResourceArn with one that references the APIGW Stage. (And further you might need to wait for the stage deployment to finish.)

Example CFN for association:

WAFv2Assocation:
  Type: AWS::WAFv2::WebACLAssociation
  Properties:
    ResourceArn: !Sub "arn:aws:apigateway:${AWS::Region}::/restapis/${ApiGatewayApi}/stages/${Stage}"
    Stage: !Ref ApiGatewayApi.Stage
Warren Parad
  • 3,910
  • 1
  • 20
  • 29
  • 4
    This is the right answer. I did this by referencing the stage as suggested. Thus, creating the implicit dependency. Like this snippet from AWS::WAFv2::WebACLAssociation: `ResourceArn: !Sub - "arn:aws:apigateway:${AWS::Region}::/restapis/${ApiGatewayApi}/stages/${Stage}" - Stage: !Ref ApiGatewayApi.Stage` Hopefully this helps someone in the future. – Rob Davis Jan 26 '22 at 01:06
  • 1
    For me changing from `resource_arn=f"arn:aws:apigateway:{region}::/restapis/{self.api_gateway.rest_api_id}/stages/{stage_name}"` To `resource_arn=self.api_gateway.deployment_stage.stage_arn` force cloud formation to deploy the WAF after the deployment of the API Gateway stage – Ari Oct 06 '22 at 19:58
  • 1
    @Ari Thanks! You are referring to the `CDK`. This is the perfect answer for doing this in the `CDK`! I don't know why all the guides online say to do the handwritten way using string interpolation, when you can just use the straightforward built in `resource_arn=self.api_gateway.deployment_stage.stage_arn`. Thanks for this. – Avramo Oct 06 '22 at 20:16
  • Based on @RobDavis comment, I ended up with something like this (it's messy in a comment, but you can paste this into a yaml file and format it): `WAFv2Assocation: Type: AWS::WAFv2::WebACLAssociation Properties: ResourceArn: !Join - '' - - !Sub "arn:aws:apigateway:${AWS::Region}::/restapis/" - !Ref MyApi - "/stages/" - !Ref MyApi.Stage` – mojave Feb 20 '23 at 22:38
0

This is the APIGW template Warren Parad

CDEAPI:
    Type: AWS::Serverless::Api
    Properties:
        # Domain:
        #     DomainName: !Ref CDEAPIDomainName
        #     SecurityPolicy: TLS_1_2
        #     CertificateArn: !Sub 'arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${CDEAPICertificateArn}'
        #     EndpointConfiguration: EDGE
        #     Route53:
        #         HostedZoneId: !Ref CDEAPIHostedZoneId
        AccessLogSetting:
            DestinationArn: !GetAtt CDEAPIAccessLogGroup.Arn
            Format: >-
                { "requestId":"$context.requestId",
                "ip":"$context.identity.sourceIp",
                "caller":"$context.identity.caller",
                "user":"$context.identity.user",
                "userAgent":"$context.identity.userAgent",
                "userArn":"$context.identity.userArn",
                "requestTime":"$context.requestTime",
                "requestTimeEpoch":"$context.requestTimeEpoch",
                "httpMethod":"$context.httpMethod",
                "resourcePath":"$context.resourcePath",
                "path":"$context.path",
                "status":"$context.status",
                "protocol":"$context.protocol",
                "responseLength":"$context.responseLength",
                "responseLatency":"$context.responseLatency",
                "authorizerLatency":"$context.authorizer.integrationLatency",
                "integrationLatency":"$context.integrationLatency",
                "integrationStatus":"$context.integrationStatus",
                "xrayTraceId":"$context.xrayTraceId",
                "errorMessage":"$context.error.message",
                "domainName":"$context.domainName",
                "domainPrefix":"$context.domainPrefix",
                "tokenScopes":"$context.authorizer.claims.scope",
                "tokenIat":"$context.authorizer.claims.iat",
                "tokenExp":"$context.authorizer.claims.exp",
                "cognitoIdentityId":"$context.identity.cognitoIdentityId",
                "awsEndpointRequestId":"$context.awsEndpointRequestId",
                "arn":"$context.identity.userArn",
                "account":"$context.identity.accountId",
                "claims-sub":"$context.authorizer.claims.sub",
                "waf-error":"$context.waf.error",
                "waf-status":"$context.waf.status",
                "waf-latency":"$context.waf.latency",
                "waf-response":"$context.waf.wafResponseCode",
                "authenticate-error":"$context.authenticate.error",
                "authenticate-status":"$context.authenticate.status",
                "authenticate-latency":"$context.authenticate.latency",
                "integration-error":"$context.integration.error",
                "integration-status":"$context.integration.status",
                "integration-latency":"$context.integration.latency",
                "integration-requestId":"$context.integration.requestId",
                "integration-integrationStatus":"$context.integration.integrationStatus",
                "response-latency":"$context.responseLatency" }
        StageName: !Ref EnvType
        Auth:
            DefaultAuthorizer: CognitoAuthorizer
            AddDefaultAuthorizerToCorsPreflight: false
            Authorizers:
                CognitoAuthorizer:
                    AuthType: COGNITO_USER_POOLS
                    UserPoolArn: !Sub 'arn:aws:cognito-idp:${AWS::Region}:${AWS::AccountId}:userpool/${CognitoUserPoolArn}'
Pontus Espe
  • 51
  • 1
  • 2