ブートストラップを完了する前の Amazon EC2 Windows インスタンスが、 CREATE_COMPLETE シグナルを返さないようにするにはどうしたらよいですか?

最終更新日: 2020 年 2 月 11 日

AWS CloudFormation のヘルパースクリプトである cfn-init (cfn-init.exe) と cfn-signal (cfn-signal.exe) を使用して、Amazon Elastic Compute Cloud (Amazon EC2) Windows インスタンスをブートストラップしています。cfn-init スクリプトのシグナルが早く返されすぎます。さらに、この Windows インスタンスがブートストラップを完了する前に、AWS CloudFormation において CREATE_COMPLETE としてマークされてしまいます。

簡単な説明

Windows インスタンスでは、UserData スクリプトを Ec2ConfigService プロセスが実行します。UserData が cfn-init.exe を呼び出します。これは Ec2ConfigService の子プロセスとして実行されます。

次の理由により、Windows インスタンスが CREATE_COMPLETE としてシグナルを返している可能性があります。

  • cfn-init.exe で実行される 1 段階処理でシステムの再起動を必要とする場合、システムはシャットダウンしてから、実行を Ec2ConfigService プロセスに返すことができます。システムは UserData スクリプトの処理を続行してから cfn-signal.exe を実行し、AWS CloudFormation にシグナルを返します。
  • UserData は 1 度しか実行されないため、再起動後に cfn-signal がシグナルを送り返しません。

以下のコード例では、cfn-signal.exe は UserData から直接呼び出されます。cfn-init.exe プロセスが再起動した場合、UserData は 1 度しか実行されないため、cfn-signal.exe コマンドを呼び出すことはできません。

JSON の例:

"UserData": {
  "Fn::Base64": {
    "Fn::Join": [
      "",
      [
        "<script>\n",
        "cfn-init.exe -v -s ",
        {
          "Ref": "AWS::StackId"
        },
        " -r WindowsInstance",
        " --configsets ascending",
        " --region ",
        {
          "Ref": "AWS::Region"
        },
        "\n",
        "cfn-signal.exe -e %ERRORLEVEL% --stack ",
        {
          "Ref": "AWS::StackId"
        },
        " --resource WindowsInstance --region ",
        {
          "Ref": "AWS::Region"
        },
        "\n",
        "</script>"
      ]
    ]
  }
}

YAML の例:

UserData:
  Fn::Base64: !Sub |
    <script>
    cfn-init.exe -v -s ${AWS::StackId} -r WindowsInstance --configsets ascending --region ${AWS::Region}
    cfn-signal.exe -e %ERRORLEVEL% --stack ${AWS::StackId} --resource WindowsInstance --region ${AWS::Region}
    </script>

解決方法

1.    テンプレートの cfn-init Metadata セクションで configsets を使用して、再起動が必要な設定と再起動が不要な設定を分離します。

2.    AWS::EC2::Instance または AWS::AutoScaling::LaunchConfiguration のリソースにある UserData セクションから、テンプレートの AWS::CloudFormation::Init Metadata セクションに cfn-signal.exe を移動します。

3.    最後の configset が実行した最後のコマンドとして cfn-signal.exe を実行します。

4.    JSON または YAML テンプレートで、UserDatacfn-init に変更し、昇順の configset を指定します。

