AWS SAM テンプレートに既存リソースをインポートする | SEEDS Creators' Blog | 株式会社シーズ

AWS SAM テンプレートに既存リソースをインポートする

クラウド事業部エンジニアの川勝です。

サーバレスアプリケーションの開発やデプロイにはいくつかツールがありますが AWS SAM CLI を使用しています。
AWS SAM CLI では各種リソースの定義には AWS SAM テンプレートを使用しますが、これは AWS CloudFormation を拡張したもので通常の CloudFormation で使用できる定義を書くことができます。

今回このAWS SAM テンプレートに既存リソースをインポートしようとしたらすんなりといかなかったのでその方法をまとめます。

概要

  1. AWS CloudFormation スタックに既存リソースをインポート
  2. インポート先が AWS SAM テンプレートの場合
  3. AWS CLI で解決

AWS CloudFormation スタックに既存リソースをインポート

AWS SAM アプリケーションに作成済みの Amazon DynamoDB をインポートしたいと思います。まずはインポート方法をドキュメントで確認。

簡単にいうと現在使用している CloudFormation テンプレートにインポートしたいリソースを追加する。
インポートするリソースは DeletionPolicy: Retain をつける、ということのようです。
テンプレートは以下のようになります。

テンプレート

sam init で作成されるテンプレートをほぼそのまま使い sam build && sam deploy で一旦デプロイします。
deploy 後に AWS CloudFormation コンソールからテンプレートをコピーしてもってきます。
(deploy 前の template.yml は CodeUri などがローカルのパスになっているため)
そこに作成済みという想定の HelloWorldTable を追加します。
Properties は現在の設定にそったものを設定しましょう。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 'blog-cloudformation

  Sample SAM Template for blog-cloudformation

  '
Globals:
  Function:
    Timeout: 5
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: s3://aws-sam-cli-managed-default-samclisourcebucket-xxxxxxxxx/blog-cloudformation/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      Handler: hello-world
      Runtime: go1.x
      Events:
        CatchAll:
          Type: Api
          Properties:
            Path: /hello
            Method: GET
  HelloWorldTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: Retain
    Properties:
      TableName: HelloWorld
      AttributeDefinitions:
        - AttributeName: key
          AttributeType: S
      KeySchema:
        - AttributeName: key
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
Outputs:
  HelloWorldAPI:
    Description: API Gateway endpoint URL for Prod environment for First Function
    Value:
      Fn::Sub: https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/
  HelloWorldFunction:
    Description: First Lambda Function ARN
    Value:
      Fn::GetAtt:
      - HelloWorldFunction
      - Arn
  HelloWorldFunctionIamRole:
    Description: Implicit IAM Role created for Hello World function
    Value:
      Fn::GetAtt:
      - HelloWorldFunctionRole
      - Arn

注意点

インポート時には既存のテンプレートの Outputs セクションを変更すると失敗します。
例えばインポートしたリソースの Arn を Outputs に入れる場合一度インポートを完了させてからスタックの更新で追加する必要がありました。

インポート先が AWS SAM テンプレートの場合

AWS CloudFormation コンソールから「スタックへのリソースのインポート」を実行していきます。

続いて「テンプレートの指定」でさきほどの HelloWorldTable を追加したテンプレートをアップロードすると…

「このテンプレートにはインポートするリソースが含まれていません」とエラーになりました。 「詳細はこちら」の情報では解決せず、しばらく調べていると以下記事にたどり着きました。

AWS サーバーレスアプリケーションモデル (AWS SAM) テンプレート。 AWS SAM テンプレートでは、AWS::Serverless transform を使用するリソースをインポートする機能はサポートされていません。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudformation-template-resources-error/

なんと… AWS SAM では使えないのか、、と思ったらその下に

> このエラーを解決するには、AWS CloudFormation コンソールの代わりに AWS コマンドラインインターフェイス (AWS CLI) を使用できます。

これだ! AWS CLI からならできそうです。

AWS CLI で解決

前述の記事ではインポートまで AWS CLI で完結していますが、変更セットさえ作れれば実行はコンソールから行っても大丈夫でした。
AWS CLI で変更セットの作成、実行は AWS CloudFormation コンソールで行う流れで進めていきます。

AWS CLI で変更セットの作成

前述の記事の手順に沿って変更セットを作成します。

  1. import.txt という名前のリソースインポートファイルを作成します。

既存リソースを特定するためのパラメータを設定します。
ResourceType はテンプレートの [Type]、LogicalResourceId は今回のテンプレートだと [HelloWorldTable]、 Amazon DynamoDB の場合はResourceIdentifier に TableName を指定します。

[
    {
        "ResourceType": "AWS::DynamoDB::Table",
        "LogicalResourceId":
            "HelloWorldTable"
        ,
        "ResourceIdentifier": {
            "TableName":"HelloWorld"
        }
    }
]
  1. スタックに対して変更セットを作成するには、次のコマンドを実行します。

変更セットが作れたら良いので ID を環境変数にセット、、、などは省いています。
import.txt と HelloWorldTable を追加した template.yml を同ディレクトリに配置して実行。

aws cloudformation create-change-set \
    --stack-name blog-cloudformation \
    --change-set-name import-test-set \
    --resources-to-import file://import.txt \
    --change-set-type IMPORT \
    --template-body file://template.yml \
    --capabilities CAPABILITY_IAM

実行できたら再びコンソールを見てみましょう。

ステータスが CREATE_IN_PROGRESS から CREATE_COMPLETE になるのを待ちます。

「変更」タブに追加した HelloWorldTable が表示されていますね。
ではさらに「実行」してリソースのインポートを行います。

「イベント」タブからステータスが IMPORT_COMPLETE になれば完了です!

最後に「リソース」タブから HelloWorldTable がスタックの管理対象になっていることを確認しておきましょう。

まとめ

AWS SAM の場合 Lambda Function から Amazon DynamoDB を参照したり、 DynamoDB Stream をイベントにして発火、ということもあり同一テンプレートで管理されていると楽なことも多いかと思います。
そのような場合に既存リソースのインポートも使うこともあるかな、と思います。
AWS SAM テンプレートへのリソースのインポートは通常のドキュメントに情報がなかったためこれが誰かの役にたてば幸いです。
(2021年7月現在の情報なので今後のアップデートですべてコンソールからできるようになる可能性もあります。)