С # aws api gateway serverless.template структура не обрабатывается через CloudFormation

Я создал бессерверное приложение .NET и у меня есть следующий serverless.template

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Transform": "AWS::Serverless-2016-10-31",
    "Description": "API Gateway to access InSite data-store.",
    "Resources": {
        "Get": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "VpcConfig": {
                    "SecurityGroupIds": [
                        "sg-111a1476"
                    ],
                    "SubnetIds": [
                        "subnet-3029a769",
                        "subnet-5ec0b928"
                    ]
                },
                "Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::Get",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "",
                "MemorySize": 256,
                "Timeout": 30,
                "Role": null,
                "Policies": [
                    "AWSLambdaBasicExecutionRole",
                    "AWSLambdaVPCAccessExecutionRole",
                    "AmazonSSMFullAccess"
                ],
                "Environment": {
                    "Variables":{
                        "LOG_LEVEL":"2",
                        "CONNECTION_STRING":"insite-api-connectionstring"
                    }
                },
                "Events": {
                    "PutResource": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/",
                            "Method": "GET"
                        }
                    }
                }
            }
        },
        "GetTableBasic": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "VpcConfig": {
                    "SecurityGroupIds": [
                        "sg-111a1476"
                    ],
                    "SubnetIds": [
                        "subnet-3029a769",
                        "subnet-5ec0b928"
                    ]
                },
                "Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::GetTableBasic",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "",
                "MemorySize": 256,
                "Timeout": 30,
                "Role": null,
                "Policies": [
                    "AWSLambdaBasicExecutionRole",
                    "AWSLambdaVPCAccessExecutionRole",
                    "AmazonSSMFullAccess"
                ],
                "Environment": {
                    "Variables":{
                        "LOG_LEVEL":"2",
                        "CONNECTION_STRING":"insite-api-connectionstring"
                    }
                },
                "Events": {
                    "PutResource": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/tables/{tableid}/{columnid}",
                            "Method": "GET"
                        }
                    }
                }
            }
        },
        "PostClickCollectNotification": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "VpcConfig": {
                    "SecurityGroupIds": [
                        "sg-111a1476"
                    ],
                    "SubnetIds": [
                        "subnet-3029a769",
                        "subnet-5ec0b928"
                    ]
                },
                "Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::PostClickCollectNotification",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "",
                "MemorySize": 256,
                "Timeout": 30,
                "Role": null,
                "Policies": [
                    "AWSLambdaBasicExecutionRole",
                    "AWSLambdaVPCAccessExecutionRole",
                    "AmazonSSMFullAccess"
                ],
                "Environment": {
                    "Variables":{
                        "LOG_LEVEL":"2",
                        "CONNECTION_STRING":"insite-api-connectionstring"
                    }
                },
                "Events": {
                    "PutResource": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/order/notify",
                            "Method": "POST"
                        }
                    }
                }
            }
        },
        "PostClickCollectStockUpdate": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "VpcConfig": {
                    "SecurityGroupIds": [
                        "sg-111a1476"
                    ],
                    "SubnetIds": [
                        "subnet-3029a769",
                        "subnet-5ec0b928"
                    ]
                },
                "Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::PostClickCollectStockUpdate",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "",
                "MemorySize": 256,
                "Timeout": 30,
                "Role": null,
                "Policies": [
                    "AWSLambdaBasicExecutionRole",
                    "AWSLambdaVPCAccessExecutionRole",
                    "AmazonSSMFullAccess"
                ],
                "Environment": {
                    "Variables":{
                        "LOG_LEVEL":"2",
                        "TARGET_TABLE":"InSiteClickCollect",
                        "CONNECTION_STRING":"insite-api-connectionstring"
                    }
                },
                "Events": {
                    "PutResource": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "/order/stock/update/",
                            "Method": "POST"
                        }
                    }
                }
            }
        },
        "GetTableResponse": {
            "Type": "AWS::Serverless::Function",
            "Properties": {
                "VpcConfig": {
                    "SecurityGroupIds": [
                        "sg-111a1476"
                    ],
                    "SubnetIds": [
                        "subnet-3029a769",
                        "subnet-5ec0b928"
                    ]
                },
                "Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::GetTableResponse",
                "Runtime": "dotnetcore2.1",
                "CodeUri": "",
                "MemorySize": 256,
                "Timeout": 30,
                "Role": null,
                "Policies": [
                    "AWSLambdaBasicExecutionRole",
                    "AWSLambdaVPCAccessExecutionRole",
                    "AmazonSSMFullAccess"
                ],
                "Environment": {
                    "Variables":{
                        "LOG_LEVEL":"2",
                        "CONNECTION_STRING":"insite-api-connectionstring"
                    }
                },
                "Events": {
                    "PutResource": {
                        "Type": "Api",
                        "Properties": {
                            "Path": "tables/query/{tableid}",
                            "Method": "GET"
                        }
                    }
                }
            }
        },
        "ServerlessRestApi": {
            "Type": "AWS::ApiGateway::RestApi",
            "Properties": {
                "Description":"InSite Web API Version 2.0.0.0",
                "Body": {
                    "swagger": "2.0",
                    "info": {
                        "version": "1.0",
                        "title": {
                            "Ref": "AWS::StackName"
                        }
                    },
                    "x-amazon-apigateway-api-key-source" : "HEADER",
                    "schemes":["https"],
                    "paths": {
                        "tables/query/{tableid}": {
                            "get": {
                                "x-amazon-apigateway-integration": {
                                    "httpMethod": "GET",
                                    "type": "aws_proxy",
                                    "uri": {
                                        "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableResponse.Arn}/invocations"
                                    }
                                },
                                "responses": {},
                                    "security": [
                                    {
                                        "api_key": []
                                    }
                                ]
                            }
                        },
                        "/products/update/": {
                            "post": {
                                "x-amazon-apigateway-integration": {
                                    "httpMethod": "POST",
                                    "type": "aws_proxy",
                                    "uri": {
                                        "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostClickCollectStockUpdate.Arn}/invocations"
                                    }
                                },
                                "responses": {}
                            }
                        },
                        "/": {
                            "get": {
                                "x-amazon-apigateway-integration": {
                                    "httpMethod": "GET",
                                    "type": "aws_proxy",
                                    "uri": {
                                        "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Get.Arn}/invocations"
                                    }
                                },
                                "responses": {}
                            }
                        },
                        "/tables/{tableid}/{columnid}": {
                            "get": {
                                "x-amazon-apigateway-integration": {
                                    "httpMethod": "GET",
                                    "type": "aws_proxy",
                                    "uri": {
                                        "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableBasic.Arn}/invocations"
                                    }
                                },
                                "responses": {}
                            }
                        }
                    },
                    "securityDefinitions": {
                        "api_key": {
                            "type": "apiKey",
                            "name": "x-api-key",
                            "in": "header"
                        }
                    }
                }
            }
        },
        "InternalUsagePlan": {
            "Type": "AWS::ApiGateway::UsagePlan",
            "Properties": {
                "ApiStages": [
                    {
                        "ApiId": {
                            "Ref": "ServerlessRestApi"
                        },
                        "Stage": {
                            "Ref": "ServerlessRestApiProdStage"
                        }
                    }
                ],
                "Description": "Internal Apps Usage Plan",
                "UsagePlanName": "Insite-datagw-InternalAppPlan-new"
            }
        },
        "ExternalUsagePlan": {
            "Type": "AWS::ApiGateway::UsagePlan",
            "Properties": {
                "ApiStages": [
                    {
                        "ApiId": {
                            "Ref": "ServerlessRestApi"
                        },
                        "Stage": {
                            "Ref": "ServerlessRestApiProdStage"
                        }
                    }
                ],
                "Description": "External Apps Usage Plan",
                "UsagePlanName": "InSite-datagw-ExternalAppPlan-new"
            }
        },
        "KeyHeartInHand": {
            "Type": "AWS::ApiGateway::ApiKey",
            "DependsOn": [
                "ServerlessRestApi",
                "ServerlessRestApiProdStage"
            ],
            "Properties": {
                "Name": "apikeyHeartInHand-new",
                "Description": "Api Key for Heart In Hand",
                "Enabled": true,
                "GenerateDistinctId": true,
                "StageKeys": [
                    {
                        "RestApiId": {
                            "Ref": "ServerlessRestApi"
                        },
                        "StageName": {
                            "Ref": "ServerlessRestApiProdStage"
                        }
                    }
                ]
            }
        },
        "LinkHeartInHandKey": { 
            "Type": "AWS::ApiGateway::UsagePlanKey",
            "Properties": {
                "KeyId": {
                    "Ref": "KeyHeartInHand"
                },
                "KeyType": "API_KEY",
                "UsagePlanId": {
                    "Ref": "InternalUsagePlan"
                }
            }
        },
        "KeyUmbraco": {
            "Type": "AWS::ApiGateway::ApiKey",
            "DependsOn": [
                "ServerlessRestApi",
                "ServerlessRestApiProdStage"
            ],
            "Properties": {
                "Name": "KeyUmbraco-new",
                "Description": "Api Key for Umbraco Website",
                "Enabled": true,
                "GenerateDistinctId": true,
                "StageKeys": [
                    {
                        "RestApiId": {
                            "Ref": "ServerlessRestApi"
                        },
                        "StageName": {
                            "Ref": "ServerlessRestApiProdStage"
                        }
                    }
                ]
            }
        },
        "LinkPricelineSiteKey": {
            "Type": "AWS::ApiGateway::UsagePlanKey",
            "Properties": {
                "KeyId": {
                    "Ref": "KeyUmbraco"
                },
                "KeyType": "API_KEY",
                "UsagePlanId": {
                    "Ref": "InternalUsagePlan"
                }
            }
        },
        "KeyPenTest": {
            "Type": "AWS::ApiGateway::ApiKey",
            "DependsOn": [
                "ServerlessRestApi",
                "ServerlessRestApiProdStage"
            ],
            "Properties": {
                "Name": "apiKeyPenTesting-new",
                "Description": "Api Key for pen testing",
                "Enabled": true,
                "GenerateDistinctId": true,
                "StageKeys": [
                    {
                        "RestApiId": {
                            "Ref": "ServerlessRestApi"
                        },
                        "StageName": {
                            "Ref": "ServerlessRestApiProdStage"
                        }
                    }
                ]
            }
        },
        "LinkPenTestKey": {
            "Type": "AWS::ApiGateway::UsagePlanKey",
            "Properties": {
                "KeyId": {
                    "Ref": "KeyPenTest"
                },
                "KeyType": "API_KEY",
                "UsagePlanId": {
                    "Ref": "InternalUsagePlan"
                }
            }
        }
    },
    "Outputs": {
        "ApiURL": {
            "Description": "API endpoint URL for Prod environment",
            "Value": {
                "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
            }
        }
    }
}

