How do I stop my Amazon EC2 Windows instance from signaling back as CREATE_COMPLETE before the instance finishes bootstrapping?

4 minuti di lettura
0

I try to bootstrap an Amazon Elastic Compute Cloud (Amazon EC2) Windows instance with the cfn-init and cfn-signal helper scripts in AWS CloudFormation. However, the cfn-init script signals back before the instance finishes bootstrapping.

Short description

In an Amazon EC2 Windows instance, the Ec2ConfigService process invokes the UserData scripts. Then, UserData invokes cfn-init.exe so that cfn-init runs as a child process of Ec2ConfigService.

Your instance might signal back as CREATE_COMPLETE for the following reasons:

  • If one of the steps in cfn-init.exe requires a system reboot, then the system might shut down and return to the Ec2ConfigService process. The system continues to process the UserData script, and then runs cfn-signal.exe to signal back to AWS CloudFormation.
  • The cfn-signal.exe file doesn't signal back after a reboot because the UserData script runs once.

In the following code examples, cfn-signal.exe is invoked directly from the UserData script. If the cfn-init.exe process performs a reboot, then the cfn-signal.exe command can't be invoked because the UserData script runs once.

JSON example:

"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 example:

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>

Resolution

  1. Use configsets in the cfn-init Metadata section of your template to separate the configurations that that require a reboot from those that don't.

  2. Move cfn-signal.exe from the UserData section of the AWS::EC2::Instance or AWS::AutoScaling::LaunchConfiguration resource to the AWS::CloudFormation::Init Metadata section of the template.

  3. Run cfn-signal.exe as the last command run by the last configset.

  4. In your JSON or YAML template, change UserData to cfn-init and specify the ascending configset.
    JSON example:

    {
      "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 example:

    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

    In the preceding templates, the signal doesn't run in UserData, and you can't retrieve the exit code provided by the cfn-init process. By default, CloudFormation fails to create or update a stack without a signal from UserData or Metadata. Then, the stack returns a "timeout exceeded" error.
    Tip: To troubleshoot any failures, use the logs at c:\cfn\log in the Windows instance.

  5. Set the waitAfterCompletion parameter to forever.
    Note: The default value of waitAfterCompletion is 60 seconds. If you change the value to forever, then cfn-init exits and resumes only after the reboot is complete.

Related information

Bootstrapping AWS CloudFormation Windows stacks

AWS UFFICIALE
AWS UFFICIALEAggiornata 12 giorni fa