Comment arrêter et démarrer des instances Amazon EC2 à des intervalles réguliers avec Lambda ?

Dernière mise à jour : 15/02/2021

Je souhaite réduire mon utilisation d'Amazon Elastic Compute Cloud (Amazon EC2) en arrêtant et en démarrant mes instances EC2 automatiquement. Comment dois-je utiliser AWS Lambda et Amazon CloudWatch Events pour faire cela ?

Brève description

Remarque : cet exemple de configuration est une solution simple. Pour une solution plus robuste, utilisez AWS Instance Scheduler. Pour plus d'informations, consultez Comment arrêter et démarrer mes instances à l'aide d'AWS Instance Scheduler ?

Pour arrêter et démarrer des instances Amazon EC2 à des intervalles réguliers avec Lambda, procédez comme suit :

1.    Créez un rôle d'exécution et une stratégie AWS Identity and Access Management (IAM) personnalisés pour votre fonction Lambda.

2.    Créez des fonctions Lambda pour démarrer et arrêter vos instances EC2.

3.    Testez votre fonction Lambda.

4.    Créez des règles CloudWatch Events qui déclenchent votre fonction selon une planification.
Remarque : Vous pouvez également créer des règles qui se déclenchent lors d'un événement qui se produit dans votre compte AWS.

Vous pouvez également utiliser le modèle AWS CloudFormation fourni à la fin de cet article pour automatiser la procédure.

Résolution

Remarque : Si vous recevez une erreur Erreur Client lors du lancement après avoir terminé la procédure suivante, suivez les étapes décrites dans cet article de dépannage de l'Erreur client lors du lancement.

Obtenez les ID des instances EC2 que vous souhaitez arrêter et démarrer. Puis, procédez comme suit.

Créez une stratégie IAM et un rôle d'exécution pour votre fonction Lambda

1.    Créez une stratégie IAM à l'aide d'un éditeur de stratégie JSON. Copiez et collez le document de stratégie JSON suivant dans l'éditeur de stratégie :

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:Start*",
        "ec2:Stop*"
      ],
      "Resource": "*"
    }
  ]
}

2.    Créez un rôle IAM pour Lambda.

Important : Lorsque vous associez une stratégie d'autorisations à Lambda, assurez-vous de choisir la stratégie IAM que vous venez de créer.

Créez des fonctions Lambda pour démarrer et arrêter vos instances EC2

1.    Dans la console AWS Lambda, choisissez Créer une fonction.

2.    Choisissez Créer à partir de zéro.