Приложение развертывается нормально через AWS Toolkit для Visual Studio, но в обработанной облачной информации, похоже, не хватает большинства, если не всех обновлений, которые я внес в раздел AWS :: ApiGateway :: RestApi. Когда я проверяю консоль AWS, обработанный шаблон CF выглядит следующим образом:

   "ServerlessRestApi": {
      "Type": "AWS::ApiGateway::RestApi",
      "Properties": {
        "Body": {
          "info": {
            "version": "1.0",
            "title": {
              "Ref": "AWS::StackName"
            }
          },
          "paths": {
            "/order/stock/update/": {
              "post": {
                "x-amazon-apigateway-integration": {
                  "httpMethod": "POST",
                  "type": "aws_proxy",
                  "uri": {
                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostClickCollectStockUpdate.Arn}/invocations"
                  }
                },
                "responses": {}
              }
            },
            "/order/notify": {
              "post": {
                "x-amazon-apigateway-integration": {
                  "httpMethod": "POST",
                  "type": "aws_proxy",
                  "uri": {
                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostClickCollectNotification.Arn}/invocations"
                  }
                },
                "responses": {}
              }
            },
            "/tables/{tableid}/{columnid}": {
              "get": {
                "x-amazon-apigateway-integration": {
                  "httpMethod": "POST",
                  "type": "aws_proxy",
                  "uri": {
                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableBasic.Arn}/invocations"
                  }
                },
                "responses": {}
              }
            },
            "tables/query/{tableid}": {
              "get": {
                "x-amazon-apigateway-integration": {
                  "httpMethod": "POST",
                  "type": "aws_proxy",
                  "uri": {
                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableResponse.Arn}/invocations"
                  }
                },
                "responses": {}
              }
            },
            "/": {
              "get": {
                "x-amazon-apigateway-integration": {
                  "httpMethod": "POST",
                  "type": "aws_proxy",
                  "uri": {
                    "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Get.Arn}/invocations"
                  }
                },
                "responses": {}
              }
            }
          },
          "swagger": "2.0"
        }
      }
    },

