Aufrufen einer AWS Lambda-Funktion in einer Pipeline in CodePipeline - AWS CodePipeline

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Aufrufen einer AWS Lambda-Funktion in einer Pipeline in CodePipeline

AWS Lambda ist ein Datenverarbeitungsservice, mit dem Sie Code ausführen können, ohne Server bereitstellen oder verwalten zu müssen. Sie können Lambda-Funktionen erstellen und sie als Aktionen zu Ihren Pipelines hinzufügen. Da Sie mit Lambda Funktionen für fast jede Aufgabe schreiben können, können Sie die Funktionsweise Ihrer Pipeline anpassen.

Wichtig

Protokollieren Sie nicht das JSON-Ereignis, dasCodePipeline an Lambda gesendet wird, da dies dazu führen kann, dass Benutzeranmeldeinformationen inCloudWatch Logs protokolliert werden. DieCodePipeline Rolle verwendet ein JSON-Ereignis, um temporäre Anmeldeinformationen an Lambda imartifactCredentials Feld zu übergeben. Ein Beispiel für ein Ereignis finden Sie unter JSON-Beispielereignis.

Hier sind einige Möglichkeiten, wie Lambda-Funktionen in Pipelines verwendet werden können:

  • Bedarfsabhängiges Erstellen von Ressourcen in einer Phase einer Pipeline mithilfe von AWS CloudFormation und zum Löschen in einer anderen Phase

  • Um Anwendungsversionen ohne Ausfallzeiten bereitzustellen, verwenden Sie eine Lambda-Funktion, die CNAME-Werte austauscht.AWS Elastic Beanstalk

  • Zur Bereitstellung auf Amazon ECS Docker-Instances.

  • Sichern von Ressourcen mithilfe eines AMI-Snapshots vor dem Erstellen oder Bereitstellen

  • Integrieren von Drittanbieterprodukten in Ihre Pipeline, wie beispielsweise das Versenden von Nachrichten an einen IRC-Client

Anmerkung

Durch das Erstellen und Ausführen von Lambda-Funktionen können Kosten für IhrAWS Konto anfallen. Weitere Informationen finden Sie unter -Preisgestaltung.

In diesem Thema wird vorausgesetzt, dass Sie mit Pipelines, FunktionenAWS CodePipelineAWS Lambda und den IAM-Richtlinien und -Rollen, von denen sie abhängen, vertraut sind und wissen, wie man sie erstellt. In diesem Thema wird Folgendes veranschaulicht:

  • Erstellen Sie eine Lambda-Funktion, die testet, ob eine Webseite erfolgreich bereitgestellt wurde.

  • Konfigurieren Sie dieCodePipeline und Lambda-Ausführungsrollen sowie die Berechtigungen, die erforderlich sind, um die Funktion als Teil der Pipeline auszuführen.

  • Bearbeiten Sie eine Pipeline, um die Lambda-Funktion als Aktion hinzuzufügen.

  • Testen der Aktion durch manuelles Veröffentlichen einer Änderung

Anmerkung

Wenn Sie die regionsübergreifende Lambda-Aufrufaktion in verwendenCodePipeline, PutJobFailureResultsollte der Status der Lambda-Ausführung mit dem PutJobSuccessResultund an dieAWS Region gesendet werden, in der die Lambda-Funktion vorhanden ist, und nicht an die Region, in der sieCodePipeline vorhanden ist.

Dieses Thema enthält Beispielfunktionen, um die Flexibilität der Arbeit mit Lambda-Funktionen in folgenden Bereichen zu demonstrierenCodePipeline:

  • Basic Lambda function

    • Erstellen einer grundlegenden Lambda-Funktion zur VerwendungCodePipeline.

    • Zurücksenden von Erfolgs- oder Fehlerergebnissen an CodePipeline im Link Details der Aktion.

  • Beispiel einer Python-Funktion, die eine AWS CloudFormation-Vorlage verwendet

    • Verwenden von JSON-verschlüsselten Benutzerparametern für die Weiterleitung mehrerer Konfigurationswerte an die Funktion (get_user_params)

    • Interaktion mit ZIP-Artefakten in einem Artefakt-Bucket (get_template)

    • Verwenden eines Fortsetzungstokens, um einen lange andauernden asynchronen Prozess zu überwachen (continue_job_later). Dadurch kann die Aktion fortgesetzt werden und die Funktion kann erfolgreich sein, auch wenn sie eine Laufzeit von fünfzehn Minuten überschreitet (ein Limit in Lambda).

Jede Beispielfunktion beinhaltet Informationen zu den Berechtigungen, die Sie der Rolle hinzufügen müssen. Informationen zu Einschränkungen in AWS Lambda finden Sie unter Einschränkungen im AWS Lambda-Entwicklerhandbuch.

