CloudFormationを使用してアカウント間でのリソース移行をしてみた

こんにちは、中根です。

コンソール画面から手動で構築したリソースを、複製したり別アカウントへ移行したいということもあるかと思います。
その際に、同じ設定のリソースを手動で構築するのは手間がかかりますし、ミスにも繋がります。

そこで今回は、CloudFormationというサービスを使用して、リソースをテンプレートとして管理し、別アカウントに移行するという方法を紹介します!

CloudFormationとは

CloudFormationは、JSONまたはYAML形式のテキストファイルを使用して、AWSリソースを自動構築するサービスです。
AWSリソースの設定をテンプレート化できるため、同じ環境を何度でも自動で構築することが可能となります。

CloudFormation自体は無料で使用できますが、CloudFormationで構築された各リソースは通常通り課金の対象となります。

今回の目標

今回は、CloudFront・S3・API Gateway・Lambdaを使用したサービスを別アカウントに複製していきます。
以下が移行するサービスの画面となります。

複製するサービス

「Lambda実行」のボタンを押すと、Lambdaが「Hello World!」を返すものとなっています。
Lambdaのソースコードは以下のようなシンプルなものです。

exports.handler = async (event) => {
    console.log('Lambda Start');
    const response = {
        statusCode: 200,
        body: 'Hello World!',
    };
    return response;
};

構成図

別アカウントに移行するサービスの構成図は以下のようになっています。

AWS構成図

S3+CloudFrontのテンプレート作成

はじめにS3とCloudFrontの部分のテンプレートをYAML形式で作成しました。
テンプレートは以下のような設定で作成しています。

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Globals:
  Api:
    OpenApiVersion: 3.0.2

Resources:
  #S3バケット
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: S3BucketName
      PublicAccessBlockConfiguration: #パブリックアクセスブロックを有効化
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
 #バケットポリシー
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement: #OAIからのみのアクセスに制限
            - Sid: '1'
              Effect: Allow
              Action: s3:GetObject
              Resource: arn:aws:s3:::S3BucketName/*
              Principal:
                OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'

 #CloudFront
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
  Properties:
      DistributionConfig:
        Origins:
     - Id: S3Origin
      DomainName: !GetAtt S3Bucket.DomainName
      S3OriginConfig:
              OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/{CloudFrontOriginAccessIdentity}' #OAIを紐づけ
        Enabled: true #ディストリビューションを有効化
         DefaultRootObject: index.html
         DefaultCacheBehavior:
           AllowedMethods: #許可された HTTP メソッド
             - GET
             - HEAD
           TargetOriginId: S3Origin
           ForwardedValues:
             QueryString: false
           ViewerProtocolPolicy: https-only #ビューワープロトコルポリシー
   CloudFrontOriginAccessIdentity:
     Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
     Properties:
       CloudFrontOriginAccessIdentityConfig: #OAIを作成
         Comment: !Ref AWS::StackName

API Gateway+Lambdaのテンプレート作成

先ほどのテンプレートの続きに、API GatewayとLambdaの設定を記述していきます。

#API Gateway
  APIGateway: #論理ID
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev #ステージ名
      Name: api-blog-test
      DefinitionBody: #解説①
        Fn::Transform:
          Name: AWS::Include
          Parameters:
      #API構成ファイルのパス
            Location: s3://×××××/××××××××-swagger-apigateway.yaml

  #Lambda
  LambdaFunction: #論理ID
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: index.handler
      Runtime: nodejs16.x
      #解説②
      CodeUri: s3://×××××/××××××××××.zip
      Description: ''
      MemorySize: 128
      Timeout: 3
      Role: 'arn:aws:iam::××××:role/lambdaRole' #実行ロール(IAMロールが無い場合は事前に作成する必要がある)
      Events: #API Gatewayを紐づけ
        Api:
          Type: Api
          Properties:
            Path: /
            Method: GET
            RestApiId: !Ref APIGateway

解説① DefinitionBody

DefinitionBodyには、API Gatewayの定義ファイルを設定します。
定義ファイルはコンソール画面からエクスポートすることができます。

API Gatewayエクスポート

エクスポートした定義ファイルを開き、紐づけるLambdaを指定している「uri」の部分を、複製するLambda用に書き換えます。

定義ファイル書き換え

続いて、定義ファイルを配置するためのS3バケットを作成します。
作成したS3バケットに、編集した定義ファイルをアップロードして、S3 URIを控えます。

S3アップロード

控えておいたS3 URIをDefinitionBodyのLocationにセットすることで、API Gatewayの設定を複製することができます。

開発② CodeUri

CodeUriには、Lambda関数のソースコードをzip化したものを配置したフォルダパスを指定します。
zip化したソースコードはLambdaのコンソール画面からエクスポートすることができます。

Lambda関数の画面から、「アクション」⇒「関数のエクスポート」をクリックすると、以下のようなエクスポート画面が表示されます。
zipファイルをエクスポートするには、「デプロイパッケージのダウンロード」をクリックします。

Lambdaエクスポート

エクスポートしたzipファイルを、先ほどのAPI Gatewayの定義ファイルをアップロードしたS3バケットにアップロードします。

Lambdaアップロード

S3 URIをテンプレートのCodeUriにセットすることで、Lambda関数のソースコードを複製することができます。

テンプレートの読込

テンプレートの作成が完了したら、複製したいアカウントのCloudFormationの画面からテンプレートを読み込んで、リソースを作成します。

CloudFormationの画面から、「スタックの作成」⇒「新しいリソースを使用(標準)」をクリックして、スタックを作成する画面へ遷移します。
ちなみにスタックとは、テンプレートによって作成されたリソースの集まりのことを表します。

スタック作成開始

スタックの作成画面で、ローカルにあるテンプレートファイルをアップロードします。

テンプレートアップロード

スタックの詳細を指定する画面では、スタックの名前を設定します。

スタック名設定

次のスタックオプションの設定画面では、何も変更せずに進みます。
最後のレビュー画面で、以下のような確認項目が表示された場合は、すべてチェック入れてスタックの作成を行います。

レビュー

スタックの作成を開始すると、各リソースの作成状況が表示されます。
スタックのステータスが「UPDATE_COMPLATE」と表示されたら、作成が成功しているということになります。

スタック作成状況

動作確認

Lambdaが作成され、API Gatewayと紐づいていることが確認できます。

Lambda確認

CloudFrontとS3も作成されているかと思いますが、S3はバケットとバケットポリシーを作成しただけなので、HTMLファイルなどは再度アップロードする必要があります。

CloudFrontのディストリビューションドメイン名にアクセスすると、S3にアップロードした画面が表示され、ボタンクリックでLambdaが実行できるようになっています。

動作確認

さいごに

今回は、CloudFormationを使用してコンソール画面から手動で作成したリソースを、別アカウントに移行する方法を紹介しました。
今回の構成のテンプレートは90行ほどのボリュームとなりましたが、一度作成しておけば何度でも複製できたり、部分的に編集して使いまわすことも可能です!

作成したスタックを更新することも可能なので、今後試してみたいと思います。

AWS

Posted by yukiteru