3.    Sous Informations de base, procédez comme suit :
Pour Nom de la fonction, saisissez un nom l'identifiant comme la fonction utilisée pour arrêter vos instances EC2. Par exemple, « StopEC2Instances ».
Dans le champ Runtime, choisissez Python 3.8.
Sous Permissions (Autorisations), développez Choose or create an execution role (Choisir ou créer un rôle d'exécution).
Sous Rôle d'exécution, sélectionnez Utiliser un rôle existant.
Sous Rôle existant, choisissez le rôle IAM que vous avez créé plut tôt.

4.    Sélectionnez Créer une fonction.

5.    Sous Function code (Code de fonction), copiez et collez le code suivant dans le volet de l'éditeur de code (lambda_function). Ce code arrête les instances EC2 que vous identifiez.

Exemple de code de fonction : arrêt des instances EC2

import boto3
region = 'us-west-1'
instances = ['i-12345cb6de4f78g9h', 'i-08ce9b2d7eccf6d26']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    ec2.stop_instances(InstanceIds=instances)
    print('stopped your instances: ' + str(instances))

Important : pour région, remplacez « us-west-1 » avec la région AWS dans laquelle se trouve vos instances. Pour instances, remplacez les ID d'instance EC2 d'exemple avec les ID des instances spécifiques que vous souhaitez arrêter et démarrer.

6.    Sous Réglages de base, définissez Expiration à 10 secondes.

Remarque : Configurez les réglages de la fonctions Lambda comme requis pour votre cas d'utilisation. Par exemple, si vous souhaitez arrêter et démarrer plusieurs instances, une valeur différente peut être nécessaire pour Timeout (Expiration) et Memory (Mémoire).

7.    Choisissez Deploy (Déployer).

8.    Répétez les étapes 1-7 pour créer une autre fonction. Procédez différemment pour que cette fonction démarre vos instances EC2 :
À l'étape 3, entrez un Nom de la fonction différent de celui que vous avez utilisé précédemment. Par exemple, « StartEC2Instances ».
À l'étape 5, copiez et collez le code suivant dans le volet de modification dans l'éditeur de code (lambda_function) :

Exemple de code de fonction : démarrage d'instances EC2

import boto3
region = 'us-west-1'
instances = ['i-12345cb6de4f78g9h', 'i-08ce9b2d7eccf6d26']
ec2 = boto3.client('ec2', region_name=region)

def lambda_handler(event, context):
    ec2.start_instances(InstanceIds=instances)
    print('started your instances: ' + str(instances))

Important : Pour region (région) et instances, utilisez les mêmes valeurs que celles utilisées pour le code d'arrêt de vos instances EC2.

Testez vos fonctions Lambda

1.    Dans la console AWS Lambda, choisissez Fonctions.

2.    Choisissez l'une des fonctions que vous avez créées.

3.    Choisissez Test.

4.    Dans la boîte de dialogue Configure test event (Configurer un événement de test), choisissez Create new test event (Créer un événement de test).

5.    Entrez un Nom d'événement. Ensuite, choisissez Create (Créer).

Remarque : Vous n'avez pas à changer le code JSON pour l'événement de test ; la fonction ne l'utilise pas.

6.    Choisissez Test pour exécuter la fonction.

7.    Répétez les étapes 1 à 6 pour l'autre fonction créée.

Astuce : Vous pouvez vérifier l'état de vos instances EC2 avant et après avoir effectué le test pour confirmer que vos fonctions fonctionnent comme prévu.

Créez des règles d'événement CloudWatch qui déclenchent vos fonctions Lambda

1.    Ouvrez la console Amazon CloudWatch.

2.    Dans le panneau de navigation de gauche, sous Événements, choisissez Règles.

3.    Choisissez Create rule (Créer une règle).

4.    Sous Source de l'événement, choisissez Programme.

5.    Effectuez l'une ou l'autre des opérations suivantes :
Pour le champ Taux fixe de, saisissez un intervalle de temps en minutes, heures ou jours.
Pour Cron expression (Expression Cron), saisissez une expression qui indique à Lambda quand arrêter vos instances. Pour plus d'informations sur la syntaxe des expressions, consultez Expressions de planification des règles.
Remarque
 : Les expressions Cron sont évaluées selon l'heure UTC. Assurez-vous d'ajuster l'expression pour le fuseau horaire de votre choix.

6.    Sous Cibles, choisissez Ajouter une cible.

7.    Choisissez Fonction Lambda.

8.    Pour Fonction, choisissez la fonction qui arrête vos instances EC2.

9.    Sélectionnez Configure details.

10.    Sous Définition de la règle, procédez comme suit :
Pour Nom, saisissez un nom pour identifier la règle, telle que « StopEC2Instances ».
(Facultatif) Pour Description, décrivez votre règle. Par exemple, « Arrête les instances EC2 chaque nuit à 22 h. »
Pour State (État), activez la case à cocher Enabled (Activé).

11.    Choisissez Create rule (Créer une règle).

12.    Répétez les étapes 1 à 11 pour créer une règle pour démarrer vos instances EC2. Procédez comme suit :
À l'étape 5, pour le champ Expression Cron, saisissez une expression qui indique à Lambda quand démarrer vos instances.
À l'étape 8, pour le champ Function (Fonction), choisissez la fonction qui démarre vos instances EC2.
À l'étape 10, sous Rule definition (Définition de règle), entrez un Nom, tel que « StartEC2Instances ».
(Facultatif) Entrez une description, telle que « Démarre les instances EC2 tous les matins à 7 heures du matin. »

( Facultatif) Utiliser un modèle AWS CloudFormation pour automatiser la procédure

1.    Enregistrez le modèle AWS CloudFormation suivant en tant que fichier YAML.

2.    Dans le fichier YAML, entrez les variables requises.

3.    Déployez le modèle AWS CloudFormation dans Visual Studio ou AWS Command Line Interface (AWS CLI).

Remarque : Si vous recevez des erreurs lors de l'exécution de commandes depuis l'interface de ligne de commande AWS CLI, assurez-vous d'utiliser la version la plus récente de l'interface AWS CLI.

Pour plus d'informations sur l'utilisation du modèle, consultez Utilisation des modèles AWS CloudFormation.

Modèle AWS CloudFormation pour arrêter et démarrer des instances EC2 à intervalles réguliers avec Lambda

AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda function with cfn-response.
Parameters: 
  instances: 
    Default: i-12345678
    Description: Instance ID's seperated by commers 
    Type: String
  Region: 
    Description: region only 1 region supported 
    Type: String
  StopScheduled: 
    Default: cron(0 18 ? * MON-FRI *)
    Description: enter an Schedule expression example cron(0 18 ? * MON-FRI *) see https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html
    Type: String
  StartScheduled: 
    Default: cron(0 7 ? * MON-FRI *)
    Description: enter an Schedule expression example cron(0 7 ? * MON-FRI * ) see https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html
    Type: String
Resources:
  StopEC2Instances:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.8
      Role: !GetAtt Role.Arn
      Handler: index.lambda_handler
      Timeout: 60
      Environment:
         Variables:
          instances: !Ref instances
          Region: !Ref Region
      Code:
        ZipFile: |
          import json
          import re
          import os
          import boto3
          
          def lambda_handler(event, context):
            # TODO implement
            instances_str = os.environ['instances']
            region = os.environ['Region']
            ec2 = boto3.client('ec2', region_name=region)
            instances= re.findall(r"i-[0-9a-z]{17}|i-[0-9a-z]{8}", instances_str)
            print('stopped your instances: ' + str(instances) + "in Region "+ region)
            ec2.stop_instances(InstanceIds=instances)
            
            return {
              'statusCode': 200,
              'body': json.dumps('stopped your instances: ' + str(instances))
            }
      Description: Function that stops instances
  permissionForEventsToInvokeStopEC2Instances:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt StopEC2Instances.Arn
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      "SourceArn" : !GetAtt StopScheduledRule.Arn

  StartEC2Instances:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.8
      Role: !GetAtt Role.Arn
      Handler: index.lambda_handler
      Timeout: 60
      Environment:
         Variables:
          instances: !Ref instances
          Region: !Ref Region
      Code:
        ZipFile: |
          import json
          import re
          import os
          import boto3
          
          def lambda_handler(event, context):
            # TODO implement
            instances_str = os.environ['instances']
            region = os.environ['Region']
            ec2 = boto3.client('ec2', region_name=region)
            instances= re.findall(r"i-[0-9a-z]{17}|i-[0-9a-z]{8}", instances_str)
            print('started your instances: ' + str(instances)+ "in Region "+ region)
            ec2.start_instances(InstanceIds=instances)
            
            return {
              'statusCode': 200,
              'body': json.dumps('started your instances: ' + str(instances))
            }
      Description: Function that started instances
  permissionForEventsToInvokeStartEC2Instances:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt StartEC2Instances.Arn
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      "SourceArn" : !GetAtt StartScheduledRule.Arn

  Role:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      Policies:
        - PolicyName: Ec2permissions
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                - "ec2:StartInstances"
                - "ec2:StopInstances"
                Resource: '*'
                

  StopScheduledRule: 
    Type: AWS::Events::Rule
    Properties: 
      Description: "ScheduledRule"
      ScheduleExpression: !Ref StopScheduled
      State: "ENABLED"
      Targets: 
        - 
          Arn: !GetAtt StopEC2Instances.Arn
          Id: "TargetFunctionV1"
  StartScheduledRule: 
    Type: AWS::Events::Rule
    Properties: 
      Description: "ScheduledRule"
      ScheduleExpression: !Ref StartScheduled
      State: "ENABLED"
      Targets: 
        - 
          Arn: !GetAtt StartEC2Instances.Arn
          Id: "TargetFunctionV1"