Former2をローカルにホストして、既存リソースのCloudFormationテンプレートを出力してみた

既存のAWSリソースからテンプレートを作成できるFormer2をローカル環境にホストして利用してみました。
2021.06.30

最近以下の登壇資料を見てローカルでFormer2を使えると知り、「使ってみたい!」となったので実際にやってみたブログです。

Former2について解説されていて使ってみたいモチベが上がるので、Former2聞いたことない人やIaCよくわかんないよって人は是非読んでみて下さい。

Former2とは

既存のAWSのリソース情報を取得し、CloudFormation, Terraform, CDK等のテンプレート・コードを出力できるWebサービスです。

上記のサイトからそのまま利用できるのですが、AWSの認証情報(アクセスキー 、シークレットアクセスキー )を入力しなければいけないことから、セキュリティ上の不安がありました。

それが今回Dockerを使ってローカルでホストできるとのことで、これまでセキュリティ観点から利用できなかった方々は、この方法でFormer2を利用してみて下さい。

やってみた

Docker、Gitが利用できる前提で進めていきます。今回はローカルで起動したFormer2からReadOnlyAccessの権限をつけたロールを使用して、CloudFormationテンプレートを作成していきます。

Former2を起動

Former2は以下のリポジトリで公開されているので、コンテナ起動に必要なファイルをクローンします。

https://github.com/iann0036/former2

任意のフォルダで以下コマンドを実行します。

$ git clone https://github.com/iann0036/former2.git

クローンされたフォルダに移動し、コンテナを起動します。

$ cd former2
$ docker-compose up -d
Creating former2_former2_1 ... done

あとはChromeなどのブラウザで127.0.0.1:80にアクセスしてみると、Former2の画面が確認できました。

IAMロールの作成

今回はIAMロールを使用するので、専用のロールを作成します。(既に利用できるロールがある/IAMユーザーを使用する場合は飛ばして構いません。)

IAMロール/ユーザーはAdministrator権限ではなく、なるべくReadOnlyAccessの権限を利用することをお勧めします。

利用したいAWSコンソールへログインしてIAM>ロールからロールの作成をしていきます。

アカウントIDはこのロールを引き受ける予定のアカウントを指定して下さい。

必要な権限ReadOnlyAccessを選択して進みます。

今回はFormer2というロール名で作成しました。

ロールを作成したので、このロールを使用してFormer2のCredentialsを設定していきます。

拡張機能の導入

ここからの手順、詳細については以下エントリで紹介されているので、細かい説明は省きます。結構重複する部分があるのは許してください。

setup>Introductionを開き、使用しているブラウザの拡張機能を追加します。

Former2 Helperをインストールできれば完了です。

認証情報の設定

IAMロールの権限で実行するために、Setup>Credentialsに入力する赤枠4項目を取得していきます。IAMユーザーで利用したい方はアクセスキー とシークレットアクセスキー を入力するだけで大丈夫です。

どうやってロールの認証情報を取得するかというと、以下のドキュメントに記載があります。

AWS CLI で一時的なセキュリティ認証情報を使用する

記載がある通りにターミナルから以下のコマンドを実行します。{AWS:AccountID}は作成したIAMロールのアカウントID、{IAM-user-name}にはロールを引き受けることができるIAMユーザーのプロファイルを指定してください。

$ aws sts assume-role --role-arn arn:aws:iam::{AWS:AccountID}:role/Former2 --role-session-name "RoleSession1" --profile {IAM-user-name} > assume-role-output.txt

assume-role-output.txtのファイルに必要な情報が出力されているので、コピー&ペーストでFormer2にそれぞれ入力しましょう。ここで取得できる認証情報のセッション時間はデフォルト1時間となっているので、Former2を利用する際に都度実行しましょう。

{
    "Credentials": {
        "AccessKeyId": "AAAAAAAAAAAAAAAAAAA",
        "SecretAccessKey": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
        "SessionToken": "******************************************",
        "Expiration": "2021-06-25T03:32:35+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AAAAAAAAAAAAAAAAAAA:RoleSession1",
        "Arn": "arn:aws:sts::999999999999:assumed-role/Former2/RoleSession1"
    }
}

その後のParametersSettingsは全てデフォルトでOKです。

CloudFormationテンプレートを出力する

ここからは好きなリソースでテンプレートを出力するだけです。

今回は例として、Codeシリーズを使ったチュートリアルで作成したリソースがあったのでテンプレートにしてみます。このチュートリアルでは、CodeCommit,CodeDeploy,CodePipelineの3つのリソースを作成しました。

チュートリアル: シンプルなパイプラインを作成する (CodeCommit リポジトリ) - AWS CodePipeline

まずはCodeCommitをテンプレートにしてみます。Former2の使い方は簡単で、Dashboardを開くとAWSサービス一覧が表示されるので、利用したいサービスを選びます。

