如何在 SAM 模板中设置舞台名称
Posted
技术标签:
【中文标题】如何在 SAM 模板中设置舞台名称【英文标题】:How to set a stage name in a SAM template 【发布时间】:2020-10-13 14:14:52 【问题描述】:我想在 SAM template.yaml 中为 API Gateway 设置阶段名称。 但无论我尝试什么,我都没有成功。无需尝试命名我的阶段,一切都按预期工作,但使用默认阶段名称 Prod 和 Stage。
我的 sam-cli 版本是 0.47.0
我确实在 *** 上找到了三个类似的问题,但没有一个答案对我有用。
How can I change the name of the API stage in a SAM template? How can I use api gateway stages via cloudformation or sam? Using SAM file to remove default “Stages” in AWS ApiGateway?我总是收到类似这样的错误:
Unresolved resource dependencies [ServerlessRestApi] in the Outputs block of the template
那么我如何获得我自己选择的艺名。我不在乎 Prod 和 Stage 是否与我选择的名称共存。
为了完整起见,我的 template.yaml 文件如下:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app
Sample SAM Template for sam-app
Globals:
Function:
Timeout: 3
Api:
Cors:
AllowMethods: "'OPTIONS,PUT'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello-world
Method: put
Outputs:
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://$ServerlessRestApi.execute-api.$AWS::Region.amazonaws.com/dev/hello-world/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
我可能不明白这背后的预期工作流程。为什么API网关指向的lambda函数相同时有2个舞台名称?
我将拥有“dev”和“prod”环境,但它们会使用不同的堆栈名称,因此我永远不会混淆不同的环境。
我总是使用 deploy-dev.sh 和 deploy-pod.sh 脚本在实际部署之前检查我是在开发分支还是主(生产)分支。因此这些脚本将指向不同的 template.yaml 文件,因为它们是从不同的 git 分支调用的。我已经使用这种方式进行部署很长时间了,它对我来说效果很好。
附带说明:为什么现有的艺名以大写开头?它看起来如此丑陋和不寻常。
【问题讨论】:
【参考方案1】:所以我找到了自己的答案,它是我在 *** 上找到的问题中提到的两个问题的组合。
我还是不明白为什么会这么复杂。
我在 template.yaml 文件的顶层添加了一个参数。并不严格需要使用参数。我添加了这个,所以我可以拥有一个从我的 deploy-dev.sh 和 deploy-prod.sh 脚本调用的模板文件。 下面是参数声明:
Parameters:
Stage:
Type: String
Default: dev
然后,在 Resources 组下,我添加了一个新的 ApiDeployment 资源。您使用的名称完全取决于您,只要您在其他地方使用与 !Ref 完全相同的名称。添加此资源的唯一原因是不允许您在函数事件的 Api 部分的属性中简单地使用 StageName。您也不能将 StageName 放在 Globals Api 部分。
ApiDeployment:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage <- this is referencing the parameter but it could be a fixed value
然后,在 Lambda 函数的 Events 部分下,我添加了一个引用 ApiDeployment 资源的属性 RestApiId。下面块中的最后一行。
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello-world
Method: put
RestApiId: !Ref ApiDeployment
正如我在问题中提到的,我收到了抱怨 yaml 文件的输出部分的错误。事实证明,输出部分无论如何都是可选的。因此,当我将其注释掉时,一切正常。
但我使用部署脚本中的输出部分向我显示了 API 网关的 URL,因此通过一些尝试我也得到了它的工作。 错误是在第 4 行引起的。它最初有 $ServerlessRestApi。只需将其替换为我添加到 yaml 文件中的新资源名称:$ApiDeployment,一切都很好。
Outputs:
ApiDeployment:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://$ApiDeployment.execute-api.$AWS::Region.amazonaws.com/$Stage/hello-world/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
因为我在yaml文件中使用了参数,所以需要调用sam deploy,参数名和值。就像许多 AWS 的文档一样,它的确切语法被很好地隐藏了。下面是您开始部署的方式:
sam deploy --parameter-overrides "ParameterKey=Stage,ParameterValue=dev"
您可能仍然在 API Gateway 控制台中的 Stages 下拥有 Stage 阶段,但您可以删除它而不会产生任何影响。
为了完整起见,这里是我的完整 template.yaml 文件,顺便说一下,这是您在执行 sam init
时获得的文件AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
sam-app: Sample SAM Template for sam-app
Parameters:
Stage:
Type: String
Default: dev
Globals:
Function:
Timeout: 3
Api:
Cors:
AllowMethods: "'OPTIONS,PUT'"
AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs12.x
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello-world
Method: put
RestApiId: !Ref ApiDeployment
ApiDeployment:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
Outputs:
ApiDeployment:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://$ApiDeployment.execute-api.$AWS::Region.amazonaws.com/$Stage/hello-world/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn
【讨论】:
为什么没有更多的赞成票?!你拯救了我的一天。那肯定是几天的研究。 @Eric Dela Cruz 谢谢。 stage 参数现在被用作一个 stage 并在 url 内。然而,我最后仍然有 2 个 api 网关,1 个带有 dev 阶段,另一个带有 prd 阶段。有没有办法合并它们? @Jdruwe 我不认为你可以合并它们。删除你不喜欢的。【参考方案2】:这里是针对此问题的任何人的更新。
我不再使用 SAM。我切换到 AWS CDK。 CDK 让您可以在代码中定义整个 AWS 基础设施! 您可以使用 javascript、Typescript、Python、C# 和 Java,尽管大多数示例似乎都在 Typescript 中。
这不是一个很容易的转换,但它是值得的。 SAM 似乎是一条死胡同。
SAM 仅涵盖 AWS 提供的所有服务的一小部分,但 CDK 涵盖了所有内容。
它很新,而且它是一个移动的目标,而且开发人员不会对中断更新表示不满,因为他们仍在模块之间移动东西。
但是几天后,您就会开始掌握它,并且它具有无限的灵活性,因为您使用普通的编程语言来设置 API 网关、Lambda、自定义域、IAM 规则等。 它也非常紧凑(与 SAM 模板相比)。
根据我所在的 got 分支,我使用它来为登台和生产提供不同的堆栈。因此,当我在我的 repo 位于 dev 分支上时进行部署时,我将拥有不同的环境(包括不同的域名等) ) 然后当我从 master 或 prod 分支进行部署时。不同服务的名称也不同,具体取决于 git 分支。
要部署,您只需运行“cdk deploy”
要开始看这个优秀的研讨会:https://cdkworkshop.com/
以下是此分支切换的示例。我只展示了 dev 分支的部分方法。对于 prod 分支,我只是在同一个文件中拥有相同方法的副本,但该方法具有不同的名称,并且服务名称的变量也不同。只要看看这段(不完整的)代码,你就应该知道它是如何工作的。
import * as branchName from 'current-git-branch'
const branch = branchName()
/*-------- This is the development stack --------*/
export class StripePaymentsDev extends cdk.Stack
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps)
super(scope, id, props)
if (branch !== 'dev')
throw new Error('Not on dev branch')
// Env vars from Parameter Store
const STRIPE_PUBLIC = StringParameter.valueForStringParameter(this, `/$branch/STRIPE_PUBLIC`)
const STRIPE_SECRET = StringParameter.valueForStringParameter(this, `/$branch/STRIPE_SECRET`)
const STRIPE_API_VERSION = StringParameter.valueForStringParameter(this, `/$branch/STRIPE_API_VERSION_PAYMENTS`)
// Names for the dev environment
const domainMapName = 'PaymentsDev'
const eventBusName = 'WebhooksBusDev'
const ruleName = 'WebhooksRuleDev'
const eventBus = new EventBus(stackScope, eventBusName, eventBusName )
const cert = Certificate.fromCertificateArn(stackScope, certName, certArn)
const stackScope = this
// IAM rules
const lambdaPolicy = new iam.PolicyStatement(
actions: ['events:*'],
resources: ['*']
)
const sqsPolicy = new iam.PolicyStatement(
actions: ['sqs:*'],
resources: ['*']
)
const webhooks = new lambda.Function(stackScope, lambdaWebhooksName,
runtime: lambda.Runtime.NODEJS_12_X,
code: lambda.Code.fromAsset('webhook-handler'),
handler: 'webhooks.handler',
timeout: Duration.seconds(600),
description: 'Processes Stripe Webhooks',
retryAttempts: 0,
environment:
STRIPE_PUBLIC,
STRIPE_SECRET,
STRIPE_API_VERSION,
MONGO_URL,
MONGO_DB,
MONGO_PORT,
DEBUG
)
webhooks.addToRolePolicy(sqsPolicy)
const rule = new Rule(stackScope, ruleName,
description: 'Triggers lambda to process stipe webhooks',
enabled: true,
eventBus: eventBus,
eventPattern:
detailType: ['transaction'],
source: ['custom.payments']
,
ruleName: ruleName
)
rule.addTarget(new eventTargets.LambdaFunction(webhooks))
new HttpApi(stackScope, apiName,
defaultIntegration: new LambdaProxyIntegration( handler: payments ),
defaultDomainMapping:
domainName: new DomainName(stackScope, domainMapName,
domainName: PAYMENT_DOMAIN,
certificate: cert
)
)
【讨论】:
几天前我刚开始使用 SAM,但您说服我改用 CDK。感谢您为帖子带来光明。【参考方案3】:这种方式只创建您指定的阶段,而不是创建一个名为 Stage
的阶段。
这个设置成功了。
全局: 接口: OpenApiVersion:3.0.1
我还创建了名为 RestApi
的新 AWS::Serverless::Api
以覆盖隐式 ServerlessRestApi
。请记住将 RestApi
设置为每个 API 事件的 RestApiId
。
template.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: >-
app-sam
Transform:
- AWS::Serverless-2016-10-31
# ====================================
# PARAMETERS SETUP
# ====================================
Parameters:
StageName:
Type: String
Default: dev
Description: (Required) Enter dev, prod. Default is dev.
AllowedValues:
- dev
- prod
ProjectName:
Type: String
Default: sam-api
Description: (Required) The name of the project
MinLength: 3
MaxLength: 50
AllowedPattern: ^[A-Za-z_-]+$
ConstraintDescription: "Required. Can be characters, hyphen, and underscore only. No numbers or special characters allowed."
ExistingTable:
Type: String
Default: example-table
Description: (Required) The name of existing DynamoDB
MinLength: 3
MaxLength: 50
AllowedPattern: ^[A-Za-z_-]+$
ConstraintDescription: "Required. Can be characters, hyphen, and underscore only. No numbers or special characters allowed."
# ====================================
# GLOBAL SETUP
# ====================================
Globals:
Api:
OpenApiVersion: 3.0.1
Function:
Runtime: nodejs14.x
Timeout: 180
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ExistingTable
Resources:
# Reference this one to overwrite implicit stage
# https://github.com/aws/serverless-application-model/issues/191#issuecomment-580412747
RestApi:
Type: AWS::Serverless::Api
Properties:
Name: !Ref ProjectName
StageName: !Ref StageName
# This is a Lambda function config associated with the source code: get-all-items.js
getAllItemsFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/get-all-items.getAllItemsHandler
Description: A simple example includes a HTTP get method to get all items from a DynamoDB table.
Policies:
# Give Create/Read/Update/Delete Permissions to the ExistingTable
- DynamoDBCrudPolicy:
TableName: !Ref ExistingTable
Events:
Api:
Type: Api
Properties:
Path: /
Method: GET
RestApiId: !Ref RestApi
Outputs:
WebEndpoint:
Description: "API Gateway endpoint URL for Prod stage"
Value: !Sub "https://$RestApi.execute-api.$AWS::Region.amazonaws.com/$StageName/"
【讨论】:
部署“dev”阶段有效,但是当我部署“prod”阶段时,“dev”阶段被删除!然后当我再次部署“dev”阶段时,“prod”阶段被删除!当我部署另一个时,如何让一个留下来?以上是关于如何在 SAM 模板中设置舞台名称的主要内容,如果未能解决你的问题,请参考以下文章