Wichtig

Beispiel-Code, Rollen und Richtlinien, die in diesem Thema enthalten sind, dienen lediglich als Beispiele und werden unverändert bereitgestellt.

Schritt 1: Erstellen einer Pipeline

In diesem Schritt erstellen Sie eine Pipeline, zu der Sie später die Lambda-Funktion hinzufügen. Das ist dieselbe Pipeline, die Sie unter CodePipeline -Tutorials erstellt haben. Wenn diese Pipeline immer noch für Ihr Konto konfiguriert ist und sich in derselben Region befindet, in der Sie die Lambda-Funktion erstellen möchten, können Sie diesen Schritt überspringen.

So erstellen Sie die Pipeline
  1. Folgen Sie den ersten drei Schritten unterTutorial: Erstellen einer einfachen Pipeline (S3-Bucket), um einen Amazon S3 S3-Bucket,CodeDeploy Ressourcen und eine zweistufige Pipeline zu erstellen. Wählen Sie die Amazon Linux-Option für Ihre Instanztypen. Sie können einen beliebigen Namen für die Pipeline verwenden. Die Schritte in diesem Thema verwenden den Namen MyLambdaTestPipeline.

  2. Wählen Sie auf der Statusseite für Ihre Pipeline in der CodeDeploy-Aktion Details aus. Wählen Sie auf der Seite zu den Details der Bereitstellungsgruppe eine Instance-ID aus der Liste aus.

  3. Kopieren Sie in der Amazon EC2 EC2-Konsole auf der Registerkarte Details für die Instance die IP-Adresse in das Feld Öffentliche IPv4-Adresse (z. B.192.0.2.4). Sie verwenden diese Adresse als Ziel der Funktion in AWS Lambda.

Anmerkung

Die standardmäßige Servicerollenrichtlinie fürCodePipeline beinhaltet die Lambda-Berechtigungen, die zum Aufrufen der Funktion erforderlich sind. Sollten Sie jedoch die standardmäßige Servicerolle geändert oder eine andere Rolle ausgewählt haben, stellen Sie sicher, dass die Richtlinie für die Rolle die Berechtigungen für lambda:InvokeFunction und lambda:ListFunctions zulässt. Andernfalls schlagen Pipelines, die Lambda-Aktionen enthalten, fehl.

Schritt 2: Erstellen einer Lambda-Funktion

In diesem Schritt erstellen Sie eine Lambda-Funktion, die eine HTTP-Anfrage stellt und auf einer Webseite nach einer Textzeile sucht. Im Rahmen dieses Schritts müssen Sie auch eine IAM-Richtlinie und eine Lambda-Ausführungsrolle erstellen. Weitere Informationen finden Sie im Berechtigungsmodell im AWS Lambda-Entwicklerhandbuch.

So erstellen Sie eine Ausführungsrolle
  1. Melden Sie sich bei der AWS Management Console an, und öffnen Sie die IAM-Konsole unter https://console.aws.amazon.com/iam/.

  2. Wählen Sie Policies aus und wählen Sie dann Create Policy aus. Wählen Sie die Registerkarte JSON aus und kopieren Sie dann die folgende JSON-Richtlinie in das Feld.

    { "Version": "2012-10-17", "Statement": [ { "Action": [ "logs:*" ], "Effect": "Allow", "Resource": "arn:aws:logs:*:*:*" }, { "Action": [ "codepipeline:PutJobSuccessResult", "codepipeline:PutJobFailureResult" ], "Effect": "Allow", "Resource": "*" } ] }
  3. Wählen Sie Review policy (Richtlinie prüfen).

  4. Geben Sie auf der Seite Review policy (Richtlinie prüfen) unter Name einen Namen für die Richtlinie ein (z. B. CodePipelineLambdaExecPolicy). Geben Sie unter Description (Beschreibung) Folgendes ein: Enables Lambda to execute code.

    Wählen Sie Create Policy (Richtlinie erstellen) aus.

    Anmerkung

    Dies sind die Mindestberechtigungen, die erforderlich sind, damit eine Lambda-Funktion mitCodePipeline Amazon interagieren kannCloudWatch. Wenn Sie diese Richtlinie erweitern möchten, um Funktionen zuzulassen, die mit anderenAWS Ressourcen interagieren, sollten Sie diese Richtlinie ändern, um die für diese Lambda-Funktionen erforderlichen Aktionen zuzulassen.

  5. Wählen Sie auf der Seite mit dem Richtlinien-Dashboard Roles (Rollen) aus und wählen Sie dann Create role (Rolle erstellen) aus.

  6. Wählen Sie auf der Seite „Rolle erstellenAWS-Service. Wählen Sie Lambda und klicken Sie dann auf Next: Permissions (Weiter: Berechtigungen).

  7. Aktivieren Sie auf der Seite Attach permissions policies (Berechtigungsrichtlinien anfügen) das Kontrollkästchen neben CodePipelineLambdaExecPolicy und wählen Sie dann Next: Tags (Weiter: Tags) aus. Wählen Sie Weiter: Prüfen aus.

  8. Geben Sie auf der Seite Review (Überprüfen) unter Role name (Rollenname) den Namen ein. Klicken Sie dann auf Create role (Rolle erstellen).