В результате вышеупомянутой проблемы apiKey required = true не установлен в моем API-шлюзе. Все остальное работает нормально, ключи api созданы, планы использования созданы, связь между ключом и планом создается, однако ключ api, установленный на required = true, не происходит. Я здесь в растерянности. Я попытался развернуть стек с новым именем, чтобы создать новый API, но происходит то же самое.


person JamesMatson    schedule 13.09.2019    source источник


Ответы (2)


Похоже, с вашим шаблоном есть несколько проблем.

Во-первых, вы используете AWS::ApiGateway::RestApi ресурс для определения своего API вместо AWS::Serverless::Api ресурса AWS SAM. Я бы рекомендовал как можно больше использовать ресурсы AWS SAM, если нет особой причины не использовать их.

Во-вторых, похоже, что вы не используете доступные Auth свойства в бессерверных функциях. Вы сможете получить желаемую функциональность, добавив "Auth": { "ApiKeyRequired": true } к своим функциям (docs).

Наконец, я вижу ссылку на ServerlessRestApiProdStage ресурс, но нет определения для него, это может быть связано с тем, что вы используете AWS::ApiGateway::RestApi и смешивали его с AWS SAM. Если вы перейдете на AWS::Serverless::Api, вы сможете получить доступ к автоматически созданной стадии, используя { "Fn::GetAtt" : [ "ServerlessRestApi", "Stage" ] }

Надеюсь это поможет!

person Stefan Charsley    schedule 20.09.2019

Похоже, что это ограничение SAM в настоящее время. Согласно this, использование необходимого ключа API на данный момент возможно только возможно при использовании внешнего файла swagger, который, в свою очередь, ограничивает использование конфигурации события AWS::Serverless::Function.

Согласно эта проблема в репозитории SAM теперь есть RFC в чтобы сделать приоритетным добавление поддержки ключей API в SAM.

person stijndepestel    schedule 16.09.2019