はじめに
この記事では、AWS SAMテンプレートでAWS Lambda、AmazonAPI Gateway、AmazonSNSを使用したサーバレスアプリケーションを作ろうとしたときに躓いたポイントを紹介します。
AWS SAM テンプレートとは
AWS SAMのテンプレートはAWS CloudFormation テンプレートの拡張で基本的にはCloudFormationのテンプレートの書き方と同じですがSAM特有の便利なコンポーネントも追加されています。
もっと詳しく知りたい方はこちらを参考にしてみてください。
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-reference.html
#CloudFormationの組み込み関数
CloudFormationにはスタックの管理に便利な組み込み関数があります。ここではそれらの関数のうち2つを紹介します。便利な使い方としては、実行するまでわからない値(SAMテンプレートで作成したLambda関数のAmazonリソースネーム (ARN)だったり、SNSのTopicNameだったり)を取得してプロパティに代入することができます。
Fn::GetAtt(!GetAtt)
まずFn::GetAtt(!GetAtt)について紹介します。短縮形の構文で!GetAttとかかれることが大きので以下!GetAttとして説明します。!GetAtt組み込み関数は戻り値としてテンプレートのリソースから属性の値(リソース名やARNなど)を返します。例えばテンプレートで以下のようにLambdaを定義したとしましょう。
Resources:
SampleLambda:
Type:AWS::Serverless::Function
Properties:
FunctionName: SampleFunction
Runtime: python3.7
CodeUri: test/
Handler: app.lambda_handler
テンプレートにLambdaを定義しただけではLambda関数は作成されません。当然、buildとdeployをしなければなりません。buildとdeployを行うとLambda関数が作成されARNなどの値も生成されます。ということは、もしテンプレートを書いてリソースなどを定義している段階でLambdaのARNが必要になったとしてもdeployしてから手動でARNをコピーしてテンプレートに付け足してまたdeployしなければいけないの??とその時は悲しい気持ちでテンプレートを書いてました。そこで発見したのがこの!GetAttという組み込み関数です。以下の例を参考に使い方をみていきます。
Resources:
SampleLambda:
Type:AWS::Serverless::Function
Properties:
FunctionName: SampleFunction
Runtime: python3.7
CodeUri: test/
Handler: app.lambda_handler
Role: !GetAtt SampleIamRole.Arn
SampleIamRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service: lambda.amazonaws.com
Action: 'sts:AssumeRole'
SampleLambdaに対するSampleIamRoleというRoleを定義しました。このRoleをLambdaに紐付けるときに使用するのが!GettAtt関数です。
Role: !GettAtt SampleIamRole.Arn
これを付け足すと作成したRoleのARNを取得しLambdaに紐づけてくれます。すごく便利です。
Fn::Ref(!Ref)
次にFn::Ref(!Ref)について紹介します。短縮形の構文で!Refとかかれることが大きので以下!Refとして説明します。!Ref組み込み関数は戻り値として指定したパラメータまたはリソースの値を返します。先ほどと同じようにテンプレートで以下のようにLambdaを定義します。
Resources:
SampleLambda:
Type:AWS::Serverless::Function
Properties:
FunctionName: SampleFunction
Runtime: python3.7
CodeUri: test/
Handler: app.lambda_handler
Events:
SNS:
Type: SNS
Properties:
Topic: !Ref SampleTopic
SampleTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: SampleTopic
Lambdaのトリガー(イベント)としてSNSのトピックを定義しました。イベントとしてSNSのトピックを設定するときはトピックのARNが必要です。そこで先ほどの!GetAtt関数を使用してARNを取得しようとしてもうまくいきません。ここが今回紹介する躓きポイントです。ドキュメントをみてみるとこんなことが書かれています。
戻り値
参照番号
トピック ARN (例: arn:aws:sns:us-east-1:123456789012:mystack-mytopic-NZJ5JSMVGFIE) このリソースの論理 ID を組み込みの Ref 関数に渡すと、Ref は次を返します: 。
For more information about using the Ref function, see Ref.
Fn::GetAtt
Fn::GetAtt 組み込み関数は、このタイプの指定された属性の値を返します。以下には、利用可能な属性とサンプル戻り値のリストが示されます。
Fn::GetAtt 組み込み関数の使用方法の詳細については、「Fn::GetAtt」を参照してください。
TopicName
Amazon SNS トピックの名前を返します。
AWS::SNS::Topicタイプリソースに対して!GetAtt関数を使用すると戻り値として返ってくるのはARNではなくSNS トピックの名前です。!Refを使用すると戻り値としてSNSトピックののARNが返ってきます。
ARNを取得しようとして!GetAttや!Refを使用してもリソースによっては!Refで取得できたり、!GetAttで取得できる違いがあるようです。
まとめ
- 取得したい値によって!GetAttや!Refを使い分けるだけではなく、同じような値(ARNやリソース名など)を取得するにしてもリソースによって使い分けなければいけない
- とにかくドキュメントに全て書いてあるから読み込む
というのがすごく大事だなと痛感しました。