JSON の例:

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "cfn-init example using configsets",
  "Parameters": {
    "AMI": {
      "Type": "AWS::EC2::Image::Id"
    }
  },
  "Resources": {
    "WindowsInstance": {
      "Type": "AWS::EC2::Instance",
      "Metadata": {
        "AWS::CloudFormation::Init": {
          "configSets": {
            "ascending": [
              "config1",
              "config2",
              "config3"
            ]
          },
          "config1": {
            "files": {
              "C:\\setup\\setenvironment.ps1": {
                "content": {
                  "Fn::Join": [
                    "",
                    [
                      "$Folder = 'C:\\Program Files\\Server\\packages\\bin.20182.18.0826.0815\\'\n",
                      "$OldPath = [System.Environment]::GetEnvironmentVariable('path')\n",
                      "$NewPath = $OldPath + ';' + $Folder\n",
                      "[System.Environment]::SetEnvironmentVariable('path',$NewPath,'Machine')"
                    ]
                  ]
                }
              }
            }
          },
          "config2": {
            "commands": {
              "0-restart": {
                "command": "powershell.exe -Command Restart-Computer",
                "waitAfterCompletion": "forever"
              }
            }
          },
          "config3": {
            "commands": {
              "01-setenvironment": {
                "command": "powershell.exe -ExecutionPolicy Unrestricted C:\\setup\\setenvironment.ps1",
                "waitAfterCompletion": "0"
              },
              "02-signal-resource": {
                "command": {
                  "Fn::Join": [
                    "",
                    [
                      "cfn-signal.exe -e %ERRORLEVEL% --resource WindowsInstance --stack ",
                      {
                        "Ref": "AWS::StackName"
                      },
                      "         --region ",
                      {
                        "Ref": "AWS::Region"
                      }
                    ]
                  ]
                }
              }
            }
          }
        }
      },
      "Properties": {
        "ImageId": {
          "Ref": "AMI"
        },
        "InstanceType": "t2.medium",
        "UserData": {
          "Fn::Base64": {
            "Fn::Join": [
              "",
              [
                "<script>\n",
                "cfn-init.exe -v -s ",
                {
                  "Ref": "AWS::StackId"
                },
                " -r WindowsInstance",
                " --configsets ascending",
                " --region ",
                {
                  "Ref": "AWS::Region"
                },
                "</script>"
              ]
            ]
          }
        }
      },
      "CreationPolicy": {
        "ResourceSignal": {
          "Count": "1",
          "Timeout": "PT30M"
        }
      }
    }
  }
}

YAML の例:

AWSTemplateFormatVersion: '2010-09-09'
Description: cfn-init example using configsets
Parameters:
  AMI:
    Type: 'AWS::EC2::Image::Id'
Resources:
  WindowsInstance:
    Type: 'AWS::EC2::Instance'
    Metadata:
      AWS::CloudFormation::Init:
        configSets:
          ascending:
            - config1
            - config2
            - config3
        config1:
          files:
            C:\setup\setenvironment.ps1:
              content: !Sub |
                $Folder = 'C:\Program Files\Server\packages\bin.20182.18.0826.0815\'
                $OldPath = [System.Environment]::GetEnvironmentVariable('path')
                $NewPath = $OldPath + ';' + $Folder
                [System.Environment]::SetEnvironmentVariable('path',$NewPath,'Machine')
        config2:
          commands:
            0-restart:
              command: powershell.exe -Command Restart-Computer
              waitAfterCompletion: forever
        config3:
          commands:
            01-setenvironment:
              command: powershell.exe -ExecutionPolicy Unrestricted C:\setup\setenvironment.ps1
              waitAfterCompletion: '0'
            02-signal-resource:
                command: !Sub >
                  cfn-signal.exe -e %ERRORLEVEL% --resource WindowsInstance --stack ${AWS::StackName} --region ${AWS::Region}
    Properties:
      ImageId: !Ref AMI
      InstanceType: t2.medium
      UserData:
        Fn::Base64: !Sub |
          <script>
          cfn-init.exe -v -s ${AWS::StackId} -r WindowsInstance --configsets ascending --region ${AWS::Region}
          </script>
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT30M

前述のテンプレートのようにすると、シグナルは UserData で実行されなくなります。つまり、cfn-init プロセスからの終了コードが取得できなくなります。デフォルトでは、UserData または Metadata からシグナルが受信されない場合、AWS CloudFormation はスタックの作成または更新に失敗します。結果的に、スタックは 「timeout exceeded」エラーを返します。

ヒント: 各種の障害をトラブルシューティングするには、Windows インスタンスの c:\cfn\log にあるログが役立ちます。

5.    waitAfterCompletion パラメータを 永続的に設定します。

注: waitAfterCompletion のデフォルト値は 60 秒です。値を永久に変更すると、cfn-init は終了し、再起動の完了後に再び開始します。