Credencialsで設定したAWSアカウントのリソースが表示されるので、チェックを入れてAdd Selectedをクリックすれば完了です。リソースが読み込み中のまま表示されない時は、右上のScan Againを実行しましょう。

画面上部にあるGenerateをクリックするとCloudFormationのテンプレートが作成されます。

これだけでも十分ありがたいのですが、Former2の凄いところは関連リソースを合わせて選択すると、組み込み関数の形でテンプレートが修正されるところです。

同じように、CodeDeployとCodePipelineもまとめてGenerateしたのが以下のテンプレートです。

AWSTemplateFormatVersion: "2010-09-09"
Metadata:
    Generator: "former2"
Description: ""
Resources:

    CodeCommitRepository:
        Type: "AWS::CodeCommit::Repository"
        Properties:
            RepositoryDescription: "MyDemoRepo"
            RepositoryName: "MyDemoRepo"

    CodeDeployApplication:
        Type: "AWS::CodeDeploy::Application"
        Properties:
            ApplicationName: "MyDemoApplication"
            ComputePlatform: "Server"

    CodeDeployDeploymentGroup:
        Type: "AWS::CodeDeploy::DeploymentGroup"
        Properties:
            ApplicationName: !Ref CodeDeployApplication
            DeploymentGroupName: "MyDemoDeploymentGroup"
            DeploymentConfigName: "CodeDeployDefault.OneAtATime"
            ServiceRoleArn: "arn:aws:iam::999999999999:role/CodeDeployRole"
            AlarmConfiguration: 
                Enabled: false
                IgnorePollAlarmFailure: false
            DeploymentStyle: 
                DeploymentType: "IN_PLACE"
                DeploymentOption: "WITH_TRAFFIC_CONTROL"
            LoadBalancerInfo: 
                TargetGroupInfoList: 
                  - 
                    Name: "test-tg"
            Ec2TagSet: {}
            OnPremisesTagSet: {}

    CodePipelinePipeline:
        Type: "AWS::CodePipeline::Pipeline"
        Properties:
            Name: "MyFirstPipeline"
            RoleArn: !Sub "arn:aws:iam::999999999999:role/service-role/AWSCodePipelineServiceRole-${AWS::Region}-MyFirstPipeline"
            ArtifactStore: 
                Location: !Sub "codepipeline-${AWS::Region}-870438305283"
                Type: "S3"
            Stages: 
              - 
                Name: "Source"
                Actions: 
                  - 
                    Name: "Source"
                    ActionTypeId: 
                        Category: "Source"
                        Owner: "AWS"
                        Provider: "CodeCommit"
                        Version: "1"
                    Configuration: 
                        BranchName: "master"
                        OutputArtifactFormat: "CODE_ZIP"
                        PollForSourceChanges: "false"
                        RepositoryName: !GetAtt CodeCommitRepository.Name
                    OutputArtifacts: 
                      - 
                        Name: "SourceArtifact"
                    Region: !Ref AWS::Region
                    Namespace: "SourceVariables"
                    RunOrder: 1
              - 
                Name: "Deploy"
                Actions: 
                  - 
                    Name: "Deploy"
                    ActionTypeId: 
                        Category: "Deploy"
                        Owner: "AWS"
                        Provider: "CodeDeploy"
                        Version: "1"
                    Configuration: 
                        ApplicationName: !Ref CodeDeployApplication
                        DeploymentGroupName: !Ref CodeDeployDeploymentGroup
                    InputArtifacts: 
                      - 
                        Name: "SourceArtifact"
                    Region: !Ref AWS::Region
                    Namespace: "DeployVariables"
                    RunOrder: 1

このように複数のリソースをまとめて出力することもできます。 AWS::CodePipeline::Pipelineが定義されているセクションを見ると、CodeCommitのリポジトリ名を指定するRepositoryName!GetAtt CodeCommitRepository.Nameと組み込み関数で取得してくれています。 同じくApplicationNameを定義する部分ではCodeDeployを!Ref CodeDeployApplicationの形で参照するように、自動でコードが生成してくれているのが最高ですね。

注意点

ここで作成されるテンプレートですが、そのままCloudFormationで流せるわけではありません。必須パラメータがなかったり一部コードに落とし込めていないなど、細かいエラーがあるので微調整は必要になります。

(今回の例ではCodeDeployDeploymentGroupEc2TagSetOnPremisesTagSetが不要なのに出力されてエラーになりました。)

盲信せずドキュメントの確認、作成したリソースが同じパラメータになっているかのチェックは忘れないようにしましょう。

まとめ

DockerでFormer2を起動できると聞いてウキウキで試してみました。触ったことのないサービスをコンソールから作成してコードの書き方を確認したり、検証環境で作成したリソースをコード化して本番環境の作成したりと幅広く利用できる超便利なツールです。

これまでセキュリティ観点から利用できなかった方々は、是非この方法でFormer2を利用してみてはいかがでしょうか?