Comment empêcher l’instance Amazon EC2 Windows d’envoyer le signal CREATE_COMPLETE avant la fin de son démarrage ?

Date de la dernière mise à jour : 11/02/2020

Je démarre une instance Amazon Elastic Compute Cloud (Amazon EC2) en utilisant les scripts d’assistant cfn-init (cfn-init.exe) et cfn-signal (cfn-signal.exe) dans AWS CloudFormation. Mon script cfn-init renvoie un signal plus tôt que prévu, puis, AWS CloudFormation affiche le signal CREATE_COMPLETE pour mon instance Windows avant la fin de son démarrage.

Brève description

Dans une instance Windows, les scripts UserData sont exécutés par le processus Ec2ConfigService. UserData appelle cfn-init.exe qui s'exécute comme processus enfant de Ec2ConfigService.

Votre instance Windows peut envoyer des signaux CREATE_COMPLETE pour les raisons suivantes :

  • Si l'une des étapes exécutées par cfn-init.exe nécessite de redémarrer le système, ce dernier peut s’arrêter, puis relancer le processus Ec2ConfigService. Le système continue de traiter le script UserData, puis exécute cfn-signal.exe, qui envoie des signaux à AWS CloudFormation.
  • Le signal cfn-signal n’est pas envoyé après un redémarrage, car UserData s'exécute une seule fois.

Dans les exemples de code ci-dessous, cfn-signal.exe est appelé directement depuis UserData. Si le processus cfn-init.exe effectue un redémarrage, la commande cfn-signal.exe ne peut pas être appelée, car UserData s'exécute une seule fois.

Exemple de 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>"
      ]
    ]
  }
}

Exemple de 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>

Solution

1.    Utilisez les jeux de configuration de la section Métadonnées de cfn-init pour séparer les configurations qui nécessitent un redémarrage de celles qui ne nécessitent pas un redémarrage.

2.    Transférez cfn-signal.exe de la section UserData de la ressource AWS::EC2::Instance ou AWS::AutoScaling::LaunchConfiguration vers la section Métadonnées d’AWS::CloudFormation::Init du modèle.

3.    Exécutez cfn-signal.exe comme dernière commande exécutée par le dernier jeu de configurations.

4.    Dans votre modèle JSON ou YAML, remplacez UserData par cfn-init et spécifiez le jeu de configuration croissant.

Exemple de 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"
        }
      }
    }
  }
}

Exemple de 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

Dans les modèles précédents, le signal n'est plus en cours d'exécution dans UserData, ce qui signifie que vous ne pouvez pas récupérer le code de sortie fourni par le processus cfn-init. Par défaut, AWS CloudFormation ne peut créer ni mettre à jour la pile si aucun signal n'est reçu de UserData ou de Metadata. La pile renvoie ensuite l'erreur de « timeout exceeded » (délai d’attente dépassé).

Conseil : pour résoudre les problèmes, utilisez les journaux dans c:\cfn\log dans l'instance de Windows.

5.    Affectez au paramètre waitAfterCompletion la valeur forever.

Remarque : la valeur par défaut de waitAfterCompletion est 60 secondes. Si vous remplacez la valeur par forever (indéfiniment), cfn-init se termine et est réexécuté uniquement à la fin du redémarrage.


Cet article vous a-t-il été utile ?

Cette page peut-elle être améliorée ?


Vous avez besoin d’aide ?