NRIネットコム Blog

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.34.69 documentation

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

感想

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