NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    承認フロー付きEC2作成用のSSMAutomationを自作してみた

    はじめに

    こんにちは!入社一年目の牛塚です。今回はAWS SSM Automationを自作したので、学んだことをまとめてみました。 「SSMAutomationとはなんだろう?」という方や「AWSが提供するドキュメントじゃ要件を満たせない」と考えている方におすすめの内容となってます。

    SSM Automationとは

    SSM Automationとはメンテナンスやデプロイを簡素化するAWS Systems Manager (SSM)の機能の一つです。AWSリソースの大規模デプロイ、設定、管理などの作業を自動化することが出来ます。 例えば、複数台のEC2インスタンスをまとめて起動する必要があるとき、マネジメントコンソールから一台ずつ起動していくと時間や手間がかかってしまいます。 そこでSSMAutomationを実行することで特定のタグがついたインスタンスを一斉に起動することが出来ます。
    上記の例はほんの一例ですが、SSMAutomationは複数のリソースをまとめて操作する、同じ作業の繰り返すを自動化できるなど利点があります。

    ランブックについて

    SSMAutomationはランブックと呼ばれるドキュメントを実行することで起動します。 ランブックはJSONまたはYAMLで記述します。 ランブックには以下の要素を記載していきます。

    description: <ランブックの説明文>
    schemaVersion: <スキーマのバージョン>
    parameters:  <入力パラメータ(ランブックへの引数)>
    mainSteps: <各ステップを記載する>

    この他にもランブックの出力(戻り値)を記載したりすることが出来ます。
    詳細は以下の公式ドキュメントをご覧ください。
    データ要素とパラメータ - AWS Systems Manager

    SSM Automationを実行するとmainSteps内に記載されたステップを上から順番に実行していきます。

    ドキュメントに記載されたステップの実行画面

    ステップごとに実行するアクションを定義します。アクションはリソースを特定したり、APIを実行したりと様々なものが用意されています。 詳しくは以下をご覧ください。
    Systems Manager Automation アクションのリファレンス - AWS Systems Manager

    このような構成で記載されているランブックですが、 AWSが提供している定義済みのランブックも存在し様々なタスクを実行出来ます。 具体的には以下のようなことが実現可能です。

    • 複数のEC2、RDSの一括起動・停止
    • CloudFormationのスタックの削除
    • 実行中のインスタンスからAMIを作成する
    • など

    「AWSが提供しているランブックじゃやりたいことができないよ~」という人はランブックを自作することが出来ます。

    今回自作するランブック

    今回はEC2インスタンスを作成するランブックを自作します。このランブックは次のような利用シーンに活用出来ます。

    複数人のユーザーそれぞれに必要な時に必要な時だけ個別のインスタンスを作成する必要があるときに、 マネジメントコンソール上でAMIやサブネットなどを選択し作成するとヒューマンエラーが発生しやすくなります。 そこでSSMAutomationを使用することで最低限の操作だけでインスタンスを作成することが出来ます。

    AWSが事前定義しているランブックは停止状態のインスタンスを起動するものはありますが、インスタンスを一から作成するものは現時点でありません(2024年3月7日時点)。 今回はインスタンスを作成するだけではなく、承認フローやインスタンス作成完了メールも組み込んでみました。

    依頼者(ユーザー)が承認者に申請をしてSSMAutomationは承認がおりたら、インスタンスの作成を始めます。ユーザーごとにSNSトピックをあらかじめ作成しており、インスタンスが作成された後、ユーザーへ 作成完了を知らせるメールが送信されます。

    以下のようなステップで構成されています

    1. ユーザーのSNSトピックの特定
    2. 承認依頼・承認
    3. EC2インスタンスの作成
    4. EC2のプライベートIPを取得
    5. インスタンスの作成完了メール送信

    構成図

    サンプルコード

    入力パラメータ

    SSMAutomationのパラメータですが、今回はユーザーネームを入力する仕様にしました。のちに出てくるユーザーごとのSNSトピックのタグに紐づけられて、ユーザーのSNSトピックを特定するときに使用されます。

    SSMAutomationの入力画面

    description: Create and launch an EC2 instance after approval
    schemaVersion: '0.3'
    parameters:
      UserName:
        type: String
        description: (Required) UserName
    
    SNSTopicの特定 Step

    SNSトピックのタグにはユーザーネームが設定されており、入力パラメータに指定されたユーザーネームからユーザー固有のSNSトピックを検索します。

    ユーザーのSNSトピックのタグ

    mainSteps:
      - name: IdentifyTopicsonSNS
        action: aws:executeAwsApi
        nextStep: approve
        isEnd: false
        inputs:
          Service: resourcegroupstaggingapi
          Api: GetResources
          TagFilters:
            - Key: UserName
              Values:
                - '{{UserName}}'
          ResourceTypeFilters:
            - sns
        outputs:
          - Name: SNSTopicArn
            Selector: $.ResourceTagMappingList[0].ResourceARN
            Type: String
          - Name: UserName
            Selector: $.ResourceTagMappingList[0].Tags[0].Value
    
    承認Step

    承認者へ承認依頼のメールを送信します。

    インスタンス作成の承認依頼メール

      - name: approve
        action: aws:approve
        nextStep: launchInstance
        isEnd: false
        onFailure: Abort
        inputs:
          NotificationArn: arn:aws:sns:ap-northeast-1:hogehoge
          Message: '{{UserName}} is requesting authorization to launch an EC2 instance'
          MinRequiredApprovals: 1
          Approvers:
            - arn:aws:iam::hugahuga
    インスタンス作成Step 

    インスタンスを作成します。AMIやロールは事前に作成されたものを指定します。

    インスタンスの起動

      - name: launchInstance
        action: aws:runInstances
        maxAttempts: 3
        timeoutSeconds: 1200
        nextStep: GetEC2InstanceIP
        isEnd: false
        onFailure: Abort
        inputs:
          ImageId: ami-hugohugo
          InstanceType: t2.micro
          MinInstanceCount: 1
          MaxInstanceCount: 1
          IamInstanceProfileName: sample_role
          TagSpecifications:
            - ResourceType: instance
              Tags:
                - Key: LaunchedBy
                  Value: SSMAutomation
                - Key: CreatedBy
                  Value: '{{UserName}}'
                - Key: Name
                  Value: '{{UserName}}'
        outputs:
          - Name: myInstanceId
            Selector: $.InstanceIds
            Type: StringList
    インスタンスの情報抽出Step

    ユーザーへ送信するメールに記載するプライベートIPを抽出します。

      - name: GetEC2InstanceIP
        action: aws:executeAwsApi
        nextStep: SendNotification
        isEnd: false
        inputs:
          Service: ec2
          Api: DescribeInstances
          InstanceIds: '{{launchInstance.myInstanceId}}'
        outputs:
          - Name: PrivateIP
            Selector: $.Reservations[0].Instances[0].PrivateIpAddress
            Type: String
    作成完了メール送信Step

    ユーザーへインスタンス作成が完了したことをSNSTopicを使用して通知します。

    インスタンス作成後にユーザーに届くメール

      - name: SendNotification
        action: aws:executeAwsApi
        isEnd: true
        inputs:
          Service: sns
          Api: Publish
          Subject: PrivateIP
          Message: PrivateIP is '{{GetEC2InstanceIP.PrivateIP}}' | UserName is '{{UserName}}' |
          TopicArn: '{{IdentifyTopicsonSNS.SNSTopicArn}}'
    

    苦戦した点

    各ステップからステップへ値を渡す際にはoutputとして出力する必要があり、APIを使用するステップでは出力の仕方がわからず苦戦しました。 例えばサンプルコードのIdentifyTopicsonSNS、GetEC2InstanceIPのステップです。 実行するAPIごとに出力の仕方が決まっているのでBoto3のドキュメントを参照して解決できました。

    Boto3 1.40.16 documentation

    例えば「⑤インスタンスのIP取得」ではApi: DescribeInstancesを使用しているので、EC2>describe_instances>Response Syntaxを見ると、プライベートIPの型と出力方法を知ることが出来ます。

    感想

    今回ははじめてSSM Automationを自作してみました。 アクションは様々なものが用意されており、非常に汎用性の高いサービスだなと感じました。 はじめは書き方などわからないことも多かったのですが、公式ドキュメントなどを参照しながら完成することが出来ました。 同じく書き方に戸惑っている人の助けに少しでもなればうれしいです。 今回作成したものはユーザーネームを入力する仕様ですが、SSM Automationを実行するユーザーの情報を抽出してよりセキュアにもできそうです。