So erstellen Sie die Lambda-Beispielfunktion, die mit verwendet werden sollCodePipeline
  1. Melden Sie sich bei der AWS Management Console an und öffnen Sie die AWS Lambda-Konsole an https://console.aws.amazon.com/lambda.

  2. Klicken Sie auf der Seite Functions (Funktionen) auf Create function (Funktion erstellen).

    Anmerkung

    Wenn Sie anstelle der Lambda-Seite eine Willkommensseite sehen, wählen Sie Get Started Now.

  3. Wählen Sie auf der Seite Create function die Option Author from scratch. Geben Sie im Feld Funktionsname einen Namen für Ihre Lambda-Funktion ein (z. B.MyLambdaFunctionForAWSCodePipeline). Wählen Sie unter Runtime Node.js 14.x aus.

  4. Wählen Sie für Role (Rolle) die Option Choose an existing role (Eine vorhandene Rolle wählen) aus. Wählen Sie Ihre Rolle unter Existing role (Vorhandene Rolle) aus und klicken Sie dann auf Create function (Funktion erstellen).

    Die Detailseite für Ihre erstellte Funktion wird geöffnet.

  5. Fügen Sie den folgenden Code in das Feld Function code (Funktionscode) ein:

    Anmerkung

    Das Ereignisobjekt unter demCodePipeline Schlüssel.job enthält die Jobdetails. Ein vollständiges Beispiel für dieCodePipeline Rückkehr des JSON-Ereignisses zu Lambda finden Sie unterJSON-Beispielereignis.

    var assert = require('assert'); var AWS = require('aws-sdk'); var http = require('http'); exports.handler = function(event, context) { var codepipeline = new AWS.CodePipeline(); // Retrieve the Job ID from the Lambda action var jobId = event["CodePipeline.job"].id; // Retrieve the value of UserParameters from the Lambda action configuration in CodePipeline, in this case a URL which will be // health checked by this function. var url = event["CodePipeline.job"].data.actionConfiguration.configuration.UserParameters; // Notify CodePipeline of a successful job var putJobSuccess = function(message) { var params = { jobId: jobId }; codepipeline.putJobSuccessResult(params, function(err, data) { if(err) { context.fail(err); } else { context.succeed(message); } }); }; // Notify CodePipeline of a failed job var putJobFailure = function(message) { var params = { jobId: jobId, failureDetails: { message: JSON.stringify(message), type: 'JobFailed', externalExecutionId: context.awsRequestId } }; codepipeline.putJobFailureResult(params, function(err, data) { context.fail(message); }); }; // Validate the URL passed in UserParameters if(!url || url.indexOf('http://') === -1) { putJobFailure('The UserParameters field must contain a valid URL address to test, including http:// or https://'); return; } // Helper function to make a HTTP GET request to the page. // The helper will test the response and succeed or fail the job accordingly var getPage = function(url, callback) { var pageObject = { body: '', statusCode: 0, contains: function(search) { return this.body.indexOf(search) > -1; } }; http.get(url, function(response) { pageObject.body = ''; pageObject.statusCode = response.statusCode; response.on('data', function (chunk) { pageObject.body += chunk; }); response.on('end', function () { callback(pageObject); }); response.resume(); }).on('error', function(error) { // Fail the job if our request failed putJobFailure(error); }); }; getPage(url, function(returnedPage) { try { // Check if the HTTP response has a 200 status assert(returnedPage.statusCode === 200); // Check if the page contains the text "Congratulations" // You can change this to check for different text, or add other tests as required assert(returnedPage.contains('Congratulations')); // Succeed the job putJobSuccess("Tests passed."); } catch (ex) { // If any of the assertions failed then fail the job putJobFailure(ex); } }); };
  6. Behalten Sie für Handler den Standardwert und für Role (Rolle) die Standardrolle CodePipelineLambdaExecRole bei.

  7. Geben Sie unter Basic settings (Grundlegende Einstellungen) für Timeout den Wert 20 Sekunden ein.

  8. Wählen Sie Speichern.

Schritt 3: Fügen Sie die Lambda-Funktion zu einer Pipeline in derCodePipeline Konsole hinzu

In diesem Schritt fügen Sie Ihrer Pipeline eine neue Stufe hinzu und fügen dann eine Lambda-Aktion hinzu, die Ihre Funktion zu dieser Phase aufruft.

So fügen Sie eine Stufe hinzu
  1. Melden Sie sich bei der anAWS Management Console und öffnen Sie dieCodePipeline Konsole unter http://console.aws.amazon.com/codesuite/codepipeline/home.

  2. Wählen Sie auf der Seite Welcome (Willkommen) die von Ihnen erstellte Pipeline aus.

  3. Wählen Sie auf der Pipeline-Seite Edit aus.

  4. Wählen Sie auf der Seite Edit (Bearbeiten) die Option + Add stage (Stufe hinzufügen) zum Hinzufügen einer Stufe nach der Bereitstellungsstufe mit der CodeDeploy-Aktion aus. Geben Sie einen Namen für die Stufe ein (z. B. LambdaStage) und wählen Sie Add stage (Stufe hinzufügen).

    Anmerkung

    Sie haben auch die Möglichkeit, Ihre Lambda-Aktion einer vorhandenen Phase hinzuzufügen. Zu Demonstrationszwecken fügen wir die Lambda-Funktion als einzige Aktion in einer Phase hinzu, damit Sie ihren Fortschritt einfach verfolgen können, wenn Artefakte eine Pipeline durchlaufen.

  5. Wählen Sie + Add action group (Aktionsgruppe hinzufügen). Geben Sie unter Aktion bearbeiten unter Aktionsname einen Namen für Ihre Lambda-Aktion ein (z. B.MyLambdaAction). Wählen Sie in Provider (Anbieter) AWS Lambda aus. Wählen oder geben Sie im Feld Funktionsname den Namen Ihrer Lambda-Funktion ein (z. B.MyLambdaFunctionForAWSCodePipeline). Geben Sie unter Benutzerparameter die IP-Adresse für die Amazon EC2 EC2-Instance an, die Sie zuvor kopiert haben (z. B.http://192.0.2.4), und wählen Sie dann Fertig.

    Anmerkung

    Dieses Thema verwendet eine IP-Adresse. In echten Szenarien sollten Sie jedoch Ihren registrierten Website-Namen verwenden (z. B. http://www.example.com). Weitere Informationen zu Ereignisdaten und -handlern in AWS Lambda finden Sie unter Programmiermodell im AWS LambdaEntwicklerhandbuch.

  6. Wählen Sie auf der Seite Edit action (Aktion bearbeiten) die Option Save (Speichern).

Schritt 4: Testen der Pipeline mit der Lambda-Funktion

Um die Funktion zu testen, führen Sie die aktuelleste Änderung über die Pipeline aus.

So verwenden Sie die Konsole zum Ausführen der letzten Version eines Artefaktes in einer Pipeline
  1. Wählen Sie auf der Seite mit den Pipeline-Details die Option Änderung freigeben aus. Dadurch wird die letzte Revision gestartet, die in jedem in einer Quellaktion der Pipeline angegebenen Quellspeicherort vorhanden ist.

  2. Wenn die Lambda-Aktion abgeschlossen ist, wählen Sie den Link Details, um den Log-Stream für die Funktion in Amazon anzuzeigenCloudWatch, einschließlich der in Rechnung gestellten Dauer des Ereignisses. Wenn die Funktion fehlgeschlagen ist, stellt das CloudWatch-Protokoll Informationen zur Ursache bereit.

Schritt 5: Nächste Schritte

Nachdem Sie erfolgreich eine Lambda-Funktion erstellt und sie als Aktion in einer Pipeline hinzugefügt haben, können Sie Folgendes ausprobieren:

  • Fügen Sie Ihrer Bühne weitere Lambda-Aktionen hinzu, um andere Webseiten zu überprüfen.

  • Ändern Sie die Lambda-Funktion, um nach einer anderen Textzeichenfolge zu suchen.

  • Erkunden Sie Lambda-Funktionen und erstellen Sie Ihre eigenen Lambda-Funktionen und fügen Sie sie zu Pipelines hinzu.


                EineAWS Lambda Aktion, die durch eine Pipeline läuft.

Nachdem Sie das Experimentieren mit der Lambda-Funktion abgeschlossen haben, sollten Sie erwägen, sie aus Ihrer Pipeline zu entfernenAWS Lambda, sie aus und die Rolle aus IAM zu löschen, um mögliche Gebühren zu vermeiden. Weitere Informationen finden Sie unter Bearbeiten einer Pipeline in CodePipeline, Löschen einer Pipeline in CodePipeline und Löschen von Rollen oder Instance-Profilen.

JSON-Beispielereignis

Das folgende Beispiel zeigt ein Beispiel für ein JSON-Ereignis, das von an Lambda gesendet istCodePipeline. Die Struktur dieses Ereignisses ähnelt der Antwort unter GetJobDetails API, aber ohne den actionTypeId- und pipelineContext-Datentyp. Zwei Aktionskonfigurationsdetails, FunctionName und UserParameters, sind sowohl im JSON-Ereignis als auch in der Antwort auf die GetJobDetails-API enthalten. Die Werte in roter Kursivschrift sind Beispiele oder Erklärungen, keine realen Werte.

{ "CodePipeline.job": { "id": "11111111-abcd-1111-abcd-111111abcdef", "accountId": "111111111111", "data": { "actionConfiguration": { "configuration": { "FunctionName": "MyLambdaFunctionForAWSCodePipeline", "UserParameters": "some-input-such-as-a-URL" } }, "inputArtifacts": [ { "location": { "s3Location": { "bucketName": "the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890", "objectKey": "the name of the application, for example CodePipelineDemoApplication.zip" }, "type": "S3" }, "revision": null, "name": "ArtifactName" } ], "outputArtifacts": [], "artifactCredentials": { "secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "sessionToken": "MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w 0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZ WF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIw EAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5 jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBh MCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBb WF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMx HzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQE BBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVI k60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQ ITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nr AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auN KyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6Guo EDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw 3rrszlaEXAMPLE=", "accessKeyId": "AKIAIOSFODNN7EXAMPLE" }, "continuationToken": "A continuation token if continuing job", "encryptionKey": { "id": "arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab", "type": "KMS" } } } }

Weitere Beispielfunktionen

Die folgenden Lambda-Beispielfunktionen demonstrieren zusätzliche Funktionen, die Sie für Ihre Pipelines verwenden könnenCodePipeline. Um diese Funktionen verwenden zu können, müssen Sie möglicherweise die Richtlinie für die Lambda-Ausführungsrolle ändern, wie in der Einführung für jedes Beispiel beschrieben.

Beispiel einer Python-Funktion, die eine AWS CloudFormation-Vorlage verwendet

Im folgenden Beispiel wird eine Funktion gezeigt, die einen Stack auf Grundlage einer bereitgestellten AWS CloudFormation-Vorlage erstellt. Die Vorlage erstellt einen Amazon S3 S3-Bucket. Dies erfolgt nur zu Demonstrationszwecken, um Kosten zu sparen. Im Idealfall sollten Sie den Stack löschen, bevor Sie Inhalte in den Bucket hochladen. Falls Sie Dateien in den Bucket hochladen, können Sie den Bucket nicht mehr löschen, wenn Sie den Stack löschen. In diesem Fall müssen Sie alle Inhalte des Buckets manuell löschen, bevor Sie den Bucket selbst löschen können.

In diesem Python-Beispiel wird davon ausgegangen, dass Sie über eine Pipeline verfügen, die einen Amazon S3 S3-Bucket als Quellaktion verwendet, oder dass Sie Zugriff auf einen versionierten Amazon S3 S3-Bucket haben, den Sie mit der Pipeline verwenden können. Sie erstellen die AWS CloudFormation-Vorlage, komprimieren sie und laden Sie als ZIP-Datei in den Bucket hoch. Anschließend müssen Sie Ihrer Pipeline eine Quellaktion hinzufügen, welche die ZIP-Datei aus dem Bucket abruft.

Anmerkung

Wenn Amazon S3 der Quellanbieter für Ihre Pipeline ist, können Sie Ihre Quelldatei oder Dateien in eine einzelne ZIP-Datei komprimieren und die ZIP-Datei in Ihren Quell-Bucket hochladen. Sie können auch eine einzelne Datei ungezippt hochladen, aber nachgelagerte Aktionen, die eine ZIP-Datei erwarten, schlagen dann fehl.

In diesem Beispiel wird Folgendes gezeigt:

  • Die Verwendung von JSON-verschlüsselten Benutzerparametern für die Weiterleitung mehrerer Konfigurationswerte an die Funktion (get_user_params)

  • Die Interaktion mit ZIP-Artefakten in einem Artefakt-Bucket (get_template)

  • Die Verwendung eines Fortsetzungstokens, um einen lange andauernden asynchronen Prozess zu überwachen (continue_job_later). Dadurch kann die Aktion fortgesetzt werden und die Funktion kann erfolgreich sein, auch wenn sie eine Laufzeit von fünfzehn Minuten überschreitet (ein Limit in Lambda).

Um diese Lambda-Beispielfunktion verwenden zu können, muss die Richtlinie für die Lambda-Ausführungsrolle überAllow Berechtigungen inAWS CloudFormation Amazon S3 verfügen undCodePipeline, wie in dieser Beispielrichtlinie gezeigt, über folgende Berechtigungen verfügen:

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "logs:*" ], "Effect": "Allow", "Resource": "arn:aws:logs:*:*:*" }, { "Action": [ "codepipeline:PutJobSuccessResult", "codepipeline:PutJobFailureResult" ], "Effect": "Allow", "Resource": "*" }, { "Action": [ "cloudformation:DescribeStacks", "cloudformation:CreateStack", "cloudformation:UpdateStack" ], "Effect": "Allow", "Resource": "*" }, { "Action": [ "s3:*" ], "Effect": "Allow", "Resource": "*" } ] }

Um die AWS CloudFormation-Vorlage zu erstellen, öffnen Sie einen Texteditor, kopieren Sie folgenden Code und fügen Sie ihn ein:

{ "AWSTemplateFormatVersion" : "2010-09-09", "Description" : "CloudFormation template which creates an S3 bucket", "Resources" : { "MySampleBucket" : { "Type" : "AWS::S3::Bucket", "Properties" : { } } }, "Outputs" : { "BucketName" : { "Value" : { "Ref" : "MySampleBucket" }, "Description" : "The name of the S3 bucket" } } }

Speichern Sie dies als JSON-Datei mit dem Namen template.json in einem Verzeichnis mit dem Namen template-package. Erstellen Sie eine komprimierte Datei (.zip) mit diesem Verzeichnis und diesem Dateinamentemplate-package.zip und laden Sie die komprimierte Datei in einen versionierten Amazon S3 S3-Bucket hoch. Wenn Sie bereits einen Bucket für Ihre Pipeline konfiguriert haben, können Sie diesen verwenden. Bearbeiten Sie als Nächstes Ihre Pipeline und fügen Sie eine Quellaktion hinzu, welche die ZIP-Datei abruft. Benennen Sie die Ausgabe für diese Aktion MyTemplate. Weitere Informationen finden Sie unter Bearbeiten einer Pipeline in CodePipeline.

Anmerkung

Die Lambda-Beispielfunktion erwartet diese Dateinamen und die komprimierte Struktur. Sie können jedoch Ihre eigene AWS CloudFormation-Vorlage anstelle dieses Beispiels verwenden. Wenn Sie Ihre eigene Vorlage verwenden, stellen Sie sicher, dass Sie die Richtlinie für die Lambda-Ausführungsrolle ändern, um alle zusätzlichen Funktionen zu ermöglichen, die für IhreAWS CloudFormation Vorlage erforderlich sind.

Um den folgenden Code als Funktion in Lambda hinzuzufügen
  1. Öffnen Sie die Lambda-Konsole und wählen Sie Funktion erstellen.

  2. Wählen Sie auf der Seite Create function die Option Author from scratch. Geben Sie im Feld Funktionsname einen Namen für Ihre Lambda-Funktion ein.

  3. Wählen Sie unter Runtime (Laufzeit) die Option Python 2.7 aus.

  4. Wählen Sie unter Ausführungsrolle auswählen oder erstellen die Option Bestehende Rolle verwenden aus. Wählen Sie Ihre Rolle unter Existing role (Vorhandene Rolle) aus und klicken Sie dann auf Create function (Funktion erstellen).

    Die Detailseite für Ihre erstellte Funktion wird geöffnet.

  5. Fügen Sie den folgenden Code in das Feld Function code (Funktionscode) ein:

    from __future__ import print_function from boto3.session import Session import json import urllib import boto3 import zipfile import tempfile import botocore import traceback print('Loading function') cf = boto3.client('cloudformation') code_pipeline = boto3.client('codepipeline') def find_artifact(artifacts, name): """Finds the artifact 'name' among the 'artifacts' Args: artifacts: The list of artifacts available to the function name: The artifact we wish to use Returns: The artifact dictionary found Raises: Exception: If no matching artifact is found """ for artifact in artifacts: if artifact['name'] == name: return artifact raise Exception('Input artifact named "{0}" not found in event'.format(name)) def get_template(s3, artifact, file_in_zip): """Gets the template artifact Downloads the artifact from the S3 artifact store to a temporary file then extracts the zip and returns the file containing the CloudFormation template. Args: artifact: The artifact to download file_in_zip: The path to the file within the zip containing the template Returns: The CloudFormation template as a string Raises: Exception: Any exception thrown while downloading the artifact or unzipping it """ tmp_file = tempfile.NamedTemporaryFile() bucket = artifact['location']['s3Location']['bucketName'] key = artifact['location']['s3Location']['objectKey'] with tempfile.NamedTemporaryFile() as tmp_file: s3.download_file(bucket, key, tmp_file.name) with zipfile.ZipFile(tmp_file.name, 'r') as zip: return zip.read(file_in_zip) def update_stack(stack, template): """Start a CloudFormation stack update Args: stack: The stack to update template: The template to apply Returns: True if an update was started, false if there were no changes to the template since the last update. Raises: Exception: Any exception besides "No updates are to be performed." """ try: cf.update_stack(StackName=stack, TemplateBody=template) return True except botocore.exceptions.ClientError as e: if e.response['Error']['Message'] == 'No updates are to be performed.': return False else: raise Exception('Error updating CloudFormation stack "{0}"'.format(stack), e) def stack_exists(stack): """Check if a stack exists or not Args: stack: The stack to check Returns: True or False depending on whether the stack exists Raises: Any exceptions raised .describe_stacks() besides that the stack doesn't exist. """ try: cf.describe_stacks(StackName=stack) return True except botocore.exceptions.ClientError as e: if "does not exist" in e.response['Error']['Message']: return False else: raise e def create_stack(stack, template): """Starts a new CloudFormation stack creation Args: stack: The stack to be created template: The template for the stack to be created with Throws: Exception: Any exception thrown by .create_stack() """ cf.create_stack(StackName=stack, TemplateBody=template) def get_stack_status(stack): """Get the status of an existing CloudFormation stack Args: stack: The name of the stack to check Returns: The CloudFormation status string of the stack such as CREATE_COMPLETE Raises: Exception: Any exception thrown by .describe_stacks() """ stack_description = cf.describe_stacks(StackName=stack) return stack_description['Stacks'][0]['StackStatus'] def put_job_success(job, message): """Notify CodePipeline of a successful job Args: job: The CodePipeline job ID message: A message to be logged relating to the job status Raises: Exception: Any exception thrown by .put_job_success_result() """ print('Putting job success') print(message) code_pipeline.put_job_success_result(jobId=job) def put_job_failure(job, message): """Notify CodePipeline of a failed job Args: job: The CodePipeline job ID message: A message to be logged relating to the job status Raises: Exception: Any exception thrown by .put_job_failure_result() """ print('Putting job failure') print(message) code_pipeline.put_job_failure_result(jobId=job, failureDetails={'message': message, 'type': 'JobFailed'}) def continue_job_later(job, message): """Notify CodePipeline of a continuing job This will cause CodePipeline to invoke the function again with the supplied continuation token. Args: job: The JobID message: A message to be logged relating to the job status continuation_token: The continuation token Raises: Exception: Any exception thrown by .put_job_success_result() """ # Use the continuation token to keep track of any job execution state # This data will be available when a new job is scheduled to continue the current execution continuation_token = json.dumps({'previous_job_id': job}) print('Putting job continuation') print(message) code_pipeline.put_job_success_result(jobId=job, continuationToken=continuation_token) def start_update_or_create(job_id, stack, template): """Starts the stack update or create process If the stack exists then update, otherwise create. Args: job_id: The ID of the CodePipeline job stack: The stack to create or update template: The template to create/update the stack with """ if stack_exists(stack): status = get_stack_status(stack) if status not in ['CREATE_COMPLETE', 'ROLLBACK_COMPLETE', 'UPDATE_COMPLETE']: # If the CloudFormation stack is not in a state where # it can be updated again then fail the job right away. put_job_failure(job_id, 'Stack cannot be updated when status is: ' + status) return were_updates = update_stack(stack, template) if were_updates: # If there were updates then continue the job so it can monitor # the progress of the update. continue_job_later(job_id, 'Stack update started') else: # If there were no updates then succeed the job immediately put_job_success(job_id, 'There were no stack updates') else: # If the stack doesn't already exist then create it instead # of updating it. create_stack(stack, template) # Continue the job so the pipeline will wait for the CloudFormation # stack to be created. continue_job_later(job_id, 'Stack create started') def check_stack_update_status(job_id, stack): """Monitor an already-running CloudFormation update/create Succeeds, fails or continues the job depending on the stack status. Args: job_id: The CodePipeline job ID stack: The stack to monitor """ status = get_stack_status(stack) if status in ['UPDATE_COMPLETE', 'CREATE_COMPLETE']: # If the update/create finished successfully then # succeed the job and don't continue. put_job_success(job_id, 'Stack update complete') elif status in ['UPDATE_IN_PROGRESS', 'UPDATE_ROLLBACK_IN_PROGRESS', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS', 'CREATE_IN_PROGRESS', 'ROLLBACK_IN_PROGRESS', 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS']: # If the job isn't finished yet then continue it continue_job_later(job_id, 'Stack update still in progress') else: # If the Stack is a state which isn't "in progress" or "complete" # then the stack update/create has failed so end the job with # a failed result. put_job_failure(job_id, 'Update failed: ' + status) def get_user_params(job_data): """Decodes the JSON user parameters and validates the required properties. Args: job_data: The job data structure containing the UserParameters string which should be a valid JSON structure Returns: The JSON parameters decoded as a dictionary. Raises: Exception: The JSON can't be decoded or a property is missing. """ try: # Get the user parameters which contain the stack, artifact and file settings user_parameters = job_data['actionConfiguration']['configuration']['UserParameters'] decoded_parameters = json.loads(user_parameters) except Exception as e: # We're expecting the user parameters to be encoded as JSON # so we can pass multiple values. If the JSON can't be decoded # then fail the job with a helpful message. raise Exception('UserParameters could not be decoded as JSON') if 'stack' not in decoded_parameters: # Validate that the stack is provided, otherwise fail the job # with a helpful message. raise Exception('Your UserParameters JSON must include the stack name') if 'artifact' not in decoded_parameters: # Validate that the artifact name is provided, otherwise fail the job # with a helpful message. raise Exception('Your UserParameters JSON must include the artifact name') if 'file' not in decoded_parameters: # Validate that the template file is provided, otherwise fail the job # with a helpful message. raise Exception('Your UserParameters JSON must include the template file name') return decoded_parameters def setup_s3_client(job_data): """Creates an S3 client Uses the credentials passed in the event by CodePipeline. These credentials can be used to access the artifact bucket. Args: job_data: The job data structure Returns: An S3 client with the appropriate credentials """ key_id = job_data['artifactCredentials']['accessKeyId'] key_secret = job_data['artifactCredentials']['secretAccessKey'] session_token = job_data['artifactCredentials']['sessionToken'] session = Session(aws_access_key_id=key_id, aws_secret_access_key=key_secret, aws_session_token=session_token) return session.client('s3', config=botocore.client.Config(signature_version='s3v4')) def lambda_handler(event, context): """The Lambda function handler If a continuing job then checks the CloudFormation stack status and updates the job accordingly. If a new job then kick of an update or creation of the target CloudFormation stack. Args: event: The event passed by Lambda context: The context passed by Lambda """ try: # Extract the Job ID job_id = event['CodePipeline.job']['id'] # Extract the Job Data job_data = event['CodePipeline.job']['data'] # Extract the params params = get_user_params(job_data) # Get the list of artifacts passed to the function artifacts = job_data['inputArtifacts'] stack = params['stack'] artifact = params['artifact'] template_file = params['file'] if 'continuationToken' in job_data: # If we're continuing then the create/update has already been triggered # we just need to check if it has finished. check_stack_update_status(job_id, stack) else: # Get the artifact details artifact_data = find_artifact(artifacts, artifact) # Get S3 client to access artifact with s3 = setup_s3_client(job_data) # Get the JSON template file out of the artifact template = get_template(s3, artifact_data, template_file) # Kick off a stack update or create start_update_or_create(job_id, stack, template) except Exception as e: # If any other exceptions which we didn't expect are raised # then fail the job and log the exception message. print('Function failed due to exception.') print(e) traceback.print_exc() put_job_failure(job_id, 'Function exception: ' + str(e)) print('Function complete.') return "Complete."
  6. Belassen Sie Handler auf dem Standardwert und belassen Sie Role bei dem Namen, den Sie zuvor ausgewählt oder erstellt habenCodePipelineLambdaExecRole.

  7. Ersetzen Sie unter Basic settings (Grundlegende Einstellungen) im Feld Timeout den Standardwert von 3 Sekunden durch 20.

  8. Wählen Sie Speichern.

  9. Bearbeiten Sie die Pipeline in der CodePipeline-Konsole, um die Funktion als Aktion in einer Stufe in Ihrer Pipeline hinzuzufügen. Wählen Sie Bearbeiten für die Pipeline-Phase, die Sie ändern möchten, und wählen Sie Aktionsgruppe hinzufügen. Geben Sie auf der Seite Aktion bearbeiten unter Aktionsname einen Namen für Ihre Aktion ein. Wählen Sie unter Aktionsanbieter Lambda aus.

    Wählen Sie unter Eingabeartefakte ausMyTemplate. In UserParametersmüssen Sie eine JSON-Zeichenfolge mit drei Parametern angeben:

    • Stack name

    • AWS CloudFormation-Vorlagename und -Pfad zur Datei

    • Eingabeartefakt

    Verwenden Sie geschweifte Klammern ({}) und trennen Sie die Parameter durch Kommas. Um beispielsweise einen Stapel mit dem Namen MyTestStackfür eine Pipeline mit dem Eingabeartefakt MyTemplatein zu erstellen UserParameters, geben Sie Folgendes ein: {"stack“:“ MyTestStack„, "file“: „template-package/template.json“, "artifact“:“ MyTemplate„}.

    Anmerkung

    Obwohl Sie das Eingabeartefakt in angegeben haben UserParameters, müssen Sie dieses Eingabeartefakt auch für die Aktion unter Eingabeartefakte angeben.

  10. Speichern Sie Ihre Änderungen an der Pipeline und geben Sie dann manuell eine Änderung frei, um die Aktion und die Lambda-Funktion zu testen.