

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.

# Entwicklung benutzerdefinierter Hooks mit der CloudFormation CLI
<a name="hooks-develop"></a>

Dieser Abschnitt richtet sich an Kunden, die benutzerdefinierte Hooks entwickeln und diese in der CloudFormation Registrierung registrieren möchten. Es bietet einen Überblick über die Struktur von CloudFormation Hooks und Anleitungen zum Entwickeln, Registrieren, Testen, Verwalten und Veröffentlichen eigener Hooks mit Python oder Java.

Bei der Entwicklung eines benutzerdefinierten Hooks gibt es drei Hauptschritte:

1. **Initiieren**

   Um benutzerdefinierte Hooks zu entwickeln, müssen Sie die CloudFormation CLI konfigurieren und verwenden. Um das Projekt eines Hooks und die erforderlichen Dateien zu initiieren, verwenden Sie den CloudFormation [https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-init.html](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-init.html)CLI-Befehl und geben Sie an, dass Sie einen Hook erstellen möchten. Weitere Informationen finden Sie unter [Initiieren eines benutzerdefinierten CloudFormation Hooks-Projekts](hooks-init.md).

1. **Model**

   Um Ihr Hook-Schema zu modellieren, zu erstellen und zu validieren, definieren Sie den Hook, seine Eigenschaften und Attribute.

   Die CloudFormation CLI erstellt leere Handler-Funktionen, die einem bestimmten Hook-Aufrufpunkt entsprechen. Fügen Sie diesen Handlern Ihre eigene Logik hinzu, um zu steuern, was während Ihres Hook-Aufrufs in jeder Phase seines Ziellebenszyklus passiert. Weitere Informationen finden Sie unter [Modellieren von benutzerdefinierten CloudFormation Hooks](hooks-model.md).

1. **Registrieren Sie sich**

   Um einen Hook zu registrieren, reichen Sie Ihren Hook ein, um ihn entweder als private oder als öffentliche Erweiterung eines Drittanbieters registrieren zu lassen. Registrieren Sie Ihren Hook bei der `[submit](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-submit.html)` Operation. Weitere Informationen finden Sie unter [Einen benutzerdefinierten Hook registrieren mit CloudFormation](registering-hooks.md).

   Die folgenden Aufgaben sind mit der Registrierung Ihres Hooks verbunden:

   1. *Veröffentlichen* — Hooks werden in der Registry veröffentlicht.

   1. *Konfigurieren* — Hooks werden konfiguriert, wenn die Typkonfiguration gegen Stacks aufgerufen wird.
**Anmerkung**  
Hooks laufen nach 30 Sekunden ab und versuchen es bis zu dreimal erneut. Weitere Informationen finden Sie unter [Timeout- und Wiederholungslimits](hooks-concepts.md#hook-timeout-and-retry-limits).

**Topics**
+ [Voraussetzungen](hooks-prerequisites.md)
+ [Ein Hooks-Projekt initiieren](hooks-init.md)
+ [Hooks modellieren](hooks-model.md)
+ [Hooks registrieren](registering-hooks.md)
+ [Hooks testen](testing-hooks.md)
+ [Hooks aktualisieren](updating-registered-hook.md)
+ [Hooks deregistrieren](deregistering-hooks.md)
+ [Hooks veröffentlichen](hooks-publishing.md)
+ [Schemasyntax](hooks-schema.md)

# Voraussetzungen für die Entwicklung benutzerdefinierter CloudFormation Hooks
<a name="hooks-prerequisites"></a>

Sie können einen benutzerdefinierten Hook mit Java oder Python entwickeln. Im Folgenden sind die Voraussetzungen für die Entwicklung benutzerdefinierter Hooks aufgeführt:

**Java-Voraussetzungen**
+ [Apache Maven](https://maven.apache.org/install.html)
+ [JDK 17](https://www.oracle.com/java/technologies/downloads/#java17)
**Anmerkung**  
Wenn Sie beabsichtigen, die [CloudFormation Befehlszeilenschnittstelle (CLI)](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/what-is-cloudformation-cli.html) zu verwenden, um ein Hooks-Projekt für Java zu initiieren, müssen Sie auch Python 3.8 oder höher installieren. Das Java-Plugin für die CloudFormation CLI kann über `pip` (Pythons Paketmanager) installiert werden, der mit Python nicht kompatibel ist.

Um Hook-Handler für Ihr Java-Hooks-Projekt zu implementieren, können Sie die [Java-Hook-Handler-Beispieldateien](samples/java-handlers.zip) herunterladen.

**Python-Voraussetzungen**
+ [Python-Version 3.8](https://www.python.org/downloads/) oder höher.

Um Hook-Handler für Ihr Python-Hooks-Projekt zu implementieren, können Sie die [Python-Hook-Handler-Beispieldateien](samples/python-handlers.zip) herunterladen.

## Berechtigungen für die Entwicklung von Hooks
<a name="hooks-development-permissions"></a>

Zusätzlich zu den Berechtigungen CloudFormation `Create``Update`, und `Delete` Stack benötigen Sie Zugriff auf die folgenden AWS CloudFormation Operationen. Der Zugriff auf diese Operationen wird über die CloudFormation Richtlinie Ihrer IAM-Rolle verwaltet.
+ [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/register-type.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/register-type.html)
+ [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/list-types.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/list-types.html)
+ [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deregister-type.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deregister-type.html)
+ [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-type-configuration.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-type-configuration.html)

Weitere Informationen finden Sie unter [Erteilen Sie IAM-Berechtigungen für Hooks CloudFormation](grant-iam-permissions-for-hooks.md).

## Richten Sie eine Entwicklungsumgebung für Hooks ein
<a name="hooks-environment"></a>

Um Hooks zu entwickeln, sollten Sie mit [CloudFormation Vorlagen](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-guide.html) und entweder mit Python oder Java vertraut sein. 

 

**Um die CloudFormation CLI und die zugehörigen Plugins zu installieren:**

1. Installieren Sie die CloudFormation CLI mit `pip` dem Python-Paketmanager.

   ```
   pip3 install cloudformation-cli
   ```

1. Installieren Sie entweder das Python- oder das Java-Plugin für die CloudFormation CLI.

------
#### [ Python ]

   ```
   pip3 install cloudformation-cli-python-plugin
   ```

------
#### [ Java ]

   ```
   pip3 install cloudformation-cli-java-plugin
   ```

------

Um die CloudFormation CLI und das Plugin zu aktualisieren, können Sie die Upgrade-Option verwenden.

------
#### [ Python ]

```
pip3 install --upgrade cloudformation-cli cloudformation-cli-python-plugin
```

------
#### [ Java ]

```
pip3 install --upgrade cloudformation-cli cloudformation-cli-java-plugin
```

------

# Initiieren eines benutzerdefinierten CloudFormation Hooks-Projekts
<a name="hooks-init"></a>

Der erste Schritt bei der Erstellung Ihres benutzerdefinierten Hooks-Projekts besteht darin, das Projekt zu initiieren. Sie können den CloudFormation `init` CLI-Befehl verwenden, um Ihr benutzerdefiniertes Hooks-Projekt zu initiieren.

Der `init` Befehl startet einen Assistenten, der Sie durch die Einrichtung des Projekts führt, einschließlich einer Hooks-Schemadatei. Verwenden Sie diese Schemadatei als Ausgangspunkt für die Definition der Form und Semantik Ihrer Hooks. Weitere Informationen finden Sie unter [Schemasyntax](hooks-schema.md).

**Um ein Hook-Projekt zu initiieren:**

1. Erstellen Sie ein Verzeichnis für das Projekt.

   ```
   mkdir ~/mycompany-testing-mytesthook
   ```

1. Navigieren Sie zum neuen Verzeichnis.

   ```
   cd ~/mycompany-testing-mytesthook
   ```

1. Verwenden Sie den CloudFormation `init` CLI-Befehl, um das Projekt zu initiieren.

   ```
   cfn init
   ```

   Der Befehl gibt die folgende Ausgabe zurück.

   ```
   Initializing new project
   ```

1. Der `init` Befehl startet einen Assistenten, der Sie durch die Einrichtung des Projekts führt. Wenn Sie dazu aufgefordert werden, geben Sie `h` die Eingabetaste ein, um ein Hooks-Projekt anzugeben.

   ```
   Do you want to develop a new resource(r) a module(m) or a hook(h)?
   ```

   ```
   h
   ```

1. Geben Sie einen Namen für Ihren Hook-Typ ein.

   ```
   What's the name of your hook type?
   (Organization::Service::Hook)
   ```

   ```
   MyCompany::Testing::MyTestHook
   ```

1. Wenn nur ein Sprach-Plugin installiert ist, ist es standardmäßig ausgewählt. Wenn mehr als ein Sprach-Plugin installiert ist, können Sie Ihre gewünschte Sprache wählen. Geben Sie eine Zahlenauswahl für die Sprache Ihrer Wahl ein.

   ```
   Select a language for code generation:
   [1] java
   [2] python38
   [3] python39
   (enter an integer):
   ```

1. Richten Sie die Paketierung auf der Grundlage der ausgewählten Entwicklungssprache ein.

------
#### [ Python ]

   (*Optional*) Wählen Sie Docker für plattformunabhängige Paketierung. Docker ist zwar nicht erforderlich, wird aber dringend empfohlen, um das Paketieren zu vereinfachen.

   ```
   Use docker for platform-independent packaging (Y/n)?
   This is highly recommended unless you are experienced with cross-platform Python packaging.
   ```

------
#### [ Java ]

   Legen Sie den Namen des Java-Pakets fest und wählen Sie ein Codegen-Modell aus. Sie können den Standard-Paketnamen verwenden oder einen neuen erstellen.

   ```
   Enter a package name (empty for default 'com.mycompany.testing.mytesthook'):
   ```

   ```
   Choose codegen model - 1 (default) or 2 (guided-aws):
   ```

------

*Ergebnisse*: Sie haben das Projekt erfolgreich initiiert und die für die Entwicklung eines Hooks erforderlichen Dateien generiert. Das Folgende ist ein Beispiel für die Verzeichnisse und Dateien, aus denen ein Hooks-Projekt für Python 3.8 besteht.

```
mycompany-testing-mytesthook.json
rpdk.log
README.md
requirements.txt
hook-role.yaml
template.yml
docs
    README.md
src
    __init__.py
    handlers.py
    models.py
    target_models
        aws_s3_bucket.py
```

**Anmerkung**  
Die Dateien im `src` Verzeichnis werden auf der Grundlage Ihrer Sprachauswahl erstellt. Die generierten Dateien enthalten einige nützliche Kommentare und Beispiele. Einige Dateien, wie z. B.`models.py`, werden in einem späteren Schritt automatisch aktualisiert, wenn Sie den `generate` Befehl zum Hinzufügen von Laufzeitcode für Ihre Handler ausführen.

# Modellieren von benutzerdefinierten CloudFormation Hooks
<a name="hooks-model"></a>

Die Modellierung von benutzerdefinierten CloudFormation Hooks beinhaltet die Erstellung eines Schemas, das den Hook, seine Eigenschaften und seine Attribute definiert. Wenn Sie Ihr benutzerdefiniertes Hook-Projekt mithilfe des `cfn init` Befehls erstellen, wird ein Beispiel-Hook-Schema als Textdatei im JSON-Format erstellt. `hook-name.json`

Zielaufrufpunkte und Zielaktionen geben den genauen Punkt an, an dem der Hook aufgerufen wird. *Hook-Handler* hosten eine ausführbare benutzerdefinierte Logik für diese Punkte. Beispielsweise verwendet eine Zielaktion der `CREATE` Operation einen `preCreate` Handler. Ihr im Handler geschriebener Code wird aufgerufen, wenn Hook-Ziele und -Dienste eine passende Aktion ausführen. *Hook-Ziele* sind das Ziel, an dem Hooks aufgerufen werden. Sie können Ziele wie CloudFormation öffentliche Ressourcen, private Ressourcen oder benutzerdefinierte Ressourcen angeben. Hooks unterstützen eine unbegrenzte Anzahl von Hook-Zielen.

Das Schema enthält die für den Hook erforderlichen Berechtigungen. Um den Hook zu erstellen, müssen Sie die Berechtigungen für jeden Hook-Handler angeben. CloudFormation ermutigt Autoren, Richtlinien zu verfassen, die den standardmäßigen Sicherheitsempfehlungen folgen, d. h. die Gewährung *geringster* Rechte oder nur die zur Ausführung einer Aufgabe erforderlichen Berechtigungen. Ermitteln Sie, was Benutzer (und Rollen) tun müssen, und erstellen Sie dann Richtlinien, die es ihnen ermöglichen, *nur* diese Aufgaben für Hook-Operationen auszuführen. CloudFormation verwendet diese Berechtigungen, um die von Hook-Benutzern bereitgestellten Berechtigungen einzugrenzen. Diese Berechtigungen werden an den Hook weitergegeben. Hook-Handler verwenden diese Berechtigungen, um auf AWS Ressourcen zuzugreifen.

Sie können die folgende Schemadatei als Ausgangspunkt verwenden, um Ihren Hook zu definieren. Verwenden Sie das Hook-Schema, um anzugeben, welche Handler Sie implementieren möchten. Wenn Sie einen bestimmten Handler nicht implementieren möchten, entfernen Sie ihn aus dem Abschnitt „Handler“ des Hook-Schemas. Weitere Informationen zum Schema finden Sie unter. [Schemasyntax](hooks-schema.md)

```
{
    "typeName":"MyCompany::Testing::MyTestHook",
    "description":"Verifies S3 bucket and SQS queues properties before create and update",
    "sourceUrl":"https://mycorp.com/my-repo.git",
    "documentationUrl":"https://mycorp.com/documentation",
    "typeConfiguration":{
        "properties":{
            "minBuckets":{
                "description":"Minimum number of compliant buckets",
                "type":"string"
            },
            "minQueues":{
                "description":"Minimum number of compliant queues",
                "type":"string"
            },
            "encryptionAlgorithm":{
                "description":"Encryption algorithm for SSE",
                "default":"AES256",
                "type":"string"
            }
        },
        "required":[
            
        ],
        "additionalProperties":false
    },
    "handlers":{
        "preCreate":{
            "targetNames":[
                "AWS::S3::Bucket",
                "AWS::SQS::Queue"
            ],
            "permissions":[
                
            ]
        },
        "preUpdate":{
            "targetNames":[
                "AWS::S3::Bucket",
                "AWS::SQS::Queue"
            ],
            "permissions":[
                
            ]
        },
        "preDelete":{
            "targetNames":[
                "AWS::S3::Bucket",
                "AWS::SQS::Queue"
            ],
            "permissions":[
                "s3:ListBucket",
                "s3:ListAllMyBuckets",
                "s3:GetEncryptionConfiguration",
                "sqs:ListQueues",
                "sqs:GetQueueAttributes",
                "sqs:GetQueueUrl"
            ]
        }
    },
    "additionalProperties":false
}
```

**Topics**
+ [Modellieren von benutzerdefinierten CloudFormation Hooks mit Java](hooks-model-java.md)
+ [Modellieren von benutzerdefinierten CloudFormation Hooks mit Python](hooks-model-python.md)

# Modellieren von benutzerdefinierten CloudFormation Hooks mit Java
<a name="hooks-model-java"></a>

Die Modellierung von benutzerdefinierten CloudFormation Hooks beinhaltet die Erstellung eines Schemas, das den Hook, seine Eigenschaften und seine Attribute definiert. Dieses Tutorial führt Sie durch die Modellierung benutzerdefinierter Hooks mit Java.

## Schritt 1: Fügen Sie Projektabhängigkeiten hinzu
<a name="model-hook-project-dependencies"></a>

Java-basierte Hooks-Projekte verlassen sich auf die `pom.xml` Datei von Maven als Abhängigkeit. Erweitern Sie den folgenden Abschnitt und kopieren Sie den Quellcode in die `pom.xml` Datei im Stammverzeichnis des Projekts.

### Hook-Projektabhängigkeiten (pom.xml)
<a name="hook-java-dependencies"></a>

```
<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mycompany.testing.mytesthook</groupId>
    <artifactId>mycompany-testing-mytesthook-handler</artifactId>
    <name>mycompany-testing-mytesthook-handler</name>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <aws.java.sdk.version>2.16.1</aws.java.sdk.version>
        <checkstyle.version>8.36.2</checkstyle.version>
        <commons-io.version>2.8.0</commons-io.version>
        <jackson.version>2.11.3</jackson.version>
        <maven-checkstyle-plugin.version>3.1.1</maven-checkstyle-plugin.version>
        <mockito.version>3.6.0</mockito.version>
        <spotbugs.version>4.1.4</spotbugs.version>
        <spotless.version>2.5.0</spotless.version>
        <maven-javadoc-plugin.version>3.2.0</maven-javadoc-plugin.version>
        <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
        <cfn.generate.args/>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>software.amazon.awssdk</groupId>
                <artifactId>bom</artifactId>
                <version>2.16.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/software.amazon.cloudformation/aws-cloudformation-rpdk-java-plugin -->
        <dependency>
            <groupId>software.amazon.cloudformation</groupId>
            <artifactId>aws-cloudformation-rpdk-java-plugin</artifactId>
            <version>[2.0.0,3.0.0)</version>
        </dependency>

        <!-- AWS Java SDK v2 Dependencies -->
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>sdk-core</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>cloudformation</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>utils</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>apache-client</artifactId>
        </dependency>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>sqs</artifactId>
        </dependency>

        <!-- Test dependency for Java Providers -->
        <dependency>
            <groupId>software.amazon.cloudformation</groupId>
            <artifactId>cloudformation-cli-java-plugin-testing-support</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3 -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-s3</artifactId>
            <version>1.12.85</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>${commons-io.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>29.0-jre</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-cloudformation -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-cloudformation</artifactId>
            <version>1.11.555</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.14</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/software.amazon.cloudformation/aws-cloudformation-resource-schema -->
        <dependency>
            <groupId>software.amazon.cloudformation</groupId>
            <artifactId>aws-cloudformation-resource-schema</artifactId>
            <version>[2.0.5, 3.0.0)</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-cbor -->
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-cbor</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-modules-java8 -->
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-modules-java8</artifactId>
            <version>${jackson.version}</version>
            <type>pom</type>
            <scope>runtime</scope>
        </dependency>

         <!-- https://mvnrepository.com/artifact/org.json/json -->
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20180813</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-core -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-core</artifactId>
            <version>1.11.1034</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-core -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-lambda-java-log4j2 -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-log4j2</artifactId>
            <version>1.2.0</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.8</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.17.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.17.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.17.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.12.2</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.5.0-M1</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.6.0</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>3.6.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <compilerArgs>
                        <arg>-Xlint:all,-options,-processing</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                    <filters>
                        <filter>
                            <artifact>*:*</artifact>
                            <excludes>
                                <exclude>**/Log4j2Plugins.dat</exclude>
                            </excludes>
                        </filter>
                    </filters>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <id>generate</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <configuration>
                            <executable>cfn</executable>
                            <commandlineArgs>generate ${cfn.generate.args}</commandlineArgs>
                            <workingDirectory>${project.basedir}</workingDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.basedir}/target/generated-sources/rpdk</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.4</version>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M3</version>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.4</version>
                <configuration>
                    <excludes>
                        <exclude>**/BaseHookConfiguration*</exclude>
                        <exclude>**/BaseHookHandler*</exclude>
                        <exclude>**/HookHandlerWrapper*</exclude>
                        <exclude>**/ResourceModel*</exclude>
                        <exclude>**/TypeConfigurationModel*</exclude>
                        <exclude>**/model/**/*</exclude>
                    </excludes>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>jacoco-check</id>
                        <goals>
                            <goal>check</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <rule>
                                    <element>PACKAGE</element>
                                    <limits>
                                        <limit>
                                            <counter>BRANCH</counter>
                                            <value>COVEREDRATIO</value>
                                            <minimum>0.8</minimum>
                                        </limit>
                                        <limit>
                                            <counter>INSTRUCTION</counter>
                                            <value>COVEREDRATIO</value>
                                            <minimum>0.8</minimum>
                                        </limit>
                                    </limits>
                                </rule>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>${project.basedir}</directory>
                <includes>
                    <include>mycompany-testing-mytesthook.json</include>
                </includes>
            </resource>
            <resource>
                <directory>${project.basedir}/target/loaded-target-schemas</directory>
                <includes>
                    <include>**/*.json</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>
```

## Schritt 2: Generieren Sie das Hook-Projektpaket
<a name="model-hook-project-package-java"></a>

Generieren Sie Ihr Hook-Projektpaket. Die CloudFormation CLI erstellt leere Handler-Funktionen, die bestimmten Hook-Aktionen im Ziellebenszyklus entsprechen, wie in der Hook-Spezifikation definiert.

```
cfn generate
```

Der Befehl gibt die folgende Ausgabe zurück.

```
Generated files for MyCompany::Testing::MyTestHook
```

**Anmerkung**  
Stellen Sie sicher, dass Ihre Lambda-Laufzeiten up-to-date die Verwendung einer veralteten Version vermeiden. Weitere Informationen finden Sie unter [Lambda-Laufzeiten für Ressourcentypen und Hooks aktualisieren](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/runtime-update.html).

## Schritt 3: Hook-Handler hinzufügen
<a name="model-hook-project-add-handler-java"></a>

Fügen Sie Ihren eigenen Hook-Handler-Laufzeitcode zu den Handlern hinzu, die Sie implementieren möchten. Sie können beispielsweise den folgenden Code für die Protokollierung hinzufügen.

```
logger.log("Internal testing Hook triggered for target: " + request.getHookContext().getTargetName());
```

Die CloudFormation CLI generiert Plain Old Java Objects (Java POJO). Im Folgenden finden Sie Ausgabebeispiele, die aus `AWS::S3::Bucket` generiert wurden.

**Example AwSS3 .java BucketTargetModel**  

```
package com.mycompany.testing.mytesthook.model.aws.s3.bucket;

import...


@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public class AwsS3BucketTargetModel extends ResourceHookTargetModel<AwsS3Bucket> {

    @JsonIgnore
    private static final TypeReference<AwsS3Bucket> TARGET_REFERENCE =
        new TypeReference<AwsS3Bucket>() {};

    @JsonIgnore
    private static final TypeReference<AwsS3BucketTargetModel> MODEL_REFERENCE =
        new TypeReference<AwsS3BucketTargetModel>() {};

    @JsonIgnore
    public static final String TARGET_TYPE_NAME = "AWS::S3::Bucket";


    @JsonIgnore
    public TypeReference<AwsS3Bucket> getHookTargetTypeReference() {
        return TARGET_REFERENCE;
    }

    @JsonIgnore
    public TypeReference<AwsS3BucketTargetModel> getTargetModelTypeReference() {
        return MODEL_REFERENCE;
    }
}
```

**Example AwsS3Bucket.java**  

```
package com.mycompany.testing.mytesthook.model.aws.s3.bucket;

import ...


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public class AwsS3Bucket extends ResourceHookTarget {
    @JsonIgnore
    public static final String TYPE_NAME = "AWS::S3::Bucket";

    @JsonIgnore
    public static final String IDENTIFIER_KEY_ID = "/properties/Id";

    @JsonProperty("InventoryConfigurations")
    private List<InventoryConfiguration> inventoryConfigurations;

    @JsonProperty("WebsiteConfiguration")
    private WebsiteConfiguration websiteConfiguration;

    @JsonProperty("DualStackDomainName")
    private String dualStackDomainName;

    @JsonProperty("AccessControl")
    private String accessControl;

    @JsonProperty("AnalyticsConfigurations")
    private List<AnalyticsConfiguration> analyticsConfigurations;

    @JsonProperty("AccelerateConfiguration")
    private AccelerateConfiguration accelerateConfiguration;

    @JsonProperty("PublicAccessBlockConfiguration")
    private PublicAccessBlockConfiguration publicAccessBlockConfiguration;

    @JsonProperty("BucketName")
    private String bucketName;

    @JsonProperty("RegionalDomainName")
    private String regionalDomainName;

    @JsonProperty("OwnershipControls")
    private OwnershipControls ownershipControls;

    @JsonProperty("ObjectLockConfiguration")
    private ObjectLockConfiguration objectLockConfiguration;

    @JsonProperty("ObjectLockEnabled")
    private Boolean objectLockEnabled;

    @JsonProperty("LoggingConfiguration")
    private LoggingConfiguration loggingConfiguration;

    @JsonProperty("ReplicationConfiguration")
    private ReplicationConfiguration replicationConfiguration;

    @JsonProperty("Tags")
    private List<Tag> tags;

    @JsonProperty("DomainName")
    private String domainName;

    @JsonProperty("BucketEncryption")
    private BucketEncryption bucketEncryption;

    @JsonProperty("WebsiteURL")
    private String websiteURL;

    @JsonProperty("NotificationConfiguration")
    private NotificationConfiguration notificationConfiguration;

    @JsonProperty("LifecycleConfiguration")
    private LifecycleConfiguration lifecycleConfiguration;

    @JsonProperty("VersioningConfiguration")
    private VersioningConfiguration versioningConfiguration;

    @JsonProperty("MetricsConfigurations")
    private List<MetricsConfiguration> metricsConfigurations;

    @JsonProperty("IntelligentTieringConfigurations")
    private List<IntelligentTieringConfiguration> intelligentTieringConfigurations;

    @JsonProperty("CorsConfiguration")
    private CorsConfiguration corsConfiguration;

    @JsonProperty("Id")
    private String id;

    @JsonProperty("Arn")
    private String arn;

    @JsonIgnore
    public JSONObject getPrimaryIdentifier() {
        final JSONObject identifier = new JSONObject();
        if (this.getId() != null) {
            identifier.put(IDENTIFIER_KEY_ID, this.getId());
        }

        // only return the identifier if it can be used, i.e. if all components are present
        return identifier.length() == 1 ? identifier : null;
    }

    @JsonIgnore
    public List<JSONObject> getAdditionalIdentifiers() {
        final List<JSONObject> identifiers = new ArrayList<JSONObject>();
        // only return the identifiers if any can be used
        return identifiers.isEmpty() ? null : identifiers;
    }
}
```

**Example BucketEncryption.java**  

```
package software.amazon.testing.mytesthook.model.aws.s3.bucket;

import ...


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public class BucketEncryption {
    @JsonProperty("ServerSideEncryptionConfiguration")
    private List<ServerSideEncryptionRule> serverSideEncryptionConfiguration;

}
```

**Example ServerSideEncryptionRule.java**  

```
package com.mycompany.testing.mytesthook.model.aws.s3.bucket;

import ...


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public class ServerSideEncryptionRule {
    @JsonProperty("BucketKeyEnabled")
    private Boolean bucketKeyEnabled;

    @JsonProperty("ServerSideEncryptionByDefault")
    private ServerSideEncryptionByDefault serverSideEncryptionByDefault;

}
```

**Example ServerSideEncryptionByDefault.java**  

```
package com.mycompany.testing.mytesthook.model.aws.s3.bucket;

import ...


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
public class ServerSideEncryptionByDefault {
    @JsonProperty("SSEAlgorithm")
    private String sSEAlgorithm;

    @JsonProperty("KMSMasterKeyID")
    private String kMSMasterKeyID;

}
```

Mit dem POJOs generierten können Sie nun die Handler schreiben, die die Funktionalität des Hooks tatsächlich implementieren. Implementieren Sie in diesem Beispiel den `preUpdate` Aufrufpunkt `preCreate` und für die Handler.

## Schritt 4: Implementieren Sie Hook-Handler
<a name="model-hook-project-code-handler-java"></a>

**Topics**
+ [Codierung des API-Client-Builders](#java-code-api-client-builder)
+ [Codierung des API-Anforderungsmachers](#code-the-api-request-maker)
+ [Implementierung des Hilfscodes](#implement-helper-code)
+ [Implementierung des Basis-Handlers](#implement-base-hook-handler)
+ [Implementierung des `preCreate` Handlers](#implementing-precreate-handler)
+ [Codierung des `preCreate` Handlers](#coding-the-precreate-handler)
+ [Der `preCreate` Test wird aktualisiert](#updating-the-precreate-handler)
+ [Implementierung des `preUpdate` Handlers](#implementing-preupdate-handler)
+ [Codierung des `preUpdate` Handlers](#coding-preupdate-handler)
+ [Der `preUpdate` Test wird aktualisiert](#update-the-preupdate-handler)
+ [Implementierung des `preDelete` Handlers](#implement-the-predelete-hander)
+ [Codierung des `preDelete` Handlers](#code-the-predelete-handler)
+ [Den `preDelete` Handler aktualisieren](#update-the-predelete-handler)

### Codierung des API-Client-Builders
<a name="java-code-api-client-builder"></a>

1. Öffnen Sie in Ihrer IDE die `ClientBuilder.java` Datei, die sich im `src/main/java/com/mycompany/testing/mytesthook` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `ClientBuilder.java` Datei durch den folgenden Code.  
**Example ClientBuilder.java**  

   ```
   package com.awscommunity.kms.encryptionsettings;
   
   import software.amazon.awssdk.services.ec2.Ec2Client;
   import software.amazon.cloudformation.HookLambdaWrapper;
   
   /**
    * Describes static HTTP clients (to consume less memory) for API calls that
    * this hook makes to a number of AWS services.
    */
   public final class ClientBuilder {
   
       private ClientBuilder() {
       }
   
       /**
        * Create an HTTP client for Amazon EC2.
        *
        * @return Ec2Client An {@link Ec2Client} object.
        */
       public static Ec2Client getEc2Client() {
           return Ec2Client.builder().httpClient(HookLambdaWrapper.HTTP_CLIENT).build();
       }
   }
   ```

### Codierung des API-Anforderungsmachers
<a name="code-the-api-request-maker"></a>

1. Öffnen Sie in Ihrer IDE die `Translator.java` Datei, die sich im `src/main/java/com/mycompany/testing/mytesthook` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `Translator.java` Datei durch den folgenden Code.  
**Example Translator.java**  

   ```
   package com.mycompany.testing.mytesthook;
   
   
   import software.amazon.awssdk.services.s3.model.GetBucketEncryptionRequest;
   import software.amazon.awssdk.services.s3.model.ListBucketsRequest;
   import software.amazon.awssdk.services.sqs.model.ListQueuesRequest;
   import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
   
   /**
    * This class is a centralized placeholder for
    *  - api request construction
    *  - object translation to/from aws sdk
    */
   
   public class Translator {
   
       static ListBucketsRequest translateToListBucketsRequest(final HookTargetModel targetModel) {
           return ListBucketsRequest.builder().build();
       }
   
       static ListQueuesRequest translateToListQueuesRequest(final String nextToken) {
           return ListQueuesRequest.builder().nextToken(nextToken).build();
       }
   
       static ListBucketsRequest createListBucketsRequest() {
           return ListBucketsRequest.builder().build();
       }
   
       static ListQueuesRequest createListQueuesRequest() {
           return createListQueuesRequest(null);
       }
   
       static ListQueuesRequest createListQueuesRequest(final String nextToken) {
           return ListQueuesRequest.builder().nextToken(nextToken).build();
       }
   
       static GetBucketEncryptionRequest createGetBucketEncryptionRequest(final String bucket) {
           return GetBucketEncryptionRequest.builder().bucket(bucket).build();
       }
   }
   ```

### Implementierung des Hilfscodes
<a name="implement-helper-code"></a>

1. Öffnen Sie in Ihrer IDE die `AbstractTestBase.java` Datei, die sich im `src/main/java/com/mycompany/testing/mytesthook` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `AbstractTestBase.java` Datei durch den folgenden Code.  
**Example Translator.java**  

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.google.common.collect.ImmutableMap;
   import org.mockito.Mockito;
   import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
   import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
   import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
   import software.amazon.awssdk.awscore.AwsRequest;
   import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
   import software.amazon.awssdk.awscore.AwsResponse;
   import software.amazon.awssdk.core.SdkClient;
   import software.amazon.awssdk.core.pagination.sync.SdkIterable;
   import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
   import software.amazon.cloudformation.proxy.Credentials;
   import software.amazon.cloudformation.proxy.LoggerProxy;
   import software.amazon.cloudformation.proxy.OperationStatus;
   import software.amazon.cloudformation.proxy.ProgressEvent;
   import software.amazon.cloudformation.proxy.ProxyClient;
   import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
   
   import javax.annotation.Nonnull;
   import java.time.Duration;
   import java.util.concurrent.CompletableFuture;
   import java.util.function.Function;
   import java.util.function.Supplier;
   
   import static org.assertj.core.api.Assertions.assertThat;
   
   @lombok.Getter
   public class AbstractTestBase {
       protected final AwsSessionCredentials awsSessionCredential;
       protected final AwsCredentialsProvider v2CredentialsProvider;
       protected final AwsRequestOverrideConfiguration configuration;
       protected final LoggerProxy loggerProxy;
       protected final Supplier<Long> awsLambdaRuntime = () -> Duration.ofMinutes(15).toMillis();
       protected final AmazonWebServicesClientProxy proxy;
       protected final Credentials mockCredentials =
           new Credentials("mockAccessId", "mockSecretKey", "mockSessionToken");
   
       @lombok.Setter
       private SdkClient serviceClient;
   
       protected AbstractTestBase() {
           loggerProxy = Mockito.mock(LoggerProxy.class);
           awsSessionCredential = AwsSessionCredentials.create(mockCredentials.getAccessKeyId(),
               mockCredentials.getSecretAccessKey(), mockCredentials.getSessionToken());
           v2CredentialsProvider = StaticCredentialsProvider.create(awsSessionCredential);
           configuration = AwsRequestOverrideConfiguration.builder()
               .credentialsProvider(v2CredentialsProvider)
               .build();
           proxy = new AmazonWebServicesClientProxy(
               loggerProxy,
               mockCredentials,
               awsLambdaRuntime
           ) {
               @Override
               public <ClientT> ProxyClient<ClientT> newProxy(@Nonnull Supplier<ClientT> client) {
                   return new ProxyClient<ClientT>() {
                       @Override
                       public <RequestT extends AwsRequest, ResponseT extends AwsResponse>
                           ResponseT injectCredentialsAndInvokeV2(RequestT request,
                                                                  Function<RequestT, ResponseT> requestFunction) {
                           return proxy.injectCredentialsAndInvokeV2(request, requestFunction);
                       }
   
                       @Override
                       public <RequestT extends AwsRequest, ResponseT extends AwsResponse> CompletableFuture<ResponseT>
                           injectCredentialsAndInvokeV2Async(RequestT request, Function<RequestT, CompletableFuture<ResponseT>> requestFunction) {
                           return proxy.injectCredentialsAndInvokeV2Async(request, requestFunction);
                       }
   
                       @Override
                       public <RequestT extends AwsRequest, ResponseT extends AwsResponse, IterableT extends SdkIterable<ResponseT>>
                           IterableT
                           injectCredentialsAndInvokeIterableV2(RequestT request, Function<RequestT, IterableT> requestFunction) {
                           return proxy.injectCredentialsAndInvokeIterableV2(request, requestFunction);
                       }
   
                       @SuppressWarnings("unchecked")
                       @Override
                       public ClientT client() {
                           return (ClientT) serviceClient;
                       }
                   };
               }
           };
       }
   
       protected void assertResponse(final ProgressEvent<HookTargetModel, CallbackContext> response, final OperationStatus expectedStatus, final String expectedMsg) {
           assertThat(response).isNotNull();
           assertThat(response.getStatus()).isEqualTo(expectedStatus);
           assertThat(response.getCallbackContext()).isNull();
           assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
           assertThat(response.getMessage()).isNotNull();
           assertThat(response.getMessage()).isEqualTo(expectedMsg);
       }
   
       protected HookTargetModel createHookTargetModel(final Object resourceProperties) {
           return HookTargetModel.of(ImmutableMap.of("ResourceProperties", resourceProperties));
       }
       
       protected HookTargetModel createHookTargetModel(final Object resourceProperties, final Object previousResourceProperties) {
           return HookTargetModel.of(
                   ImmutableMap.of(
                       "ResourceProperties", resourceProperties,
                       "PreviousResourceProperties", previousResourceProperties
                   )
           );
       }
   }
   ```

### Implementierung des Basis-Handlers
<a name="implement-base-hook-handler"></a>

1. Öffnen Sie in Ihrer IDE die `BaseHookHandlerStd.java` Datei, die sich im `src/main/java/com/mycompany/testing/mytesthook` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `BaseHookHandlerStd.java` Datei durch den folgenden Code.  
**Example Translator.java**  

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3Bucket;
   import com.mycompany.testing.mytesthook.model.aws.sqs.queue.AwsSqsQueue;
   import software.amazon.awssdk.services.s3.S3Client;
   import software.amazon.awssdk.services.sqs.SqsClient;
   import software.amazon.cloudformation.exceptions.UnsupportedTargetException;
   import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
   import software.amazon.cloudformation.proxy.Logger;
   import software.amazon.cloudformation.proxy.ProgressEvent;
   import software.amazon.cloudformation.proxy.ProxyClient;
   import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
   import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
   
   
   public abstract class BaseHookHandlerStd extends BaseHookHandler<CallbackContext, TypeConfigurationModel> {
       public static final String HOOK_TYPE_NAME = "MyCompany::Testing::MyTestHook";
   
       protected Logger logger;
   
       @Override
       public ProgressEvent<HookTargetModel, CallbackContext> handleRequest(
               final AmazonWebServicesClientProxy proxy,
               final HookHandlerRequest request,
               final CallbackContext callbackContext,
               final Logger logger,
               final TypeConfigurationModel typeConfiguration
       ) {
           this.logger = logger;
   
           final String targetName = request.getHookContext().getTargetName();
   
           final ProgressEvent<HookTargetModel, CallbackContext> result;
           if (AwsS3Bucket.TYPE_NAME.equals(targetName)) {
               result = handleS3BucketRequest(
                       proxy,
                       request,
                       callbackContext != null ? callbackContext : new CallbackContext(),
                       proxy.newProxy(ClientBuilder::createS3Client),
                       typeConfiguration
               );
           } else if (AwsSqsQueue.TYPE_NAME.equals(targetName)) {
               result = handleSqsQueueRequest(
                       proxy,
                       request,
                       callbackContext != null ? callbackContext : new CallbackContext(),
                       proxy.newProxy(ClientBuilder::createSqsClient),
                       typeConfiguration
               );
           } else {
               throw new UnsupportedTargetException(targetName);
           }
   
           log(
               String.format(
                   "Result for [%s] invocation for target [%s] returned status [%s] with message [%s]",
                   request.getHookContext().getInvocationPoint(),
                   targetName,
                   result.getStatus(),
                   result.getMessage()
               )
           );
   
           return result;
       }
   
       protected abstract ProgressEvent<HookTargetModel, CallbackContext> handleS3BucketRequest(
               final AmazonWebServicesClientProxy proxy,
               final HookHandlerRequest request,
               final CallbackContext callbackContext,
               final ProxyClient<S3Client> proxyClient,
               final TypeConfigurationModel typeConfiguration
       );
   
       protected abstract ProgressEvent<HookTargetModel, CallbackContext> handleSqsQueueRequest(
               final AmazonWebServicesClientProxy proxy,
               final HookHandlerRequest request,
               final CallbackContext callbackContext,
               final ProxyClient<SqsClient> proxyClient,
               final TypeConfigurationModel typeConfiguration
       );
   
       protected void log(final String message) {
           if (logger != null) {
               logger.log(message);
           } else {
               System.out.println(message);
           }
       }
   }
   ```

### Implementierung des `preCreate` Handlers
<a name="implementing-precreate-handler"></a>

Der `preCreate` Handler überprüft die serverseitigen Verschlüsselungseinstellungen für eine `AWS::S3::Bucket` Oder-Ressource`AWS::SQS::Queue`.
+ Für eine `AWS::S3::Bucket` Ressource ist der Hook nur erfolgreich, wenn Folgendes zutrifft:
  + Die Amazon S3 S3-Bucket-Verschlüsselung ist eingestellt.
  + Der Amazon S3 S3-Bucket-Schlüssel ist für den Bucket aktiviert.
  + Der für den Amazon S3 S3-Bucket festgelegte Verschlüsselungsalgorithmus ist der richtige erforderliche Algorithmus.
  + Die AWS Key Management Service Schlüssel-ID ist festgelegt.
+ Für eine `AWS::SQS::Queue` Ressource wird der Hook nur erfolgreich sein, wenn Folgendes zutrifft:
  + Die AWS Key Management Service Schlüssel-ID ist gesetzt.

### Codierung des `preCreate` Handlers
<a name="coding-the-precreate-handler"></a>

1. Öffnen Sie in Ihrer IDE die `PreCreateHookHandler.java` Datei, die sich im `src/main/java/software/mycompany/testing/mytesthook` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `PreCreateHookHandler.java` Datei durch den folgenden Code.

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3Bucket;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3BucketTargetModel;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.BucketEncryption;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.ServerSideEncryptionByDefault;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.ServerSideEncryptionRule;
   import com.mycompany.testing.mytesthook.model.aws.sqs.queue.AwsSqsQueue;
   import com.mycompany.testing.mytesthook.model.aws.sqs.queue.AwsSqsQueueTargetModel;
   import org.apache.commons.collections.CollectionUtils;
   import org.apache.commons.lang3.StringUtils;
   import software.amazon.cloudformation.exceptions.UnsupportedTargetException;
   import software.amazon.cloudformation.proxy.HandlerErrorCode;
   import software.amazon.cloudformation.proxy.Logger;
   import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
   import software.amazon.cloudformation.proxy.hook.HookStatus;
   import software.amazon.cloudformation.proxy.hook.HookProgressEvent;
   import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
   import software.amazon.cloudformation.proxy.hook.targetmodel.ResourceHookTargetModel;
   
   import java.util.List;
   
   public class PreCreateHookHandler extends BaseHookHandler<TypeConfigurationModel, CallbackContext> {
   
       @Override
       public HookProgressEvent<CallbackContext> handleRequest(
           final AmazonWebServicesClientProxy proxy,
           final HookHandlerRequest request,
           final CallbackContext callbackContext,
           final Logger logger,
           final TypeConfigurationModel typeConfiguration) {
   
           final String targetName = request.getHookContext().getTargetName();
           if ("AWS::S3::Bucket".equals(targetName)) {
               final ResourceHookTargetModel<AwsS3Bucket> targetModel = request.getHookContext().getTargetModel(AwsS3BucketTargetModel.class);
   
               final AwsS3Bucket bucket = targetModel.getResourceProperties();
               final String encryptionAlgorithm = typeConfiguration.getEncryptionAlgorithm();
   
               return validateS3BucketEncryption(bucket, encryptionAlgorithm);
   
           } else if ("AWS::SQS::Queue".equals(targetName)) {
               final ResourceHookTargetModel<AwsSqsQueue> targetModel = request.getHookContext().getTargetModel(AwsSqsQueueTargetModel.class);
   
               final AwsSqsQueue queue = targetModel.getResourceProperties();
               return validateSQSQueueEncryption(queue);
           } else {
               throw new UnsupportedTargetException(targetName);
           }
       }
   
       private HookProgressEvent<CallbackContext> validateS3BucketEncryption(final AwsS3Bucket bucket, final String requiredEncryptionAlgorithm) {
           HookStatus resultStatus = null;
           String resultMessage = null;
   
           if (bucket != null) {
               final BucketEncryption bucketEncryption = bucket.getBucketEncryption();
               if (bucketEncryption != null) {
                   final List<ServerSideEncryptionRule> serverSideEncryptionRules = bucketEncryption.getServerSideEncryptionConfiguration();
                   if (CollectionUtils.isNotEmpty(serverSideEncryptionRules)) {
                       for (final ServerSideEncryptionRule rule : serverSideEncryptionRules) {
                           final Boolean bucketKeyEnabled = rule.getBucketKeyEnabled();
                           if (bucketKeyEnabled) {
                               final ServerSideEncryptionByDefault serverSideEncryptionByDefault = rule.getServerSideEncryptionByDefault();
   
                               final String encryptionAlgorithm = serverSideEncryptionByDefault.getSSEAlgorithm();
                               final String kmsKeyId = serverSideEncryptionByDefault.getKMSMasterKeyID(); // "KMSMasterKeyID" is name of the property for an AWS::S3::Bucket;
   
                               if (!StringUtils.equals(encryptionAlgorithm, requiredEncryptionAlgorithm) && StringUtils.isBlank(kmsKeyId)) {
                                   resultStatus = HookStatus.FAILED;
                                   resultMessage = "KMS Key ID not set and SSE Encryption Algorithm is incorrect for bucket with name: " + bucket.getBucketName();
                               } else if (!StringUtils.equals(encryptionAlgorithm, requiredEncryptionAlgorithm)) {
                                   resultStatus = HookStatus.FAILED;
                                   resultMessage = "SSE Encryption Algorithm is incorrect for bucket with name: " + bucket.getBucketName();
                               } else if (StringUtils.isBlank(kmsKeyId)) {
                                   resultStatus = HookStatus.FAILED;
                                   resultMessage = "KMS Key ID not set for bucket with name: " + bucket.getBucketName();
                               } else {
                                   resultStatus = HookStatus.SUCCESS;
                                   resultMessage = "Successfully invoked PreCreateHookHandler for target: AWS::S3::Bucket";
                               }
                           } else {
                               resultStatus = HookStatus.FAILED;
                               resultMessage = "Bucket key not enabled for bucket with name: " + bucket.getBucketName();
                           }
   
                           if (resultStatus == HookStatus.FAILED) {
                               break;
                           }
                       }
                   } else {
                       resultStatus = HookStatus.FAILED;
                       resultMessage = "No SSE Encryption configurations for bucket with name: " + bucket.getBucketName();
                   }
               } else {
                   resultStatus = HookStatus.FAILED;
                   resultMessage = "Bucket Encryption not enabled for bucket with name: " + bucket.getBucketName();
               }
           } else {
               resultStatus = HookStatus.FAILED;
               resultMessage = "Resource properties for S3 Bucket target model are empty";
           }
   
           return HookProgressEvent.<CallbackContext>builder()
                   .status(resultStatus)
                   .message(resultMessage)
                   .errorCode(resultStatus == HookStatus.FAILED ? HandlerErrorCode.ResourceConflict : null)
                   .build();
       }
   
       private HookProgressEvent<CallbackContext> validateSQSQueueEncryption(final AwsSqsQueue queue) {
           if (queue == null) {
               return HookProgressEvent.<CallbackContext>builder()
                       .status(HookStatus.FAILED)
                       .message("Resource properties for SQS Queue target model are empty")
                       .errorCode(HandlerErrorCode.ResourceConflict)
                       .build();
           }
   
           final String kmsKeyId = queue.getKmsMasterKeyId(); // "KmsMasterKeyId" is name of the property for an AWS::SQS::Queue
           if (StringUtils.isBlank(kmsKeyId)) {
               return HookProgressEvent.<CallbackContext>builder()
                       .status(HookStatus.FAILED)
                       .message("Server side encryption turned off for queue with name: " + queue.getQueueName())
                       .errorCode(HandlerErrorCode.ResourceConflict)
                       .build();
           }
   
           return HookProgressEvent.<CallbackContext>builder()
                       .status(HookStatus.SUCCESS)
                       .message("Successfully invoked PreCreateHookHandler for target: AWS::SQS::Queue")
                       .build();
       }
   }
   ```

### Der `preCreate` Test wird aktualisiert
<a name="updating-the-precreate-handler"></a>

1. Öffnen Sie in Ihrer IDE die `PreCreateHandlerTest.java` Datei, die sich im `src/test/java/software/mycompany/testing/mytesthook` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `PreCreateHandlerTest.java` Datei durch den folgenden Code.

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.google.common.collect.ImmutableMap;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3Bucket;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.BucketEncryption;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.ServerSideEncryptionByDefault;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.ServerSideEncryptionRule;
   import com.mycompany.testing.mytesthook.model.aws.sqs.queue.AwsSqsQueue;
   import org.junit.jupiter.api.BeforeEach;
   import org.junit.jupiter.api.Test;
   import org.junit.jupiter.api.extension.ExtendWith;
   import org.mockito.Mock;
   import org.mockito.junit.jupiter.MockitoExtension;
   import software.amazon.cloudformation.exceptions.UnsupportedTargetException;
   import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
   import software.amazon.cloudformation.proxy.HandlerErrorCode;
   import software.amazon.cloudformation.proxy.Logger;
   import software.amazon.cloudformation.proxy.hook.HookContext;
   import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
   import software.amazon.cloudformation.proxy.hook.HookProgressEvent;
   import software.amazon.cloudformation.proxy.hook.HookStatus;
   import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
   
   import java.util.Collections;
   import java.util.Map;
   
   import static org.assertj.core.api.Assertions.assertThat;
   import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
   import static org.mockito.Mockito.mock;
   
   @ExtendWith(MockitoExtension.class)
   public class PreCreateHookHandlerTest {
   
       @Mock
       private AmazonWebServicesClientProxy proxy;
   
       @Mock
       private Logger logger;
   
       @BeforeEach
       public void setup() {
           proxy = mock(AmazonWebServicesClientProxy.class);
           logger = mock(Logger.class);
       }
       
       @Test
       public void handleRequest_awsSqsQueueSuccess() {
           final PreCreateHookHandler handler = new PreCreateHookHandler();
   
           final AwsSqsQueue queue = buildSqsQueue("MyQueue", "KmsKey");
           final HookTargetModel targetModel = createHookTargetModel(queue);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::SQS::Queue").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.SUCCESS, "Successfully invoked PreCreateHookHandler for target: AWS::SQS::Queue");
       }
   
       @Test
       public void handleRequest_awsS3BucketSuccess() {
           final PreCreateHookHandler handler = new PreCreateHookHandler();
   
           final AwsS3Bucket bucket = buildAwsS3Bucket("amzn-s3-demo-bucket", true, "AES256", "KmsKey");
           final HookTargetModel targetModel = createHookTargetModel(bucket);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::S3::Bucket").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.SUCCESS, "Successfully invoked PreCreateHookHandler for target: AWS::S3::Bucket");
       }
   
       @Test
       public void handleRequest_awsS3BucketFail_bucketKeyNotEnabled() {
           final PreCreateHookHandler handler = new PreCreateHookHandler();
   
           final AwsS3Bucket bucket = buildAwsS3Bucket("amzn-s3-demo-bucket", false, "AES256", "KmsKey");
           final HookTargetModel targetModel = createHookTargetModel(bucket);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::S3::Bucket").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.FAILED, "Bucket key not enabled for bucket with name: amzn-s3-demo-bucket");
       }
   
       @Test
       public void handleRequest_awsS3BucketFail_incorrectSSEEncryptionAlgorithm() {
           final PreCreateHookHandler handler = new PreCreateHookHandler();
   
           final AwsS3Bucket bucket = buildAwsS3Bucket("amzn-s3-demo-bucket", true, "SHA512", "KmsKey");
           final HookTargetModel targetModel = createHookTargetModel(bucket);
          final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::S3::Bucket").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.FAILED, "SSE Encryption Algorithm is incorrect for bucket with name: amzn-s3-demo-bucket");
       }
   
       @Test
       public void handleRequest_awsS3BucketFail_kmsKeyIdNotSet() {
           final PreCreateHookHandler handler = new PreCreateHookHandler();
   
           final AwsS3Bucket bucket = buildAwsS3Bucket("amzn-s3-demo-bucket", true, "AES256", null);
           final HookTargetModel targetModel = createHookTargetModel(bucket);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::S3::Bucket").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.FAILED, "KMS Key ID not set for bucket with name: amzn-s3-demo-bucket");
       }
   
       @Test
       public void handleRequest_awsSqsQueueFail_serverSideEncryptionOff() {
           final PreCreateHookHandler handler = new PreCreateHookHandler();
   
           final AwsSqsQueue queue = buildSqsQueue("MyQueue", null);
           final HookTargetModel targetModel = createHookTargetModel(queue);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::SQS::Queue").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.FAILED, "Server side encryption turned off for queue with name: MyQueue");
       }
   
       @Test
       public void handleRequest_unsupportedTarget() {
           final PreCreateHookHandler handler = new PreCreateHookHandler();
   
           final Map<String, Object> unsupportedTarget = ImmutableMap.of("ResourceName", "MyUnsupportedTarget");
           final HookTargetModel targetModel = createHookTargetModel(unsupportedTarget);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::Unsupported::Target").targetModel(targetModel).build())
               .build();
   
           assertThatExceptionOfType(UnsupportedTargetException.class)
                   .isThrownBy(() -> handler.handleRequest(proxy, request, null, logger, typeConfiguration))
                   .withMessageContaining("Unsupported target")
                   .withMessageContaining("AWS::Unsupported::Target")
                   .satisfies(e -> assertThat(e.getErrorCode()).isEqualTo(HandlerErrorCode.InvalidRequest));
       }
   
       private void assertResponse(final HookProgressEvent<CallbackContext> response, final HookStatus expectedStatus, final String expectedErrorMsg) {
           assertThat(response).isNotNull();
           assertThat(response.getStatus()).isEqualTo(expectedStatus);
           assertThat(response.getCallbackContext()).isNull();
           assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
           assertThat(response.getMessage()).isNotNull();
           assertThat(response.getMessage()).isEqualTo(expectedErrorMsg);
       }
   
       private HookTargetModel createHookTargetModel(final Object resourceProperties) {
           return HookTargetModel.of(ImmutableMap.of("ResourceProperties", resourceProperties));
       }
   
       @SuppressWarnings("SameParameterValue")
       private AwsSqsQueue buildSqsQueue(final String queueName, final String kmsKeyId) {
           return AwsSqsQueue.builder()
                   .queueName(queueName)
                   .kmsMasterKeyId(kmsKeyId) // "KmsMasterKeyId" is name of the property for an AWS::SQS::Queue
                   .build();
       }
   
       @SuppressWarnings("SameParameterValue")
       private AwsS3Bucket buildAwsS3Bucket(
               final String bucketName,
               final Boolean bucketKeyEnabled,
               final String sseAlgorithm,
               final String kmsKeyId
       ) {
           return AwsS3Bucket.builder()
                   .bucketName(bucketName)
                   .bucketEncryption(
                       BucketEncryption.builder()
                           .serverSideEncryptionConfiguration(
                               Collections.singletonList(
                                   ServerSideEncryptionRule.builder()
                                       .bucketKeyEnabled(bucketKeyEnabled)
                                       .serverSideEncryptionByDefault(
                                           ServerSideEncryptionByDefault.builder()
                                               .sSEAlgorithm(sseAlgorithm)
                                               .kMSMasterKeyID(kmsKeyId) // "KMSMasterKeyID" is name of the property for an AWS::S3::Bucket
                                               .build()
                                       ).build()
                               )
                           ).build()
                   ).build();
       }
   }
   ```

### Implementierung des `preUpdate` Handlers
<a name="implementing-preupdate-handler"></a>

Implementieren Sie einen `preUpdate` Handler, der vor den Aktualisierungsoperationen für alle angegebenen Ziele im Handler initiiert. Der `preUpdate` Handler erreicht Folgendes:
+ Für eine `AWS::S3::Bucket` Ressource wird der Hook nur erfolgreich sein, wenn Folgendes zutrifft:
  + Der Bucket-Verschlüsselungsalgorithmus für einen Amazon S3 S3-Bucket wurde nicht geändert.

### Codierung des `preUpdate` Handlers
<a name="coding-preupdate-handler"></a>

1. Öffnen Sie in Ihrer IDE die `PreUpdateHookHandler.java` Datei, die sich im `src/main/java/software/mycompany/testing/mytesthook` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `PreUpdateHookHandler.java` Datei durch den folgenden Code.

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3Bucket;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3BucketTargetModel;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.ServerSideEncryptionRule;
   import org.apache.commons.lang3.StringUtils;
   import software.amazon.cloudformation.exceptions.UnsupportedTargetException;
   import software.amazon.cloudformation.proxy.HandlerErrorCode;
   import software.amazon.cloudformation.proxy.Logger;
   import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
   import software.amazon.cloudformation.proxy.hook.HookStatus;
   import software.amazon.cloudformation.proxy.hook.HookProgressEvent;
   import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
   import software.amazon.cloudformation.proxy.hook.targetmodel.ResourceHookTargetModel;
   
   import java.util.List;
   
   public class PreUpdateHookHandler extends BaseHookHandler<TypeConfigurationModel, CallbackContext> {
   
       @Override
       public HookProgressEvent<CallbackContext> handleRequest(
           final AmazonWebServicesClientProxy proxy,
           final HookHandlerRequest request,
           final CallbackContext callbackContext,
           final Logger logger,
           final TypeConfigurationModel typeConfiguration) {
   
           final String targetName = request.getHookContext().getTargetName();
           if ("AWS::S3::Bucket".equals(targetName)) {
               final ResourceHookTargetModel<AwsS3Bucket> targetModel = request.getHookContext().getTargetModel(AwsS3BucketTargetModel.class);
   
               final AwsS3Bucket bucketProperties = targetModel.getResourceProperties();
               final AwsS3Bucket previousBucketProperties = targetModel.getPreviousResourceProperties();
   
               return validateBucketEncryptionRulesNotUpdated(bucketProperties, previousBucketProperties);
           } else {
               throw new UnsupportedTargetException(targetName);
           }
       }
   
       private HookProgressEvent<CallbackContext> validateBucketEncryptionRulesNotUpdated(final AwsS3Bucket resourceProperties, final AwsS3Bucket previousResourceProperties) {
           final List<ServerSideEncryptionRule> bucketEncryptionConfigs = resourceProperties.getBucketEncryption().getServerSideEncryptionConfiguration();
           final List<ServerSideEncryptionRule> previousBucketEncryptionConfigs = previousResourceProperties.getBucketEncryption().getServerSideEncryptionConfiguration();
   
           if (bucketEncryptionConfigs.size() != previousBucketEncryptionConfigs.size()) {
               return HookProgressEvent.<CallbackContext>builder()
                       .status(HookStatus.FAILED)
                       .errorCode(HandlerErrorCode.NotUpdatable)
                       .message(
                           String.format(
                               "Current number of bucket encryption configs does not match previous. Current has %d configs while previously there were %d configs",
                               bucketEncryptionConfigs.size(),
                               previousBucketEncryptionConfigs.size()
                           )
                       ).build();
           }
   
           for (int i = 0; i < bucketEncryptionConfigs.size(); ++i) {
               final String currentEncryptionAlgorithm = bucketEncryptionConfigs.get(i).getServerSideEncryptionByDefault().getSSEAlgorithm();
               final String previousEncryptionAlgorithm = previousBucketEncryptionConfigs.get(i).getServerSideEncryptionByDefault().getSSEAlgorithm();
   
               if (!StringUtils.equals(currentEncryptionAlgorithm, previousEncryptionAlgorithm)) {
                   return HookProgressEvent.<CallbackContext>builder()
                       .status(HookStatus.FAILED)
                       .errorCode(HandlerErrorCode.NotUpdatable)
                       .message(
                           String.format(
                               "Bucket Encryption algorithm can not be changed once set. The encryption algorithm was changed to '%s' from '%s'.",
                               currentEncryptionAlgorithm,
                               previousEncryptionAlgorithm
                           )
                       )
                       .build();
               }
           }
   
           return HookProgressEvent.<CallbackContext>builder()
                       .status(HookStatus.SUCCESS)
                       .message("Successfully invoked PreUpdateHookHandler for target: AWS::SQS::Queue")
                       .build();
       }
   }
   ```

### Der `preUpdate` Test wird aktualisiert
<a name="update-the-preupdate-handler"></a>

1. Öffnen Sie in Ihrer IDE die `PreUpdateHandlerTest.java` Datei im `src/main/java/com/mycompany/testing/mytesthook` Ordner.

1. Ersetzen Sie den gesamten Inhalt der `PreUpdateHandlerTest.java` Datei durch den folgenden Code.

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.google.common.collect.ImmutableMap;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3Bucket;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.BucketEncryption;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.ServerSideEncryptionByDefault;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.ServerSideEncryptionRule;
   import org.junit.jupiter.api.BeforeEach;
   import org.junit.jupiter.api.Test;
   import org.junit.jupiter.api.extension.ExtendWith;
   import org.mockito.Mock;
   import org.mockito.junit.jupiter.MockitoExtension;
   import software.amazon.cloudformation.exceptions.UnsupportedTargetException;
   import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
   import software.amazon.cloudformation.proxy.HandlerErrorCode;
   import software.amazon.cloudformation.proxy.Logger;
   import software.amazon.cloudformation.proxy.hook.HookContext;
   import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
   import software.amazon.cloudformation.proxy.hook.HookProgressEvent;
   import software.amazon.cloudformation.proxy.hook.HookStatus;
   import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
   
   import java.util.Arrays;
   import java.util.stream.Stream;
   
   import static org.assertj.core.api.Assertions.assertThat;
   import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
   import static org.mockito.Mockito.mock;
   
   @ExtendWith(MockitoExtension.class)
   public class PreUpdateHookHandlerTest {
   
       @Mock
       private AmazonWebServicesClientProxy proxy;
   
       @Mock
       private Logger logger;
   
       @BeforeEach
       public void setup() {
           proxy = mock(AmazonWebServicesClientProxy.class);
           logger = mock(Logger.class);
       }
   
       @Test
       public void handleRequest_awsS3BucketSuccess() {
           final PreUpdateHookHandler handler = new PreUpdateHookHandler();
   
           final ServerSideEncryptionRule serverSideEncryptionRule = buildServerSideEncryptionRule("AES256");
           final AwsS3Bucket resourceProperties = buildAwsS3Bucket("amzn-s3-demo-bucket", serverSideEncryptionRule);
           final AwsS3Bucket previousResourceProperties = buildAwsS3Bucket("amzn-s3-demo-bucket", serverSideEncryptionRule);
           final HookTargetModel targetModel = createHookTargetModel(resourceProperties, previousResourceProperties);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::S3::Bucket").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.SUCCESS, "Successfully invoked PreUpdateHookHandler for target: AWS::SQS::Queue");
       }
   
       @Test
       public void handleRequest_awsS3BucketFail_bucketEncryptionConfigsDontMatch() {
           final PreUpdateHookHandler handler = new PreUpdateHookHandler();
   
           final ServerSideEncryptionRule[] serverSideEncryptionRules = Stream.of("AES256", "SHA512", "AES32")
                   .map(this::buildServerSideEncryptionRule)
                   .toArray(ServerSideEncryptionRule[]::new);
   
           final AwsS3Bucket resourceProperties = buildAwsS3Bucket("amzn-s3-demo-bucket", serverSideEncryptionRules[0]);
           final AwsS3Bucket previousResourceProperties = buildAwsS3Bucket("amzn-s3-demo-bucket", serverSideEncryptionRules);
           final HookTargetModel targetModel = createHookTargetModel(resourceProperties, previousResourceProperties);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::S3::Bucket").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.FAILED, "Current number of bucket encryption configs does not match previous. Current has 1 configs while previously there were 3 configs");
       }
   
       @Test
       public void handleRequest_awsS3BucketFail_bucketEncryptionAlgorithmDoesNotMatch() {
           final PreUpdateHookHandler handler = new PreUpdateHookHandler();
   
           final AwsS3Bucket resourceProperties = buildAwsS3Bucket("amzn-s3-demo-bucket", buildServerSideEncryptionRule("SHA512"));
           final AwsS3Bucket previousResourceProperties = buildAwsS3Bucket("amzn-s3-demo-bucket", buildServerSideEncryptionRule("AES256"));
           final HookTargetModel targetModel = createHookTargetModel(resourceProperties, previousResourceProperties);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::S3::Bucket").targetModel(targetModel).build())
               .build();
   
           final HookProgressEvent<CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
           assertResponse(response, HookStatus.FAILED, String.format("Bucket Encryption algorithm can not be changed once set. The encryption algorithm was changed to '%s' from '%s'.", "SHA512", "AES256"));
       }
   
       @Test
       public void handleRequest_unsupportedTarget() {
           final PreUpdateHookHandler handler = new PreUpdateHookHandler();
   
           final Object resourceProperties = ImmutableMap.of("FileSizeLimit", 256);
           final Object previousResourceProperties = ImmutableMap.of("FileSizeLimit", 512);
           final HookTargetModel targetModel = createHookTargetModel(resourceProperties, previousResourceProperties);
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder().encryptionAlgorithm("AES256").build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
               .hookContext(HookContext.builder().targetName("AWS::Unsupported::Target").targetModel(targetModel).build())
               .build();
   
           assertThatExceptionOfType(UnsupportedTargetException.class)
                   .isThrownBy(() -> handler.handleRequest(proxy, request, null, logger, typeConfiguration))
                   .withMessageContaining("Unsupported target")
                   .withMessageContaining("AWS::Unsupported::Target")
                   .satisfies(e -> assertThat(e.getErrorCode()).isEqualTo(HandlerErrorCode.InvalidRequest));
       }
   
       private void assertResponse(final HookProgressEvent<CallbackContext> response, final HookStatus expectedStatus, final String expectedErrorMsg) {
           assertThat(response).isNotNull();
           assertThat(response.getStatus()).isEqualTo(expectedStatus);
           assertThat(response.getCallbackContext()).isNull();
           assertThat(response.getCallbackDelaySeconds()).isEqualTo(0);
           assertThat(response.getMessage()).isNotNull();
           assertThat(response.getMessage()).isEqualTo(expectedErrorMsg);
       }
   
       private HookTargetModel createHookTargetModel(final Object resourceProperties, final Object previousResourceProperties) {
           return HookTargetModel.of(
                   ImmutableMap.of(
                       "ResourceProperties", resourceProperties,
                       "PreviousResourceProperties", previousResourceProperties
                   )
           );
       }
   
       @SuppressWarnings("SameParameterValue")
       private AwsS3Bucket buildAwsS3Bucket(
               final String bucketName,
               final ServerSideEncryptionRule ...serverSideEncryptionRules
               ) {
           return AwsS3Bucket.builder()
                   .bucketName(bucketName)
                   .bucketEncryption(
                       BucketEncryption.builder()
                           .serverSideEncryptionConfiguration(
                                   Arrays.asList(serverSideEncryptionRules)
                           ).build()
                   ).build();
       }
   
       private ServerSideEncryptionRule buildServerSideEncryptionRule(final String encryptionAlgorithm) {
           return ServerSideEncryptionRule.builder()
                   .bucketKeyEnabled(true)
                   .serverSideEncryptionByDefault(
                           ServerSideEncryptionByDefault.builder()
                                   .sSEAlgorithm(encryptionAlgorithm)
                                   .build()
                   ).build();
       }
   }
   ```

### Implementierung des `preDelete` Handlers
<a name="implement-the-predelete-hander"></a>

Implementieren Sie einen `preDelete` Handler, der vor den Löschvorgängen für alle angegebenen Ziele im Handler initiiert. Der `preDelete` Handler erreicht Folgendes:
+ Für eine `AWS::S3::Bucket` Ressource wird der Hook nur erfolgreich sein, wenn Folgendes zutrifft:
  + Überprüft, ob das Konto nach dem Löschen der Ressource über die erforderlichen Mindestressourcen für Beschwerden verfügt.
  + Die Mindestmenge an erforderlichen Ressourcen für Beschwerden ist in der Typkonfiguration des Hooks festgelegt.

### Codierung des `preDelete` Handlers
<a name="code-the-predelete-handler"></a>

1. Öffnen Sie in Ihrer IDE die `PreDeleteHookHandler.java` Datei im `src/main/java/com/mycompany/testing/mytesthook` Ordner.

1. Ersetzen Sie den gesamten Inhalt der `PreDeleteHookHandler.java` Datei durch den folgenden Code.

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.google.common.annotations.VisibleForTesting;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3Bucket;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3BucketTargetModel;
   import com.mycompany.testing.mytesthook.model.aws.sqs.queue.AwsSqsQueue;
   import com.mycompany.testing.mytesthook.model.aws.sqs.queue.AwsSqsQueueTargetModel;
   import org.apache.commons.lang3.StringUtils;
   import org.apache.commons.lang3.math.NumberUtils;
   import software.amazon.awssdk.services.cloudformation.CloudFormationClient;
   import software.amazon.awssdk.services.cloudformation.model.CloudFormationException;
   import software.amazon.awssdk.services.cloudformation.model.DescribeStackResourceRequest;
   import software.amazon.awssdk.services.s3.S3Client;
   import software.amazon.awssdk.services.s3.model.Bucket;
   import software.amazon.awssdk.services.s3.model.S3Exception;
   import software.amazon.awssdk.services.sqs.SqsClient;
   import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest;
   import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest;
   import software.amazon.awssdk.services.sqs.model.ListQueuesRequest;
   import software.amazon.awssdk.services.sqs.model.ListQueuesResponse;
   import software.amazon.awssdk.services.sqs.model.QueueAttributeName;
   import software.amazon.awssdk.services.sqs.model.SqsException;
   import software.amazon.cloudformation.exceptions.CfnGeneralServiceException;
   import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
   import software.amazon.cloudformation.proxy.HandlerErrorCode;
   import software.amazon.cloudformation.proxy.OperationStatus;
   import software.amazon.cloudformation.proxy.ProgressEvent;
   import software.amazon.cloudformation.proxy.ProxyClient;
   import software.amazon.cloudformation.proxy.hook.HookContext;
   import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
   import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
   import software.amazon.cloudformation.proxy.hook.targetmodel.ResourceHookTargetModel;
   
   import java.util.ArrayList;
   import java.util.Collection;
   import java.util.HashSet;
   import java.util.List;
   import java.util.Objects;
   import java.util.stream.Collectors;
   
   public class PreDeleteHookHandler extends BaseHookHandlerStd {
   
       private ProxyClient<S3Client> s3Client;
       private ProxyClient<SqsClient> sqsClient;
   
       @Override
       protected ProgressEvent<HookTargetModel, CallbackContext> handleS3BucketRequest(
               final AmazonWebServicesClientProxy proxy,
               final HookHandlerRequest request,
               final CallbackContext callbackContext,
               final ProxyClient<S3Client> proxyClient,
               final TypeConfigurationModel typeConfiguration
       ) {
           final HookContext hookContext = request.getHookContext();
           final String targetName = hookContext.getTargetName();
           if (!AwsS3Bucket.TYPE_NAME.equals(targetName)) {
               throw new RuntimeException(String.format("Request target type [%s] is not 'AWS::S3::Bucket'", targetName));
           }
           this.s3Client = proxyClient;
   
           final String encryptionAlgorithm = typeConfiguration.getEncryptionAlgorithm();
           final int minBuckets = NumberUtils.toInt(typeConfiguration.getMinBuckets());
   
           final ResourceHookTargetModel<AwsS3Bucket> targetModel = hookContext.getTargetModel(AwsS3BucketTargetModel.class);
           final List<String> buckets = listBuckets().stream()
                   .filter(b -> !StringUtils.equals(b, targetModel.getResourceProperties().getBucketName()))
                   .collect(Collectors.toList());
   
           final List<String> compliantBuckets = new ArrayList<>();
           for (final String bucket : buckets) {
               if (getBucketSSEAlgorithm(bucket).contains(encryptionAlgorithm)) {
                   compliantBuckets.add(bucket);
               }
   
               if (compliantBuckets.size() >= minBuckets) {
                   return ProgressEvent.<HookTargetModel, CallbackContext>builder()
                           .status(OperationStatus.SUCCESS)
                           .message("Successfully invoked PreDeleteHookHandler for target: AWS::S3::Bucket")
                           .build();
               }
           }
   
           return ProgressEvent.<HookTargetModel, CallbackContext>builder()
                   .status(OperationStatus.FAILED)
                   .errorCode(HandlerErrorCode.NonCompliant)
                   .message(String.format("Failed to meet minimum of [%d] encrypted buckets.", minBuckets))
                   .build();
       }
   
       @Override
       protected ProgressEvent<HookTargetModel, CallbackContext> handleSqsQueueRequest(
               final AmazonWebServicesClientProxy proxy,
               final HookHandlerRequest request,
               final CallbackContext callbackContext,
               final ProxyClient<SqsClient> proxyClient,
               final TypeConfigurationModel typeConfiguration
       ) {
           final HookContext hookContext = request.getHookContext();
           final String targetName = hookContext.getTargetName();
           if (!AwsSqsQueue.TYPE_NAME.equals(targetName)) {
               throw new RuntimeException(String.format("Request target type [%s] is not 'AWS::SQS::Queue'", targetName));
           }
           this.sqsClient = proxyClient;
           final int minQueues = NumberUtils.toInt(typeConfiguration.getMinQueues());
   
           final ResourceHookTargetModel<AwsSqsQueue> targetModel = hookContext.getTargetModel(AwsSqsQueueTargetModel.class);
   
           final String queueName = Objects.toString(targetModel.getResourceProperties().get("QueueName"), null);
   
           String targetQueueUrl = null;
           if (queueName != null) {
               try {
                   targetQueueUrl = sqsClient.injectCredentialsAndInvokeV2(
                           GetQueueUrlRequest.builder().queueName(
                                   queueName
                           ).build(),
                           sqsClient.client()::getQueueUrl
                   ).queueUrl();
               } catch (SqsException e) {
                   log(String.format("Error while calling GetQueueUrl API for queue name [%s]: %s", queueName, e.getMessage()));
               }
           } else {
               log("Queue name is empty, attempting to get queue's physical ID");
               try {
                   final ProxyClient<CloudFormationClient> cfnClient = proxy.newProxy(ClientBuilder::createCloudFormationClient);
                   targetQueueUrl = cfnClient.injectCredentialsAndInvokeV2(
                           DescribeStackResourceRequest.builder()
                                   .stackName(hookContext.getTargetLogicalId())
                                   .logicalResourceId(hookContext.getTargetLogicalId())
                                   .build(),
                           cfnClient.client()::describeStackResource
                   ).stackResourceDetail().physicalResourceId();
               } catch (CloudFormationException e) {
                   log(String.format("Error while calling DescribeStackResource API for queue name: %s", e.getMessage()));
               }
           }
   
           // Creating final variable for the filter lambda
           final String finalTargetQueueUrl = targetQueueUrl;
   
           final List<String> compliantQueues = new ArrayList<>();
   
           String nextToken = null;
           do {
               final ListQueuesRequest req = Translator.createListQueuesRequest(nextToken);
               final ListQueuesResponse res = sqsClient.injectCredentialsAndInvokeV2(req, sqsClient.client()::listQueues);
               final List<String> queueUrls = res.queueUrls().stream()
                       .filter(q -> !StringUtils.equals(q, finalTargetQueueUrl))
                       .collect(Collectors.toList());
   
               for (final String queueUrl : queueUrls) {
                   if (isQueueEncrypted(queueUrl)) {
                       compliantQueues.add(queueUrl);
                   }
   
                   if (compliantQueues.size() >= minQueues) {
                       return ProgressEvent.<HookTargetModel, CallbackContext>builder()
                           .status(OperationStatus.SUCCESS)
                           .message("Successfully invoked PreDeleteHookHandler for target: AWS::SQS::Queue")
                           .build();
                   }
                   nextToken = res.nextToken();
               }
           } while (nextToken != null);
   
           return ProgressEvent.<HookTargetModel, CallbackContext>builder()
                   .status(OperationStatus.FAILED)
                   .errorCode(HandlerErrorCode.NonCompliant)
                   .message(String.format("Failed to meet minimum of [%d] encrypted queues.", minQueues))
                   .build();
       }
   
       private List<String> listBuckets() {
           try {
               return s3Client.injectCredentialsAndInvokeV2(Translator.createListBucketsRequest(), s3Client.client()::listBuckets)
                       .buckets()
                       .stream()
                       .map(Bucket::name)
                       .collect(Collectors.toList());
           } catch (S3Exception e) {
               throw new CfnGeneralServiceException("Error while calling S3 ListBuckets API", e);
           }
       }
   
       @VisibleForTesting
       Collection<String> getBucketSSEAlgorithm(final String bucket) {
           try {
               return s3Client.injectCredentialsAndInvokeV2(Translator.createGetBucketEncryptionRequest(bucket), s3Client.client()::getBucketEncryption)
                       .serverSideEncryptionConfiguration()
                       .rules()
                       .stream()
                       .filter(r -> Objects.nonNull(r.applyServerSideEncryptionByDefault()))
                       .map(r -> r.applyServerSideEncryptionByDefault().sseAlgorithmAsString())
                       .collect(Collectors.toSet());
           } catch (S3Exception e) {
               return new HashSet<>();
           }
       }
   
       @VisibleForTesting
       boolean isQueueEncrypted(final String queueUrl) {
           try {
               final GetQueueAttributesRequest request = GetQueueAttributesRequest.builder()
                       .queueUrl(queueUrl)
                       .attributeNames(QueueAttributeName.KMS_MASTER_KEY_ID)
                       .build();
               final String kmsKeyId = sqsClient.injectCredentialsAndInvokeV2(request, sqsClient.client()::getQueueAttributes)
                       .attributes()
                       .get(QueueAttributeName.KMS_MASTER_KEY_ID);
   
               return StringUtils.isNotBlank(kmsKeyId);
           } catch (SqsException e) {
               throw new CfnGeneralServiceException("Error while calling SQS GetQueueAttributes API", e);
           }
       }
   }
   ```

### Den `preDelete` Handler aktualisieren
<a name="update-the-predelete-handler"></a>

1. Öffnen Sie in Ihrer IDE die `PreDeleteHookHandler.java` Datei im `src/main/java/com/mycompany/testing/mytesthook` Ordner.

1. Ersetzen Sie den gesamten Inhalt der `PreDeleteHookHandler.java` Datei durch den folgenden Code.

   ```
   package com.mycompany.testing.mytesthook;
   
   import com.google.common.collect.ImmutableList;
   import com.google.common.collect.ImmutableMap;
   import com.mycompany.testing.mytesthook.model.aws.s3.bucket.AwsS3Bucket;
   import org.junit.jupiter.api.BeforeEach;
   import org.junit.jupiter.api.Test;
   import org.junit.jupiter.api.extension.ExtendWith;
   import org.mockito.Mock;
   import org.mockito.Mockito;
   import org.mockito.junit.jupiter.MockitoExtension;
   import software.amazon.awssdk.services.s3.S3Client;
   import software.amazon.awssdk.services.s3.model.Bucket;
   import software.amazon.awssdk.services.s3.model.GetBucketEncryptionRequest;
   import software.amazon.awssdk.services.s3.model.GetBucketEncryptionResponse;
   import software.amazon.awssdk.services.s3.model.ListBucketsRequest;
   import software.amazon.awssdk.services.s3.model.ListBucketsResponse;
   import software.amazon.awssdk.services.s3.model.S3Exception;
   import software.amazon.awssdk.services.s3.model.ServerSideEncryptionByDefault;
   import software.amazon.awssdk.services.s3.model.ServerSideEncryptionConfiguration;
   import software.amazon.awssdk.services.s3.model.ServerSideEncryptionRule;
   import software.amazon.awssdk.services.sqs.SqsClient;
   import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest;
   import software.amazon.awssdk.services.sqs.model.GetQueueAttributesResponse;
   import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest;
   import software.amazon.awssdk.services.sqs.model.GetQueueUrlResponse;
   import software.amazon.awssdk.services.sqs.model.ListQueuesRequest;
   import software.amazon.awssdk.services.sqs.model.ListQueuesResponse;
   import software.amazon.awssdk.services.sqs.model.QueueAttributeName;
   import software.amazon.cloudformation.proxy.Logger;
   import software.amazon.cloudformation.proxy.OperationStatus;
   import software.amazon.cloudformation.proxy.ProgressEvent;
   import software.amazon.cloudformation.proxy.hook.HookContext;
   import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
   import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
   
   import java.util.Arrays;
   import java.util.Collection;
   import java.util.HashMap;
   import java.util.List;
   import java.util.stream.Collectors;
   
   import static org.mockito.ArgumentMatchers.any;
   import static org.mockito.Mockito.mock;
   import static org.mockito.Mockito.never;
   import static org.mockito.Mockito.times;
   import static org.mockito.Mockito.verify;
   import static org.mockito.Mockito.when;
   
   @ExtendWith(MockitoExtension.class)
   public class PreDeleteHookHandlerTest extends AbstractTestBase {
   
       @Mock private S3Client s3Client;
       @Mock private SqsClient sqsClient;
       @Mock private Logger logger;
   
       @BeforeEach
       public void setup() {
           s3Client = mock(S3Client.class);
           sqsClient = mock(SqsClient.class);
           logger = mock(Logger.class);
       }
   
       @Test
       public void handleRequest_awsS3BucketSuccess() {
           final PreDeleteHookHandler handler = Mockito.spy(new PreDeleteHookHandler());
   
           final List<Bucket> bucketList = ImmutableList.of(
                   Bucket.builder().name("bucket1").build(),
                   Bucket.builder().name("bucket2").build(),
                   Bucket.builder().name("toBeDeletedBucket").build(),
                   Bucket.builder().name("bucket3").build(),
                   Bucket.builder().name("bucket4").build(),
                   Bucket.builder().name("bucket5").build()
           );
           final ListBucketsResponse mockResponse = ListBucketsResponse.builder().buckets(bucketList).build();
           when(s3Client.listBuckets(any(ListBucketsRequest.class))).thenReturn(mockResponse);
           when(s3Client.getBucketEncryption(any(GetBucketEncryptionRequest.class)))
                   .thenReturn(buildGetBucketEncryptionResponse("AES256"))
                   .thenReturn(buildGetBucketEncryptionResponse("AES256", "aws:kms"))
                   .thenThrow(S3Exception.builder().message("No Encrypt").build())
                   .thenReturn(buildGetBucketEncryptionResponse("aws:kms"))
                   .thenReturn(buildGetBucketEncryptionResponse("AES256"));
           setServiceClient(s3Client);
   
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder()
                   .encryptionAlgorithm("AES256")
                   .minBuckets("3")
                   .build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
                   .hookContext(
                       HookContext.builder()
                           .targetName("AWS::S3::Bucket")
                           .targetModel(
                               createHookTargetModel(
                                   AwsS3Bucket.builder()
                                       .bucketName("toBeDeletedBucket")
                                       .build()
                               )
                           )
                           .build())
                   .build();
   
           final ProgressEvent<HookTargetModel, CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
   
           verify(s3Client, times(5)).getBucketEncryption(any(GetBucketEncryptionRequest.class));
           verify(handler, never()).getBucketSSEAlgorithm("toBeDeletedBucket");
   
           assertResponse(response, OperationStatus.SUCCESS, "Successfully invoked PreDeleteHookHandler for target: AWS::S3::Bucket");
       }
   
   
       @Test
       public void handleRequest_awsSqsQueueSuccess() {
           final PreDeleteHookHandler handler = Mockito.spy(new PreDeleteHookHandler());
   
           final List<String> queueUrls = ImmutableList.of(
                   "https://queue1.queue",
                   "https://queue2.queue",
                   "https://toBeDeletedQueue.queue",
                   "https://queue3.queue",
                   "https://queue4.queue",
                   "https://queue5.queue"
           );
   
           when(sqsClient.getQueueUrl(any(GetQueueUrlRequest.class)))
                   .thenReturn(GetQueueUrlResponse.builder().queueUrl("https://toBeDeletedQueue.queue").build());
           when(sqsClient.listQueues(any(ListQueuesRequest.class)))
                   .thenReturn(ListQueuesResponse.builder().queueUrls(queueUrls).build());
           when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class)))
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(ImmutableMap.of(QueueAttributeName.KMS_MASTER_KEY_ID, "kmsKeyId")).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(new HashMap<>()).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(ImmutableMap.of(QueueAttributeName.KMS_MASTER_KEY_ID, "kmsKeyId")).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(new HashMap<>()).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(ImmutableMap.of(QueueAttributeName.KMS_MASTER_KEY_ID, "kmsKeyId")).build());
           setServiceClient(sqsClient);
   
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder()
                   .minQueues("3")
                   .build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
                   .hookContext(
                       HookContext.builder()
                           .targetName("AWS::SQS::Queue")
                           .targetModel(
                               createHookTargetModel(
                                   ImmutableMap.of("QueueName", "toBeDeletedQueue")
                               )
                           )
                           .build())
                   .build();
   
           final ProgressEvent<HookTargetModel, CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
   
           verify(sqsClient, times(5)).getQueueAttributes(any(GetQueueAttributesRequest.class));
           verify(handler, never()).isQueueEncrypted("toBeDeletedQueue");
   
           assertResponse(response, OperationStatus.SUCCESS, "Successfully invoked PreDeleteHookHandler for target: AWS::SQS::Queue");
       }
   
       @Test
       public void handleRequest_awsS3BucketFailed() {
           final PreDeleteHookHandler handler = Mockito.spy(new PreDeleteHookHandler());
   
           final List<Bucket> bucketList = ImmutableList.of(
                   Bucket.builder().name("bucket1").build(),
                   Bucket.builder().name("bucket2").build(),
                   Bucket.builder().name("toBeDeletedBucket").build(),
                   Bucket.builder().name("bucket3").build(),
                   Bucket.builder().name("bucket4").build(),
                   Bucket.builder().name("bucket5").build()
           );
           final ListBucketsResponse mockResponse = ListBucketsResponse.builder().buckets(bucketList).build();
           when(s3Client.listBuckets(any(ListBucketsRequest.class))).thenReturn(mockResponse);
           when(s3Client.getBucketEncryption(any(GetBucketEncryptionRequest.class)))
                   .thenReturn(buildGetBucketEncryptionResponse("AES256"))
                   .thenReturn(buildGetBucketEncryptionResponse("AES256", "aws:kms"))
                   .thenThrow(S3Exception.builder().message("No Encrypt").build())
                   .thenReturn(buildGetBucketEncryptionResponse("aws:kms"))
                   .thenReturn(buildGetBucketEncryptionResponse("AES256"));
           setServiceClient(s3Client);
   
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder()
                   .encryptionAlgorithm("AES256")
                   .minBuckets("10")
                   .build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
                   .hookContext(
                       HookContext.builder()
                           .targetName("AWS::S3::Bucket")
                           .targetModel(
                               createHookTargetModel(
                                   AwsS3Bucket.builder()
                                       .bucketName("toBeDeletedBucket")
                                       .build()
                               )
                           )
                           .build())
                   .build();
   
           final ProgressEvent<HookTargetModel, CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
   
           verify(s3Client, times(5)).getBucketEncryption(any(GetBucketEncryptionRequest.class));
           verify(handler, never()).getBucketSSEAlgorithm("toBeDeletedBucket");
   
           assertResponse(response, OperationStatus.FAILED, "Failed to meet minimum of [10] encrypted buckets.");
       }
   
   
       @Test
       public void handleRequest_awsSqsQueueFailed() {
           final PreDeleteHookHandler handler = Mockito.spy(new PreDeleteHookHandler());
   
           final List<String> queueUrls = ImmutableList.of(
                   "https://queue1.queue",
                   "https://queue2.queue",
                   "https://toBeDeletedQueue.queue",
                   "https://queue3.queue",
                   "https://queue4.queue",
                   "https://queue5.queue"
           );
   
           when(sqsClient.getQueueUrl(any(GetQueueUrlRequest.class)))
                   .thenReturn(GetQueueUrlResponse.builder().queueUrl("https://toBeDeletedQueue.queue").build());
           when(sqsClient.listQueues(any(ListQueuesRequest.class)))
                   .thenReturn(ListQueuesResponse.builder().queueUrls(queueUrls).build());
           when(sqsClient.getQueueAttributes(any(GetQueueAttributesRequest.class)))
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(ImmutableMap.of(QueueAttributeName.KMS_MASTER_KEY_ID, "kmsKeyId")).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(new HashMap<>()).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(ImmutableMap.of(QueueAttributeName.KMS_MASTER_KEY_ID, "kmsKeyId")).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(new HashMap<>()).build())
                   .thenReturn(GetQueueAttributesResponse.builder().attributes(ImmutableMap.of(QueueAttributeName.KMS_MASTER_KEY_ID, "kmsKeyId")).build());
           setServiceClient(sqsClient);
   
           final TypeConfigurationModel typeConfiguration = TypeConfigurationModel.builder()
                   .minQueues("10")
                   .build();
   
           final HookHandlerRequest request = HookHandlerRequest.builder()
                   .hookContext(
                       HookContext.builder()
                           .targetName("AWS::SQS::Queue")
                           .targetModel(
                               createHookTargetModel(
                                   ImmutableMap.of("QueueName", "toBeDeletedQueue")
                               )
                           )
                           .build())
                   .build();
   
           final ProgressEvent<HookTargetModel, CallbackContext> response = handler.handleRequest(proxy, request, null, logger, typeConfiguration);
   
           verify(sqsClient, times(5)).getQueueAttributes(any(GetQueueAttributesRequest.class));
           verify(handler, never()).isQueueEncrypted("toBeDeletedQueue");
   
           assertResponse(response, OperationStatus.FAILED, "Failed to meet minimum of [10] encrypted queues.");
       }
   
       private GetBucketEncryptionResponse buildGetBucketEncryptionResponse(final String ...sseAlgorithm) {
           return buildGetBucketEncryptionResponse(
                   Arrays.stream(sseAlgorithm)
                       .map(a -> ServerSideEncryptionRule.builder().applyServerSideEncryptionByDefault(
                           ServerSideEncryptionByDefault.builder()
                               .sseAlgorithm(a)
                               .build()
                           ).build()
                       )
                       .collect(Collectors.toList())
           );
       }
   
       private GetBucketEncryptionResponse buildGetBucketEncryptionResponse(final Collection<ServerSideEncryptionRule> rules) {
           return GetBucketEncryptionResponse.builder()
                   .serverSideEncryptionConfiguration(
                       ServerSideEncryptionConfiguration.builder().rules(
                           rules
                       ).build()
                   ).build();
       }
   }
   ```

# Modellieren von benutzerdefinierten CloudFormation Hooks mit Python
<a name="hooks-model-python"></a>

Die Modellierung von benutzerdefinierten CloudFormation Hooks beinhaltet die Erstellung eines Schemas, das den Hook, seine Eigenschaften und seine Attribute definiert. Dieses Tutorial führt Sie durch die Modellierung benutzerdefinierter Hooks mit Python.

## Schritt 1: Generieren Sie das Hook-Projektpaket
<a name="model-hook-project-package-python"></a>

Generieren Sie Ihr Hook-Projektpaket. Die CloudFormation CLI erstellt leere Handler-Funktionen, die bestimmten Hook-Aktionen im Ziellebenszyklus entsprechen, wie in der Hook-Spezifikation definiert.

```
cfn generate
```

Der Befehl gibt die folgende Ausgabe zurück.

```
Generated files for MyCompany::Testing::MyTestHook
```

**Anmerkung**  
Stellen Sie sicher, dass Ihre Lambda-Laufzeiten up-to-date die Verwendung einer veralteten Version vermeiden. Weitere Informationen finden Sie unter [Lambda-Laufzeiten für Ressourcentypen und Hooks aktualisieren](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/runtime-update.html).

## Schritt 2: Hook-Handler hinzufügen
<a name="model-hook-project-add-handler"></a>

Fügen Sie Ihren eigenen Hook-Handler-Laufzeitcode zu den Handlern hinzu, die Sie implementieren möchten. Sie können beispielsweise den folgenden Code für die Protokollierung hinzufügen.

```
LOG.setLevel(logging.INFO)
LOG.info("Internal testing Hook triggered for target: " + request.hookContext.targetName);
```

Die CloudFormation CLI generiert die `src/models.py` Datei aus dem[Referenz der Syntax des Hook-Konfigurationsschemas](hook-configuration-schema.md).

**Example models.py**  

```
import sys
from dataclasses import dataclass
from inspect import getmembers, isclass
from typing import (
    AbstractSet,
    Any,
    Generic,
    Mapping,
    MutableMapping,
    Optional,
    Sequence,
    Type,
    TypeVar,
)

from cloudformation_cli_python_lib.interface import (
    BaseModel,
    BaseHookHandlerRequest,
)
from cloudformation_cli_python_lib.recast import recast_object
from cloudformation_cli_python_lib.utils import deserialize_list

T = TypeVar("T")


def set_or_none(value: Optional[Sequence[T]]) -> Optional[AbstractSet[T]]:
    if value:
        return set(value)
    return None


@dataclass
class HookHandlerRequest(BaseHookHandlerRequest):
    pass


@dataclass
class TypeConfigurationModel(BaseModel):
    limitSize: Optional[str]
    cidr: Optional[str]
    encryptionAlgorithm: Optional[str]

    @classmethod
    def _deserialize(
        cls: Type["_TypeConfigurationModel"],
        json_data: Optional[Mapping[str, Any]],
    ) -> Optional["_TypeConfigurationModel"]:
        if not json_data:
            return None
        return cls(
            limitSize=json_data.get("limitSize"),
            cidr=json_data.get("cidr"),
            encryptionAlgorithm=json_data.get("encryptionAlgorithm"),
        )


_TypeConfigurationModel = TypeConfigurationModel
```

## Schritt 3: Implementieren Sie Hook-Handler
<a name="model-hook-project-code-handler-python"></a>

Mit den generierten Python-Datenklassen können Sie die Handler schreiben, die die Funktionalität des Hooks tatsächlich implementieren. In diesem Beispiel implementieren Sie die `preDelete` Aufrufpunkte `preCreate``preUpdate`, und für die Handler.

**Topics**
+ [Implementieren Sie den PreCreate-Handler](#model-hook-project-code-handler-python-precreate)
+ [Implementieren Sie den PreUpdate-Handler](#model-hook-project-code-handler-python-preupdate)
+ [Implementieren Sie den PreDelete-Handler](#model-hook-project-code-handler-python-predelete)
+ [Implementieren Sie einen Hook-Handler](#model-hook-project-code-handler-python-hook-handler)

### Implementieren Sie den PreCreate-Handler
<a name="model-hook-project-code-handler-python-precreate"></a>

Der `preCreate` Handler überprüft die serverseitigen Verschlüsselungseinstellungen für eine `AWS::S3::Bucket ` Oder-Ressource. `AWS::SQS::Queue`
+ Für eine `AWS::S3::Bucket` Ressource ist der Hook nur erfolgreich, wenn Folgendes zutrifft.
  + Die Amazon S3 S3-Bucket-Verschlüsselung ist eingestellt.
  + Der Amazon S3 S3-Bucket-Schlüssel ist für den Bucket aktiviert.
  + Der für den Amazon S3 S3-Bucket festgelegte Verschlüsselungsalgorithmus ist der richtige erforderliche Algorithmus.
  + Die AWS Key Management Service Schlüssel-ID ist festgelegt.
+ Für eine `AWS::SQS::Queue` Ressource wird der Hook nur erfolgreich sein, wenn Folgendes zutrifft.
  + Die AWS Key Management Service Schlüssel-ID ist gesetzt.

### Implementieren Sie den PreUpdate-Handler
<a name="model-hook-project-code-handler-python-preupdate"></a>

Implementieren Sie einen `preUpdate` Handler, der vor den Aktualisierungsvorgängen für alle angegebenen Ziele im Handler initiiert. Der `preUpdate` Handler erreicht Folgendes:
+ Für eine `AWS::S3::Bucket` Ressource wird der Hook nur erfolgreich sein, wenn Folgendes zutrifft:
  + Der Bucket-Verschlüsselungsalgorithmus für einen Amazon S3 S3-Bucket wurde nicht geändert.

### Implementieren Sie den PreDelete-Handler
<a name="model-hook-project-code-handler-python-predelete"></a>

Implementieren Sie einen `preDelete` Handler, der vor den Löschvorgängen für alle angegebenen Ziele im Handler initiiert. Der `preDelete` Handler erreicht Folgendes:
+ Für eine `AWS::S3::Bucket` Ressource wird der Hook nur erfolgreich sein, wenn Folgendes zutrifft:
  + Überprüft, ob nach dem Löschen der Ressource die mindestens erforderlichen konformen Ressourcen im Konto vorhanden sind.
  + Die erforderliche Mindestmenge an konformen Ressourcen ist in der Konfiguration des Hooks festgelegt.

### Implementieren Sie einen Hook-Handler
<a name="model-hook-project-code-handler-python-hook-handler"></a>

1. Öffnen Sie in Ihrer IDE die `handlers.py` Datei, die sich im `src` Ordner befindet.

1. Ersetzen Sie den gesamten Inhalt der `handlers.py` Datei durch den folgenden Code.  
**Example handlers.py**  

   ```
   import logging
   from typing import Any, MutableMapping, Optional
   import botocore
   
   from cloudformation_cli_python_lib import (
       BaseHookHandlerRequest,
       HandlerErrorCode,
       Hook,
       HookInvocationPoint,
       OperationStatus,
       ProgressEvent,
       SessionProxy,
       exceptions,
   )
   
   from .models import HookHandlerRequest, TypeConfigurationModel
   
   # Use this logger to forward log messages to CloudWatch Logs.
   LOG = logging.getLogger(__name__)
   TYPE_NAME = "MyCompany::Testing::MyTestHook"
   
   LOG.setLevel(logging.INFO)
   
   hook = Hook(TYPE_NAME, TypeConfigurationModel)
   test_entrypoint = hook.test_entrypoint
   
   
   def _validate_s3_bucket_encryption(
       bucket: MutableMapping[str, Any], required_encryption_algorithm: str
   ) -> ProgressEvent:
       status = None
       message = ""
       error_code = None
   
       if bucket:
           bucket_name = bucket.get("BucketName")
   
           bucket_encryption = bucket.get("BucketEncryption")
           if bucket_encryption:
               server_side_encryption_rules = bucket_encryption.get(
                   "ServerSideEncryptionConfiguration"
               )
               if server_side_encryption_rules:
                   for rule in server_side_encryption_rules:
                       bucket_key_enabled = rule.get("BucketKeyEnabled")
                       if bucket_key_enabled:
                           server_side_encryption_by_default = rule.get(
                               "ServerSideEncryptionByDefault"
                           )
   
                           encryption_algorithm = server_side_encryption_by_default.get(
                               "SSEAlgorithm"
                           )
                           kms_key_id = server_side_encryption_by_default.get(
                               "KMSMasterKeyID"
                           )  # "KMSMasterKeyID" is name of the property for an AWS::S3::Bucket
   
                           if encryption_algorithm == required_encryption_algorithm:
                               if encryption_algorithm == "aws:kms" and not kms_key_id:
                                   status = OperationStatus.FAILED
                                   message = f"KMS Key ID not set for bucket with name: f{bucket_name}"
                               else:
                                   status = OperationStatus.SUCCESS
                                   message = f"Successfully invoked PreCreateHookHandler for AWS::S3::Bucket with name: {bucket_name}"
                           else:
                               status = OperationStatus.FAILED
                               message = f"SSE Encryption Algorithm is incorrect for bucket with name: {bucket_name}"
                       else:
                           status = OperationStatus.FAILED
                           message = f"Bucket key not enabled for bucket with name: {bucket_name}"
   
                       if status == OperationStatus.FAILED:
                           break
               else:
                   status = OperationStatus.FAILED
                   message = f"No SSE Encryption configurations for bucket with name: {bucket_name}"
           else:
               status = OperationStatus.FAILED
               message = (
                   f"Bucket Encryption not enabled for bucket with name: {bucket_name}"
               )
       else:
           status = OperationStatus.FAILED
           message = "Resource properties for S3 Bucket target model are empty"
   
       if status == OperationStatus.FAILED:
           error_code = HandlerErrorCode.NonCompliant
   
       return ProgressEvent(status=status, message=message, errorCode=error_code)
   
   
   def _validate_sqs_queue_encryption(queue: MutableMapping[str, Any]) -> ProgressEvent:
       if not queue:
           return ProgressEvent(
               status=OperationStatus.FAILED,
               message="Resource properties for SQS Queue target model are empty",
               errorCode=HandlerErrorCode.NonCompliant,
           )
       queue_name = queue.get("QueueName")
   
       kms_key_id = queue.get(
           "KmsMasterKeyId"
       )  # "KmsMasterKeyId" is name of the property for an AWS::SQS::Queue
       if not kms_key_id:
           return ProgressEvent(
               status=OperationStatus.FAILED,
               message=f"Server side encryption turned off for queue with name: {queue_name}",
               errorCode=HandlerErrorCode.NonCompliant,
           )
   
       return ProgressEvent(
           status=OperationStatus.SUCCESS,
           message=f"Successfully invoked PreCreateHookHandler for targetAWS::SQS::Queue with name: {queue_name}",
       )
   
   
   @hook.handler(HookInvocationPoint.CREATE_PRE_PROVISION)
   def pre_create_handler(
       session: Optional[SessionProxy],
       request: HookHandlerRequest,
       callback_context: MutableMapping[str, Any],
       type_configuration: TypeConfigurationModel,
   ) -> ProgressEvent:
       target_name = request.hookContext.targetName
       if "AWS::S3::Bucket" == target_name:
           return _validate_s3_bucket_encryption(
               request.hookContext.targetModel.get("resourceProperties"),
               type_configuration.encryptionAlgorithm,
           )
       elif "AWS::SQS::Queue" == target_name:
           return _validate_sqs_queue_encryption(
               request.hookContext.targetModel.get("resourceProperties")
           )
       else:
           raise exceptions.InvalidRequest(f"Unknown target type: {target_name}")
   
   
   def _validate_bucket_encryption_rules_not_updated(
       resource_properties, previous_resource_properties
   ) -> ProgressEvent:
       bucket_encryption_configs = resource_properties.get("BucketEncryption", {}).get(
           "ServerSideEncryptionConfiguration", []
       )
       previous_bucket_encryption_configs = previous_resource_properties.get(
           "BucketEncryption", {}
       ).get("ServerSideEncryptionConfiguration", [])
   
       if len(bucket_encryption_configs) != len(previous_bucket_encryption_configs):
           return ProgressEvent(
               status=OperationStatus.FAILED,
               message=f"Current number of bucket encryption configs does not match previous. Current has {str(len(bucket_encryption_configs))} configs while previously there were {str(len(previous_bucket_encryption_configs))} configs",
               errorCode=HandlerErrorCode.NonCompliant,
           )
   
       for i in range(len(bucket_encryption_configs)):
           current_encryption_algorithm = (
               bucket_encryption_configs[i]
               .get("ServerSideEncryptionByDefault", {})
               .get("SSEAlgorithm")
           )
           previous_encryption_algorithm = (
               previous_bucket_encryption_configs[i]
               .get("ServerSideEncryptionByDefault", {})
               .get("SSEAlgorithm")
           )
   
           if current_encryption_algorithm != previous_encryption_algorithm:
               return ProgressEvent(
                   status=OperationStatus.FAILED,
                   message=f"Bucket Encryption algorithm can not be changed once set. The encryption algorithm was changed to {current_encryption_algorithm} from {previous_encryption_algorithm}.",
                   errorCode=HandlerErrorCode.NonCompliant,
               )
   
       return ProgressEvent(
           status=OperationStatus.SUCCESS,
           message="Successfully invoked PreUpdateHookHandler for target: AWS::SQS::Queue",
       )
   
   
   def _validate_queue_encryption_not_disabled(
       resource_properties, previous_resource_properties
   ) -> ProgressEvent:
       if previous_resource_properties.get(
           "KmsMasterKeyId"
       ) and not resource_properties.get("KmsMasterKeyId"):
           return ProgressEvent(
               status=OperationStatus.FAILED,
               errorCode=HandlerErrorCode.NonCompliant,
               message="Queue encryption can not be disable",
           )
       else:
           return ProgressEvent(status=OperationStatus.SUCCESS)
   
   
   @hook.handler(HookInvocationPoint.UPDATE_PRE_PROVISION)
   def pre_update_handler(
       session: Optional[SessionProxy],
       request: BaseHookHandlerRequest,
       callback_context: MutableMapping[str, Any],
       type_configuration: MutableMapping[str, Any],
   ) -> ProgressEvent:
       target_name = request.hookContext.targetName
       if "AWS::S3::Bucket" == target_name:
           resource_properties = request.hookContext.targetModel.get("resourceProperties")
           previous_resource_properties = request.hookContext.targetModel.get(
               "previousResourceProperties"
           )
   
           return _validate_bucket_encryption_rules_not_updated(
               resource_properties, previous_resource_properties
           )
       elif "AWS::SQS::Queue" == target_name:
           resource_properties = request.hookContext.targetModel.get("resourceProperties")
           previous_resource_properties = request.hookContext.targetModel.get(
               "previousResourceProperties"
           )
   
           return _validate_queue_encryption_not_disabled(
               resource_properties, previous_resource_properties
           )
       else:
           raise exceptions.InvalidRequest(f"Unknown target type: {target_name}")
   ```

Fahren Sie mit dem nächsten Thema, [Einen benutzerdefinierten Hook registrieren mit CloudFormation](registering-hooks.md), fort.

# Einen benutzerdefinierten Hook registrieren mit CloudFormation
<a name="registering-hooks"></a>

Sobald Sie einen benutzerdefinierten Hook erstellt haben, müssen Sie ihn registrieren, CloudFormation damit Sie ihn verwenden können. In diesem Abschnitt erfahren Sie, wie Sie Ihren Hook für die Verwendung in Ihrem verpacken und registrieren AWS-Konto.

## Einen Hook verpacken (Java)
<a name="registering-hooks-package"></a>

Wenn Sie Ihren Hook mit Java entwickelt haben, verwenden Sie Maven, um ihn zu verpacken.

Führen Sie im Verzeichnis Ihres Hook-Projekts den folgenden Befehl aus, um Ihren Hook zu erstellen, Komponententests auszuführen und Ihr Projekt als `JAR` Datei zu verpacken, mit der Sie Ihren Hook an die CloudFormation Registry senden können.

```
mvn clean package
```

## Registrieren Sie einen benutzerdefinierten Hook
<a name="registering-hooks-register"></a>

**Um einen Hook zu registrieren**

1. (Optional) Konfigurieren Sie Ihren AWS-Region Standardnamen auf`us-west-2`, indem Sie den [https://docs.aws.amazon.com/cli/latest/reference/configure/](https://docs.aws.amazon.com/cli/latest/reference/configure/)Vorgang abschicken.

   ```
   $ aws configure
   AWS Access Key ID [None]: <Your Access Key ID>
   AWS Secret Access Key [None]: <Your Secret Key>
   Default region name [None]: us-west-2
   Default output format [None]: json
   ```

1. (Optional) Mit dem folgenden Befehl wird Ihr Hook-Projekt erstellt und verpackt, ohne es zu registrieren.

   ```
   $ cfn submit --dry-run
   ```

1. Registrieren Sie Ihren Hook mithilfe der CloudFormation [https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-submit.html](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-submit.html)CLI-Operation.

   ```
   $ cfn submit --set-default
   ```

   Der Befehl gibt den folgenden Befehl zurück.

   ```
   {‘ProgressStatus’: ‘COMPLETE’}
   ```

   *Ergebnisse*: Sie haben Ihren Hook erfolgreich registriert.

## Überprüfe, ob Hooks in deinem Konto zugänglich sind
<a name="verifying-hooks"></a>

Vergewissere dich, dass dein Hook in dir AWS-Konto und in den Regionen, in denen du ihn eingereicht hast, verfügbar ist.

1. Um deinen Hook zu verifizieren, verwende den [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/list-types.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/list-types.html)Befehl, um deinen neu registrierten Hook aufzulisten und eine zusammenfassende Beschreibung zurückzugeben.

   ```
   $ aws cloudformation list-types
   ```

   Der Befehl gibt die folgende Ausgabe zurück und zeigt dir auch öffentlich verfügbare Hooks, die du in deinen Regionen AWS-Konto und Regionen aktivieren kannst.

   ```
   {
       "TypeSummaries": [
           {
               "Type": "HOOK",
               "TypeName": "MyCompany::Testing::MyTestHook",
               "DefaultVersionId": "00000001",
               "TypeArn": "arn:aws:cloudformation:us-west-2:ACCOUNT_ID/type/hook/MyCompany-Testing-MyTestHook",
               "LastUpdated": "2021-08-04T23:00:03.058000+00:00",
               "Description": "Verifies S3 bucket and SQS queues properties before creating or updating"
           }
       ]
   }
   ```

1. Rufen Sie das `TypeArn` aus der `list-type` Ausgabe für Ihren Hook ab und speichern Sie es.

   ```
   export HOOK_TYPE_ARN=arn:aws:cloudformation:us-west-2:ACCOUNT_ID/type/hook/MyCompany-Testing-MyTestHook
   ```

Informationen zum Veröffentlichen von Hooks für den öffentlichen Gebrauch finden Sie unter[Hooks für den öffentlichen Gebrauch veröffentlichen](hooks-publishing.md).

### Hooks konfigurieren
<a name="configure-hooks"></a>

Nachdem Sie Ihren Hook entwickelt und registriert haben, können Sie Ihren Hook in Ihrem konfigurieren, AWS-Konto indem Sie ihn in der Registry veröffentlichen.
+ Verwenden Sie den [https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetTypeConfiguration.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetTypeConfiguration.html)Vorgang, um einen Hook in Ihrem Konto zu konfigurieren. Diese Operation aktiviert die Eigenschaften des Hooks, die im Schemaabschnitt `properties` des Hooks definiert sind. Im folgenden Beispiel ist die `minBuckets` Eigenschaft `1` in der Konfiguration auf gesetzt.
**Anmerkung**  
Indem Sie Hooks in Ihrem Konto aktivieren, autorisieren Sie einen Hook, die von Ihnen AWS-Konto definierten Berechtigungen zu verwenden. CloudFormation entfernt nicht benötigte Berechtigungen, bevor deine Berechtigungen an den Hook weitergegeben werden. CloudFormation empfiehlt Kunden oder Hook-Benutzern, die Hook-Berechtigungen zu überprüfen und sich darüber im Klaren zu sein, welche Berechtigungen die Hooks haben dürfen, bevor Sie Hooks in Ihrem Konto aktivieren.

  Geben Sie die Konfigurationsdaten für Ihre registrierte Hook-Erweiterung im selben Konto an und AWS-Region.

  ```
  $ aws cloudformation set-type-configuration --region us-west-2 
    --configuration '{"CloudFormationConfiguration":{"HookConfiguration":{"HookInvocationStatus":"ENABLED","FailureMode":"FAIL","Properties":{"minBuckets": "1","minQueues": "1", "encryptionAlgorithm": "aws:kms"}}}}'
    --type-arn $HOOK_TYPE_ARN
  ```
**Wichtig**  
Damit Ihr Hook die Konfiguration Ihres Stacks proaktiv überprüfen kann, müssen Sie `ENABLED` in dem `HookInvocationStatus` `HookConfiguration` Abschnitt, nachdem der Hook registriert und in Ihrem Konto aktiviert wurde, den Wert auf setzen.

## Zugriff AWS APIs in Handlern
<a name="accessing-apis-in-handlers"></a>

Wenn Ihr Hooks eine AWS API in einem seiner Handler verwendet, erstellt die CFN-CLI automatisch eine Vorlage für eine IAM-Ausführungsrolle,. `hook-role.yaml` Die `hook-role.yaml` Vorlage basiert auf den Berechtigungen, die für jeden Handler im Abschnitt des Handlers des Hook-Schemas angegeben sind. Wenn das `--role-arn` Flag während des [https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-generate.html](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-generate.html)Vorgangs nicht verwendet wird, wird die Rolle in diesem Stack bereitgestellt und als Ausführungsrolle des Hooks verwendet.

Weitere Informationen finden Sie unter [Zugreifen AWS APIs von einem Ressourcentyp aus](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-develop.html#resource-type-develop-executionrole).

### Vorlage hook-role.yaml
<a name="resource-role.yaml"></a>

**Anmerkung**  
Wenn Sie sich dafür entscheiden, Ihre eigene Ausführungsrolle zu erstellen, empfehlen wir dringend, das Prinzip der geringsten Rechte anzuwenden, indem Sie nur die Liste zulassen und. `hooks.cloudformation.amazonaws.com` `resources.cloudformation.amazonaws.com`

Die folgende Vorlage verwendet die IAM-, Amazon S3- und Amazon SQS SQS-Berechtigungen.

```
AWSTemplateFormatVersion: 2010-09-09
Description: >
  This CloudFormation template creates a role assumed by CloudFormation during
  Hook operations on behalf of the customer.
Resources:
  ExecutionRole:
    Type: 'AWS::IAM::Role'
    Properties:
      MaxSessionDuration: 8400
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - resources.cloudformation.amazonaws.com
                - hooks.cloudformation.amazonaws.com
            Action: 'sts:AssumeRole'
            Condition:
              StringEquals:
                aws:SourceAccount: !Ref AWS::AccountId
              StringLike:
                aws:SourceArn: !Sub arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:type/hook/MyCompany-Testing-MyTestHook/*
      Path: /
      Policies:
        - PolicyName: HookTypePolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 's3:GetEncryptionConfiguration'
                  - 's3:ListBucket'
                  - 's3:ListAllMyBuckets'
                  - 'sqs:GetQueueAttributes'
                  - 'sqs:GetQueueUrl'
                  - 'sqs:ListQueues'
                Resource: '*'
Outputs:
  ExecutionRoleArn:
    Value: !GetAtt 
      - ExecutionRole
      - Arn
```

# Testen Sie einen benutzerdefinierten Hook in Ihrem AWS-Konto
<a name="testing-hooks"></a>

Nachdem Sie Ihre Handler-Funktionen codiert haben, die einem Aufrufpunkt entsprechen, ist es an der Zeit, Ihren benutzerdefinierten Hook auf einem CloudFormation Stack zu testen.

Der Hook-Fehlermodus ist auf eingestellt, `FAIL` wenn die CloudFormation Vorlage keinen S3-Bucket mit den folgenden Eigenschaften bereitgestellt hat:
+ Die Amazon S3 S3-Bucket-Verschlüsselung ist eingestellt.
+ Der Amazon S3 S3-Bucket-Schlüssel ist für den Bucket aktiviert.
+ Der für den Amazon S3 S3-Bucket festgelegte Verschlüsselungsalgorithmus ist der richtige erforderliche Algorithmus.
+ Die AWS Key Management Service Schlüssel-ID ist festgelegt.

Erstellen Sie im folgenden Beispiel eine Vorlage, die `my-failed-bucket-stack.yml` mit dem Stack-Namen aufgerufen wird und bei der `my-hook-stack` die Stack-Konfiguration fehlschlägt und beendet wird, bevor die Ressource bereitgestellt wird.

## Testen von Hooks durch Bereitstellung eines Stacks
<a name="testing-hooks-provision-stack"></a>

### Beispiel 1: Um einen Stack bereitzustellen
<a name="provision-a-stack-example-1"></a>

**Stellen Sie einen nicht konformen Stack bereit**

1. Verfassen Sie eine Vorlage, die einen S3-Bucket spezifiziert. Beispiel, `my-failed-bucket-stack.yml`.

   ```
   AWSTemplateFormatVersion: 2010-09-09
   Resources:
     S3Bucket:
       Type: AWS::S3::Bucket
       Properties: {}
   ```

1. Erstellen Sie einen Stack und geben Sie Ihre Vorlage im AWS Command Line Interface (AWS CLI) an. Geben Sie im folgenden Beispiel den Stacknamen als `my-hook-stack` und den Vorlagennamen als an`my-failed-bucket-stack.yml`.

   ```
   $ aws cloudformation create-stack \
     --stack-name my-hook-stack \
     --template-body file://my-failed-bucket-stack.yml
   ```

1. (Optional) Zeigen Sie Ihren Stack-Fortschritt an, indem Sie Ihren Stack-Namen angeben. Geben Sie im folgenden Beispiel den Stack-Namen an`my-hook-stack`.

   ```
   $ aws cloudformation describe-stack-events \
     --stack-name my-hook-stack
   ```

   Verwenden Sie den `describe-stack-events` Vorgang, um den Hook-Fehler beim Erstellen des Buckets zu überprüfen. Im Folgenden finden Sie ein Beispiel für die Ausgabe des Befehls.

   ```
   {
       "StackEvents": [
       ...
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-hook-stack/2c693970-f57e-11eb-a0fb-061a2a83f0b9",
               "EventId": "S3Bucket-CREATE_FAILED-2021-08-04T23:47:03.305Z",
               "StackName": "my-hook-stack",
               "LogicalResourceId": "S3Bucket",
               "PhysicalResourceId": "",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:47:03.305000+00:00",
               "ResourceStatus": "CREATE_FAILED",
               "ResourceStatusReason": "The following hook(s) failed: [MyCompany::Testing::MyTestHook]",
               "ResourceProperties": "{}",
               "ClientRequestToken": "Console-CreateStack-abe71ac2-ade4-a762-0499-8d34d91d6a92"
           },
       ...
       ]
   }
   ```

   *Ergebnisse*: Der Hook-Aufruf hat die Stack-Konfiguration nicht bestanden und die Ressource konnte nicht bereitgestellt werden.

**Verwenden Sie eine CloudFormation Vorlage, um die Hook-Validierung zu bestehen**

1. Um einen Stack zu erstellen und die Hook-Validierung zu bestehen, aktualisieren Sie die Vorlage so, dass Ihre Ressource einen verschlüsselten S3-Bucket verwendet. In diesem Beispiel wird die Vorlage verwendet`my-encrypted-bucket-stack.yml`.

   ```
   AWSTemplateFormatVersion: 2010-09-09
   Description: |
     This CloudFormation template provisions an encrypted S3 Bucket
   Resources:
     EncryptedS3Bucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: !Sub encryptedbucket-${AWS::Region}-${AWS::AccountId}
         BucketEncryption:
           ServerSideEncryptionConfiguration:
             - ServerSideEncryptionByDefault:
                 SSEAlgorithm: 'aws:kms'
                 KMSMasterKeyID: !Ref EncryptionKey
               BucketKeyEnabled: true
     EncryptionKey:
       Type: AWS::KMS::Key
       DeletionPolicy: Retain
       Properties:
         Description: KMS key used to encrypt the resource type artifacts
         EnableKeyRotation: true
         KeyPolicy:
           Version: 2012-10-17
           Statement:
             - Sid: Enable full access for owning account
               Effect: Allow
               Principal:
                 AWS: !Ref AWS::AccountId
               Action: 'kms:*'
               Resource: '*'
   Outputs:
     EncryptedBucketName:
       Value: !Ref EncryptedS3Bucket
   ```
**Anmerkung**  
Hooks werden nicht für übersprungene Ressourcen aufgerufen.

1. Erstelle einen Stapel und spezifiziere deine Vorlage. In diesem Beispiel lautet der Stack-Name`my-encrypted-bucket-stack`.

   ```
   $ aws cloudformation create-stack \
     --stack-name my-encrypted-bucket-stack \
     --template-body file://my-encrypted-bucket-stack.yml \
   ```

1. (Optional) Zeigen Sie Ihren Stack-Fortschritt an, indem Sie den Stack-Namen angeben.

   ```
   $ aws cloudformation describe-stack-events \
     --stack-name my-encrypted-bucket-stack
   ```

   Verwenden Sie den `describe-stack-events` Befehl, um die Antwort anzuzeigen. Im Folgenden finden Sie ein Beispiel für den `describe-stack-events`-Befehl.

   ```
   {
       "StackEvents": [
       ...
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-encrypted-bucket-stack/82a97150-f57a-11eb-8eb2-06a6bdcc7779",
               "EventId": "EncryptedS3Bucket-CREATE_COMPLETE-2021-08-04T23:23:20.973Z",
               "StackName": "my-encrypted-bucket-stack",
               "LogicalResourceId": "EncryptedS3Bucket",
               "PhysicalResourceId": "encryptedbucket-us-west-2-123456789012",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:23:20.973000+00:00",
               "ResourceStatus": "CREATE_COMPLETE",
               "ResourceProperties": "{\"BucketName\":\"encryptedbucket-us-west-2-123456789012\",\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":\"true\",\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"aws:kms\",\"KMSMasterKeyID\":\"ENCRYPTION_KEY_ARN\"}}]}}",
               "ClientRequestToken": "Console-CreateStack-39df35ac-ca00-b7f6-5661-4e917478d075"
           },
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-encrypted-bucket-stack/82a97150-f57a-11eb-8eb2-06a6bdcc7779",
               "EventId": "EncryptedS3Bucket-CREATE_IN_PROGRESS-2021-08-04T23:22:59.410Z",
               "StackName": "my-encrypted-bucket-stack",
               "LogicalResourceId": "EncryptedS3Bucket",
               "PhysicalResourceId": "encryptedbucket-us-west-2-123456789012",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:22:59.410000+00:00",
               "ResourceStatus": "CREATE_IN_PROGRESS",
               "ResourceStatusReason": "Resource creation Initiated",
               "ResourceProperties": "{\"BucketName\":\"encryptedbucket-us-west-2-123456789012\",\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":\"true\",\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"aws:kms\",\"KMSMasterKeyID\":\"ENCRYPTION_KEY_ARN\"}}]}}",
               "ClientRequestToken": "Console-CreateStack-39df35ac-ca00-b7f6-5661-4e917478d075"
           },
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-encrypted-bucket-stack/82a97150-f57a-11eb-8eb2-06a6bdcc7779",
               "EventId": "EncryptedS3Bucket-6516081f-c1f2-4bfe-a0f0-cefa28679994",
               "StackName": "my-encrypted-bucket-stack",
               "LogicalResourceId": "EncryptedS3Bucket",
               "PhysicalResourceId": "",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:22:58.349000+00:00",
               "ResourceStatus": "CREATE_IN_PROGRESS",
               "ResourceStatusReason": "Hook invocations complete.  Resource creation initiated",
               "ClientRequestToken": "Console-CreateStack-39df35ac-ca00-b7f6-5661-4e917478d075"
           },
       ...
       ]
   }
   ```

   *Ergebnisse*: Der Stack wurde CloudFormation erfolgreich erstellt. Die Logik des Hooks überprüfte vor der Bereitstellung der `AWS::S3::Bucket` Ressource, ob die Ressource serverseitige Verschlüsselung enthielt.

### Beispiel 2: Um einen Stack bereitzustellen
<a name="provision-a-stack-example-2"></a>

**Stellen Sie einen nicht konformen Stack bereit**

1. Verfassen Sie eine Vorlage, die einen S3-Bucket spezifiziert. Zum Beispiel `aes256-bucket.yml`.

   ```
   AWSTemplateFormatVersion: 2010-09-09
   Description: |
     This CloudFormation template provisions an encrypted S3 Bucket
   Resources:
     EncryptedS3Bucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: !Sub encryptedbucket-${AWS::Region}-${AWS::AccountId}
         BucketEncryption:
           ServerSideEncryptionConfiguration:
             - ServerSideEncryptionByDefault:
                 SSEAlgorithm: AES256
               BucketKeyEnabled: true
   Outputs:
     EncryptedBucketName:
       Value: !Ref EncryptedS3Bucket
   ```

1. Erstellen Sie einen Stack und geben Sie Ihre Vorlage im an AWS CLI. Geben Sie im folgenden Beispiel den Stacknamen als `my-hook-stack` und den Vorlagennamen als an`aes256-bucket.yml`.

   ```
   $ aws cloudformation create-stack \
     --stack-name my-hook-stack \
     --template-body file://aes256-bucket.yml
   ```

1. (Optional) Zeigen Sie Ihren Stack-Fortschritt an, indem Sie Ihren Stack-Namen angeben. Geben Sie im folgenden Beispiel den Stack-Namen an`my-hook-stack`.

   ```
   $ aws cloudformation describe-stack-events \
     --stack-name my-hook-stack
   ```

   Verwenden Sie den `describe-stack-events` Vorgang, um den Hook-Fehler beim Erstellen des Buckets zu überprüfen. Im Folgenden finden Sie ein Beispiel für die Ausgabe des Befehls.

   ```
   {
       "StackEvents": [
       ...
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-hook-stack/2c693970-f57e-11eb-a0fb-061a2a83f0b9",
               "EventId": "S3Bucket-CREATE_FAILED-2021-08-04T23:47:03.305Z",
               "StackName": "my-hook-stack",
               "LogicalResourceId": "S3Bucket",
               "PhysicalResourceId": "",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:47:03.305000+00:00",
               "ResourceStatus": "CREATE_FAILED",
               "ResourceStatusReason": "The following hook(s) failed: [MyCompany::Testing::MyTestHook]",
               "ResourceProperties": "{}",
               "ClientRequestToken": "Console-CreateStack-abe71ac2-ade4-a762-0499-8d34d91d6a92"
           },
       ...
       ]
   }
   ```

   *Ergebnisse*: Der Hook-Aufruf hat die Stack-Konfiguration nicht bestanden und die Ressource konnte nicht bereitgestellt werden. Der Stack ist aufgrund der falsch konfigurierten S3-Bucket-Verschlüsselung fehlgeschlagen. Die Konfiguration vom Typ Hook erfordert, `aws:kms` solange dieser Bucket verwendet wird`AES256`.

**Verwenden Sie eine CloudFormation Vorlage, um die Hook-Validierung zu bestehen**

1. Um einen Stack zu erstellen und die Hook-Validierung zu bestehen, aktualisieren Sie die Vorlage so, dass Ihre Ressource einen verschlüsselten S3-Bucket verwendet. In diesem Beispiel wird die Vorlage verwendet`kms-bucket-and-queue.yml`.

   ```
   AWSTemplateFormatVersion: 2010-09-09
   Description: |
     This CloudFormation template provisions an encrypted S3 Bucket
   Resources:
     EncryptedS3Bucket:
       Type: AWS::S3::Bucket
       Properties:
         BucketName: !Sub encryptedbucket-${AWS::Region}-${AWS::AccountId}
         BucketEncryption:
           ServerSideEncryptionConfiguration:
             - ServerSideEncryptionByDefault:
                 SSEAlgorithm: 'aws:kms'
                 KMSMasterKeyID: !Ref EncryptionKey
               BucketKeyEnabled: true
     EncryptedQueue:
       Type: AWS::SQS::Queue
       Properties:
         QueueName: !Sub encryptedqueue-${AWS::Region}-${AWS::AccountId}
         KmsMasterKeyId: !Ref EncryptionKey
     EncryptionKey:
       Type: AWS::KMS::Key
       DeletionPolicy: Retain
       Properties:
         Description: KMS key used to encrypt the resource type artifacts
         EnableKeyRotation: true
         KeyPolicy:
           Version: 2012-10-17
           Statement:
             - Sid: Enable full access for owning account
               Effect: Allow
               Principal:
                 AWS: !Ref AWS::AccountId
               Action: 'kms:*'
               Resource: '*'
   Outputs:
     EncryptedBucketName:
       Value: !Ref EncryptedS3Bucket
     EncryptedQueueName:
       Value: !Ref EncryptedQueue
   ```
**Anmerkung**  
Hooks werden nicht für übersprungene Ressourcen aufgerufen.

1. Erstelle einen Stapel und spezifiziere deine Vorlage. In diesem Beispiel lautet der Stack-Name`my-encrypted-bucket-stack`.

   ```
   $ aws cloudformation create-stack \
     --stack-name my-encrypted-bucket-stack \
     --template-body file://kms-bucket-and-queue.yml
   ```

1. (Optional) Zeigen Sie Ihren Stack-Fortschritt an, indem Sie den Stack-Namen angeben.

   ```
   $ aws cloudformation describe-stack-events \
     --stack-name my-encrypted-bucket-stack
   ```

   Verwenden Sie den `describe-stack-events` Befehl, um die Antwort anzuzeigen. Im Folgenden finden Sie ein Beispiel für den `describe-stack-events`-Befehl.

   ```
   {
       "StackEvents": [
       ...
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-encrypted-bucket-stack/82a97150-f57a-11eb-8eb2-06a6bdcc7779",
               "EventId": "EncryptedS3Bucket-CREATE_COMPLETE-2021-08-04T23:23:20.973Z",
               "StackName": "my-encrypted-bucket-stack",
               "LogicalResourceId": "EncryptedS3Bucket",
               "PhysicalResourceId": "encryptedbucket-us-west-2-123456789012",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:23:20.973000+00:00",
               "ResourceStatus": "CREATE_COMPLETE",
               "ResourceProperties": "{\"BucketName\":\"encryptedbucket-us-west-2-123456789012\",\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":\"true\",\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"aws:kms\",\"KMSMasterKeyID\":\"ENCRYPTION_KEY_ARN\"}}]}}",
               "ClientRequestToken": "Console-CreateStack-39df35ac-ca00-b7f6-5661-4e917478d075"
           },
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-encrypted-bucket-stack/82a97150-f57a-11eb-8eb2-06a6bdcc7779",
               "EventId": "EncryptedS3Bucket-CREATE_IN_PROGRESS-2021-08-04T23:22:59.410Z",
               "StackName": "my-encrypted-bucket-stack",
               "LogicalResourceId": "EncryptedS3Bucket",
               "PhysicalResourceId": "encryptedbucket-us-west-2-123456789012",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:22:59.410000+00:00",
               "ResourceStatus": "CREATE_IN_PROGRESS",
               "ResourceStatusReason": "Resource creation Initiated",
               "ResourceProperties": "{\"BucketName\":\"encryptedbucket-us-west-2-123456789012\",\"BucketEncryption\":{\"ServerSideEncryptionConfiguration\":[{\"BucketKeyEnabled\":\"true\",\"ServerSideEncryptionByDefault\":{\"SSEAlgorithm\":\"aws:kms\",\"KMSMasterKeyID\":\"ENCRYPTION_KEY_ARN\"}}]}}",
               "ClientRequestToken": "Console-CreateStack-39df35ac-ca00-b7f6-5661-4e917478d075"
           },
           {
               "StackId": "arn:aws:cloudformation:us-west-2:123456789012:stack/my-encrypted-bucket-stack/82a97150-f57a-11eb-8eb2-06a6bdcc7779",
               "EventId": "EncryptedS3Bucket-6516081f-c1f2-4bfe-a0f0-cefa28679994",
               "StackName": "my-encrypted-bucket-stack",
               "LogicalResourceId": "EncryptedS3Bucket",
               "PhysicalResourceId": "",
               "ResourceType": "AWS::S3::Bucket",
               "Timestamp": "2021-08-04T23:22:58.349000+00:00",
               "ResourceStatus": "CREATE_IN_PROGRESS",
               "ResourceStatusReason": "Hook invocations complete.  Resource creation initiated",
               "ClientRequestToken": "Console-CreateStack-39df35ac-ca00-b7f6-5661-4e917478d075"
           },
       ...
       ]
   }
   ```

   *Ergebnisse*: Der Stack wurde CloudFormation erfolgreich erstellt. Die Logik des Hooks überprüfte vor der Bereitstellung der `AWS::S3::Bucket` Ressource, ob die Ressource serverseitige Verschlüsselung enthielt.

# Einen benutzerdefinierten Hook aktualisieren
<a name="updating-registered-hook"></a>

Durch die Aktualisierung eines benutzerdefinierten Hooks können Revisionen im Hook in der CloudFormation Registrierung verfügbar gemacht werden.

Um einen benutzerdefinierten Hook zu aktualisieren, reichen Sie Ihre Revisionen über die CloudFormation CLI an die CloudFormation Registry ein [https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-submit.html](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-submit.html).

```
$ cfn submit
```

Um die Standardversion Ihres Hooks in Ihrem Konto anzugeben, verwenden Sie den [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-type-default-version.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-type-default-version.html)Befehl und geben Sie den Typ, den Typnamen und die Versions-ID an.

```
$ aws cloudformation set-type-default-version \
    --type HOOK \
    --type-name MyCompany::Testing::MyTestHook \
    --version-id 00000003
```

Um Informationen über die Versionen eines Hooks abzurufen, verwenden Sie [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/list-type-versions.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/list-type-versions.html).

```
$ aws cloudformation list-type-versions \
  --type HOOK \
  --type-name "MyCompany::Testing::MyTestHook"
```

# Einen benutzerdefinierten Hook von der Registrierung abmelden CloudFormation
<a name="deregistering-hooks"></a>

Wenn Sie einen benutzerdefinierten Hook deregistrieren, wird die Erweiterung oder Erweiterungsversion als `DEPRECATED` in der CloudFormation Registrierung markiert, wodurch sie nicht mehr aktiv verwendet werden kann. Sobald der benutzerdefinierte Hook veraltet ist, kann er nicht mehr in einem Vorgang verwendet werden. CloudFormation 

**Anmerkung**  
Bevor Sie den Hook deregistrieren, müssen Sie alle vorherigen aktiven Versionen dieser Erweiterung einzeln abmelden. Weitere Informationen finden Sie unter [https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DeregisterType.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DeregisterType.html).

Um einen Hook zu deregistrieren, verwenden Sie die [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deregister-type.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deregister-type.html)Operation und geben Sie Ihren Hook-ARN an.

```
$ aws cloudformation deregister-type \
    --arn HOOK_TYPE_ARN
```

Dieser Befehl erzeugt keine Ausgabe.

# Hooks für den öffentlichen Gebrauch veröffentlichen
<a name="hooks-publishing"></a>

Um einen öffentlichen Hook eines Drittanbieters zu entwickeln, entwickeln Sie Ihren Hook als private Erweiterung. Dann in jedem, AWS-Region in dem Sie die Erweiterung öffentlich verfügbar machen möchten: 

1. Registrieren Sie Ihren Hook als private Erweiterung in der CloudFormation Registrierung.

1. Testen Sie Ihren Hook, um sicherzustellen, dass er alle erforderlichen Voraussetzungen für die Veröffentlichung in der CloudFormation Registrierung erfüllt.

1. Veröffentlichen Sie Ihren Hook in der CloudFormation Registry.
**Anmerkung**  
Bevor Sie eine Erweiterung in einer bestimmten Region veröffentlichen, müssen Sie sich zunächst als Herausgeber von Erweiterungen in dieser Region registrieren. Informationen dazu, wie Sie dies in mehreren Regionen gleichzeitig tun können, finden Sie unter [Veröffentlichen von Erweiterungen StackSets in mehreren Regionen mithilfe](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/publish-extension-stacksets.html) des * CloudFormation CLI-Benutzerhandbuchs*.

Nachdem Sie Ihren Hook entwickelt und registriert haben, können Sie ihn allgemeinen CloudFormation Benutzern öffentlich zugänglich machen, indem Sie ihn als öffentliche Erweiterung eines Drittanbieters in der CloudFormation Registry *veröffentlichen*.

Öffentliche Hooks von Drittanbietern ermöglichen es Ihnen, CloudFormation Benutzern die Möglichkeit zu geben, die Konfiguration von AWS Ressourcen vor der Bereitstellung proaktiv zu überprüfen. Wie bei privaten Hooks werden öffentliche Hooks genauso behandelt wie alle von Within veröffentlichten AWS Hooks. CloudFormation

In der Registry veröffentlichte Hooks sind für alle CloudFormation Benutzer in der Region sichtbar, AWS-Regionen in der sie veröffentlicht wurden. Benutzer können Ihre Erweiterung dann in ihrem Konto *aktivieren*, sodass sie in ihren Vorlagen verwendet werden kann. Weitere Informationen finden Sie im *CloudFormation Benutzerhandbuch* unter [Verwenden von öffentlichen Erweiterungen von Drittanbietern aus der CloudFormation Registrierung](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/registry-public.html).

# Testen eines benutzerdefinierten Hooks für den öffentlichen Gebrauch
<a name="hooks-testing-registered-hooks"></a>

Um Ihren registrierten benutzerdefinierten Hook zu veröffentlichen, muss er alle für ihn definierten Testanforderungen erfüllen. Im Folgenden finden Sie eine Liste der Anforderungen, die erforderlich sind, bevor Sie Ihren benutzerdefinierten Hook als Erweiterung eines Drittanbieters veröffentlichen.

Jeder Handler und jedes Ziel werden zweimal getestet. Einmal für `SUCCESS` und einmal für`FAILED`.
+ Für den `SUCCESS` Antwortfall:
  + Der Status muss sein`SUCCESS`.
  + Darf keinen Fehlercode zurückgeben.
  + Falls angegeben, sollte die Rückrufverzögerung auf `0` Sekunden gesetzt werden.
+ Für den `FAILED` Antwortfall:
  + Der Status muss sein`FAILED`.
  + Muss einen Fehlercode zurückgeben.
  + Es muss eine Nachricht als Antwort vorliegen.
  + Falls angegeben, sollte die Rückrufverzögerung auf `0` Sekunden gesetzt werden.
+ Für den `IN_PROGRESS` Antwortfall:
  + Darf keinen Fehlercode zurückgeben.
  + `Result`Das Feld darf nicht als Antwort gesetzt werden.

# Angabe von Eingabedaten für die Verwendung in Vertragstests
<a name="hooks-input-data-contract-test"></a>

Standardmäßig CloudFormation führt der Vertragstests mithilfe von Eingabeeigenschaften durch, die aus den Mustern generiert wurden, die Sie in Ihrem Hook-Schema definieren. Die meisten Hooks sind jedoch so komplex, dass die Eingabeeigenschaften für die vorherige Erstellung oder Aktualisierung von Provisioning-Stacks ein Verständnis der bereitgestellten Ressource erfordern. Um dieses Problem zu lösen, können Sie die Eingabe angeben, die das Unternehmen bei der Durchführung seiner CloudFormation Vertragstests verwendet.

CloudFormation bietet Ihnen zwei Möglichkeiten, die Eingabedaten anzugeben, die bei der Durchführung von Vertragstests verwendet werden sollen:
+ Überschreibt die Datei

  Die Verwendung einer `overrides` Datei bietet eine einfache Möglichkeit, Eingabedaten für bestimmte Eigenschaften anzugeben`preCreate`, die CloudFormation während `preUpdate` und bei `preDelete` Betriebstests verwendet werden können.
+ Eingabedateien

  Sie können auch mehrere `input` Dateien verwenden, um Eingabedaten für den Vertragstest anzugeben, wenn:
  + Sie möchten oder müssen unterschiedliche Eingabedaten für Erstellungs-, Aktualisierungs- und Löschvorgänge oder ungültige Daten für Tests angeben.
  + Sie möchten mehrere verschiedene Eingabedatensätze angeben.

## Eingabedaten mithilfe einer Override-Datei angeben
<a name="hook-override-inputs"></a>

Das Folgende ist ein Beispiel für die Eingabedaten von Amazon S3 Hook unter Verwendung der `overrides` Datei.

```
{
    "CREATE_PRE_PROVISION": {
        "AWS::S3::Bucket": {
            "resourceProperties": {
                "/BucketName": "encryptedbucket-us-west-2-contractor",
                "/BucketEncryption/ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            }
        },
        "AWS::SQS::Queue": {
            "resourceProperties": {
                "/QueueName": "MyQueueContract",
                "/KmsMasterKeyId": "hellocontract"
            }
        }
    },
    "UPDATE_PRE_PROVISION": {
        "AWS::S3::Bucket": {
            "resourceProperties": {
                "/BucketName": "encryptedbucket-us-west-2-contractor",
                "/BucketEncryption/ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            },
            "previousResourceProperties": {
                "/BucketName": "encryptedbucket-us-west-2-contractor",
                "/BucketEncryption/ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            }
        }
    },
    "INVALID_UPDATE_PRE_PROVISION": {
        "AWS::S3::Bucket": {
            "resourceProperties": {
                "/BucketName": "encryptedbucket-us-west-2-contractor",
                "/BucketEncryption/ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "AES256"
                        }
                    }
                ]
            },
            "previousResourceProperties": {
                "/BucketName": "encryptedbucket-us-west-2-contractor",
                "/BucketEncryption/ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            }
        }
    },
    "INVALID": {
        "AWS::SQS::Queue": {
            "resourceProperties": {
                "/QueueName": "MyQueueContract",
                "/KmsMasterKeyId": "KMS-KEY-ARN"
            }
        }
    }
}
```

## Angeben von Eingabedaten mithilfe von Eingabedateien
<a name="hook-test-inputs"></a>

Verwenden Sie `input` Dateien, um verschiedene Arten von Eingabedaten anzugeben, die verwendet werden CloudFormation sollen: `preCreate` Eingabe, `preUpdate` Eingabe und ungültige Eingabe. Jede Art von Daten wird in einer separaten Datei angegeben. Sie können auch mehrere Sätze von Eingabedaten für Vertragstests angeben.

Um `input` Dateien anzugeben, die CloudFormation bei Vertragstests verwendet werden sollen, fügen Sie dem Stammverzeichnis Ihres Hooks-Projekts einen `inputs` Ordner hinzu. Fügen Sie dann Ihre Eingabedateien hinzu.

Geben Sie an, welche Art von Eingabedaten eine Datei enthält, indem Sie die folgenden Namenskonventionen verwenden, wobei eine Ganzzahl **n**steht:
+ `inputs_n_pre_create.json`: Verwenden Sie Dateien mit `preCreate` Handlern, um Eingaben für die Erstellung der Ressource anzugeben.
+ `inputs_n_pre_update.json`: Verwenden Sie Dateien mit `preUpdate` Handlern, um Eingaben für die Aktualisierung der Ressource anzugeben.
+ `inputs_n_pre_delete.json`: Verwenden Sie Dateien mit `preDelete` Handlern, um Eingaben für das Löschen der Ressource anzugeben.
+ `inputs_n_invalid.json`: Zur Angabe ungültiger Eingaben zum Testen.

Um mehrere Sätze von Eingabedaten für Vertragstests anzugeben, erhöhen Sie die Ganzzahl in den Dateinamen, um Ihre Eingabedatensätze zu ordnen. Ihr erster Satz von Eingabedateien sollte beispielsweise den Namen `inputs_1_pre_create.json``inputs_1_pre_update.json`, und `inputs_1_pre_invalid.json` haben. Ihr nächster Satz würde den Namen `inputs_2_pre_create.json` `inputs_2_pre_update.json``inputs_2_pre_invalid.json`, und usw. tragen.

Jede Eingabedatei ist eine JSON-Datei, die nur die Ressourceneigenschaften enthält, die beim Testen verwendet werden sollen.

Das Folgende ist ein Beispielverzeichnis `inputs` für die Amazon S3 Angabe von Eingabedaten mithilfe von Eingabedateien.

`inputs_1_pre_create.json`  <a name="inputs_1_pre_create.json"></a>
Das Folgende ist ein Beispiel für den `inputs_1_pre_create.json` Vertragstest.  

```
{
    "AWS::S3::Bucket": {
        "resourceProperties": {
            "AccessControl": "BucketOwnerFullControl",
            "AnalyticsConfigurations": [],
            "BucketEncryption": {
                "ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            },
            "BucketName": "encryptedbucket-us-west-2"
        }
    },
    "AWS::SQS::Queue": {
        "resourceProperties": {
            "QueueName": "MyQueue",
            "KmsMasterKeyId": "KMS-KEY-ARN"
        }
    }
}
```

`inputs_1_pre_update.json`  <a name="inputs_1_pre_update.json"></a>
Das Folgende ist ein Beispiel für den `inputs_1_pre_update.json` Vertragstest.  

```
{
    "AWS::S3::Bucket": {
        "resourceProperties": {
            "BucketEncryption": {
                "ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            },
            "BucketName": "encryptedbucket-us-west-2"
        },
        "previousResourceProperties": {
            "BucketEncryption": {
                "ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            },
            "BucketName": "encryptedbucket-us-west-2"
        }
    }
}
```

`inputs_1_invalid.json`  <a name="inputs_1_invalid.json"></a>
Das Folgende ist ein Beispiel für den `inputs_1_invalid.json` Vertragstest.  

```
{
    "AWS::S3::Bucket": {
        "resourceProperties": {
            "AccessControl": "BucketOwnerFullControl",
            "AnalyticsConfigurations": [],
            "BucketEncryption": {
                "ServerSideEncryptionConfiguration": [
                    {
                        "ServerSideEncryptionByDefault": {
                            "SSEAlgorithm": "AES256"
                        }
                    }
                ]
            },
            "BucketName": "encryptedbucket-us-west-2"
        }
    },
    "AWS::SQS::Queue": {
        "resourceProperties": {
            "NotValid": "The property of this resource is not valid."
        }
    }
}
```

`inputs_1_invalid_pre_update.json`  <a name="inputs_1_invalid_pre_update.json"></a>
Das Folgende ist ein Beispiel für den `inputs_1_invalid_pre_update.json` Vertragstest.  

```
{
    "AWS::S3::Bucket": {
        "resourceProperties": {
            "BucketEncryption": {
                "ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "AES256"
                        }
                    }
                ]
            },
            "BucketName": "encryptedbucket-us-west-2"
        },
        "previousResourceProperties": {
            "BucketEncryption": {
                "ServerSideEncryptionConfiguration": [
                    {
                        "BucketKeyEnabled": true,
                        "ServerSideEncryptionByDefault": {
                            "KMSMasterKeyID": "KMS-KEY-ARN",
                            "SSEAlgorithm": "aws:kms"
                        }
                    }
                ]
            },
            "BucketName": "encryptedbucket-us-west-2"
        }
    }
}
```

Weitere Informationen finden Sie unter [Veröffentlichen von Erweiterungen, um sie öffentlich zugänglich zu machen](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/publish-extension.html) im *Benutzerhandbuch für CloudFormation CLI*.

# Schema-Syntaxreferenz für CloudFormation Hooks
<a name="hooks-schema"></a>

In diesem Abschnitt wird die Syntax des Schemas beschrieben, das Sie zur Entwicklung von CloudFormation Hooks verwenden.

Ein Hook beinhaltet eine Hook-Spezifikation, die durch ein JSON-Schema und Hook-Handler dargestellt wird. Der erste Schritt bei der Erstellung eines benutzerdefinierten Hooks besteht darin, ein Schema zu modellieren, das den Hook, seine Eigenschaften und seine Attribute definiert. Wenn Sie ein benutzerdefiniertes Hook-Projekt mit dem CloudFormation [https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-init.html](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/resource-type-cli-init.html)CLI-Befehl initialisieren, wird eine Hook-Schemadatei für Sie erstellt. Verwenden Sie diese Schemadatei als Ausgangspunkt für die Definition der Form und Semantik Ihres benutzerdefinierten Hooks.

## Schemasyntax
<a name="schema-syntax"></a>

Das folgende Schema ist die Struktur für einen Hook.

```
{
"typeName": "string",
    "description": "string",
    "sourceUrl": "string",
    "documentationUrl": "string",
    "definitions": {
        "definitionName": {
          . . .
        }
    },
    "typeConfiguration": {
        "properties": {
             "propertyName": {
                "description": "string",
                "type": "string",
                 . . .
            },
        },
    "required": [
        "propertyName"
         . . .
            ],
    "additionalProperties": false
    },
    "handlers": {
        "preCreate": {
            "targetNames": [
            ],
            "permissions": [
            ]
        },
        "preUpdate": {
            "targetNames": [
            ],
            "permissions": [
            ]
        },
        "preDelete": {
            "targetNames": [
            ],
            "permissions": [
            ]
        }
   },
   "additionalProperties": false
}
```

`typeName`  <a name="hooks-properties-typeName"></a>
Der eindeutige Name für Ihren Hook. Gibt einen dreiteiligen Namespace für Ihren Hook an, mit dem empfohlenen Muster von. `Organization::Service::Hook`  
Die folgenden Organisations-Namespaces sind reserviert und können nicht in Ihren Hook-Typnamen verwendet werden:  
+ `Alexa`
+ `AMZN`
+ `Amazon`
+ `ASK`
+ `AWS`
+ `Custom`
+ `Dev`
*Erforderlich*: Ja  
 *Muster*: `^[a-zA-Z0-9]{2,64}::[a-zA-Z0-9]{2,64}::[a-zA-Z0-9]{2,64}$`  
*Minimum*: `10`  
*Maximum*: `196`

`description`  <a name="hooks-properties-description"></a>
Eine kurze Beschreibung des Hooks, der in der Konsole angezeigt wird. CloudFormation   
*Erforderlich*: Ja

`sourceUrl`  <a name="hooks-properties-sourceUrl"></a>
Die URL des Quellcodes für den Hook, falls öffentlich.  
*Required*: No  
*Maximum*: `4096`

`documentationUrl`  <a name="hooks-properties-documentationurl"></a>
Die URL einer Seite mit detaillierter Dokumentation für den Hook.  
*Erforderlich*: Ja  
*Muster*: `^https\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])(\:[0-9]*)*([\?/#].*)?$`  
*Maximum*: `4096`  
Obwohl das Hook-Schema vollständige und genaue Eigenschaftsbeschreibungen enthalten sollte, können Sie die `documentationURL` Eigenschaft verwenden, um Benutzern weitere Details, einschließlich Beispiele, Anwendungsfälle und andere detaillierte Informationen, zur Verfügung zu stellen.

`definitions`  <a name="hooks-properties-definitions"></a>
Verwenden Sie den `definitions` Block, um gemeinsam genutzte Hook-Eigenschaftsschemas bereitzustellen.  
Es wird als bewährte Methode angesehen, diesen `definitions` Abschnitt zur Definition von Schemaelementen zu verwenden, die an mehreren Stellen in Ihrem Hook-Schema verwendet werden können. Sie können dann einen JSON-Zeiger verwenden, um an den entsprechenden Stellen in Ihrem Hook-Typschema auf dieses Element zu verweisen.  
*Required*: No

`typeConfiguration`  <a name="hooks-properties-typeconfiguration"></a>
Die Definition der Konfigurationsdaten eines Hooks.  
*Erforderlich*: Ja

`properties`  <a name="hooks-properties-properties"></a>
Die Eigenschaften des Hooks. Alle Eigenschaften eines Hooks müssen im Schema ausgedrückt werden. Ordnen Sie die Eigenschaften des Hook-Schemas den Konfigurationseigenschaften des Hook-Typs zu.  
Verschachtelte Eigenschaften sind nicht zulässig. Definieren Sie stattdessen alle verschachtelten Eigenschaften im `definitions` Element und verwenden Sie einen `$ref` Zeiger, um sie in der gewünschten Eigenschaft zu referenzieren.
Die folgenden Eigenschaften werden derzeit unterstützt:  
+ `default`— Der Standardwert der Eigenschaft.
+ `description`— Eine Beschreibung der Immobilie.
+ `pattern`— Ein Regex-Muster, das zur Validierung der Eingabe verwendet wird.
+ `type`— Der akzeptierte Typ der Eigenschaft.

`additionalProperties`  <a name="hooks-properties-additionalproperties"></a>
muss `additionalProperties` auf `false` festgelegt sein. Alle Eigenschaften eines Hooks müssen im Schema ausgedrückt werden: Beliebige Eingaben sind nicht erlaubt.  
*Erforderlich*: Ja  
*Gültige Werte*: `false`

`handlers`  <a name="hooks-properties-handlers"></a>
Handler spezifizieren die Operationen, die den im Schema definierten Hook initiieren können, wie z. B. Hook-Aufrufpunkte. Beispielsweise wird ein `preUpdate` Handler vor den Aktualisierungsvorgängen für alle angegebenen Ziele im Handler aufgerufen.  
*Zulässige Werte*: `preCreate` \$1 `preUpdate` \$1 `preDelete`  
Für den Handler muss mindestens ein Wert angegeben werden.
Stack-Operationen, die zum Status von führen, rufen `UpdateCleanup` keinen Hook auf. In den folgenden beiden Szenarien wird beispielsweise der `preDelete` Handler des Hooks nicht aufgerufen:  
+ Der Stack wird aktualisiert, nachdem eine Ressource aus der Vorlage entfernt wurde.
+ eine Ressource mit dem Aktualisierungstyp „[Ersatz](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-update-behaviors.html#update-replacement)“ wird gelöscht.

`targetNames`  <a name="hooks-properties-targetNames"></a>
Ein String-Array mit Typnamen, auf die Hook abzielt. Wenn ein `preCreate` Handler beispielsweise ein `AWS::S3::Bucket` Ziel hat, wird der Hook während der Vorbereitstellungsphase für Amazon S3 S3-Buckets ausgeführt.  
+ `TargetName`

  Geben Sie mindestens einen Zielnamen für jeden implementierten Handler an.

  *Muster*: `^[a-zA-Z0-9]{2,64}::[a-zA-Z0-9]{2,64}::[a-zA-Z0-9]{2,64}$`

  *Minimum*: `1`

  *Erforderlich*: Ja
**Warnung**  
Dynamische Referenzen von SSM SecureString und Secrets Manager werden nicht aufgelöst, bevor sie an Hooks übergeben werden.

`permissions`  <a name="hooks-properties-permissions"></a>
Ein Zeichenkettenarray, das die AWS Berechtigungen angibt, die zum Aufrufen des Handlers erforderlich sind.  
*Erforderlich*: Ja

`additionalProperties`  <a name="hooks-additional-properties"></a>
muss `additionalProperties` auf `false` festgelegt sein. Alle Eigenschaften eines Hooks müssen im Schema ausgedrückt werden: Beliebige Eingaben sind nicht erlaubt.  
*Erforderlich*: Ja  
*Gültige Werte*: `false`

## Beispiele für Hooks-Schemas
<a name="example-hooks"></a>

 **Beispiel 1** 

Die exemplarischen Vorgehensweisen für Java und Python verwenden das folgende Codebeispiel. Im Folgenden finden Sie eine Beispielstruktur für einen Hook namens. `mycompany-testing-mytesthook.json`

```
{
    "typeName":"MyCompany::Testing::MyTestHook",
    "description":"Verifies S3 bucket and SQS queues properties before create and update",
    "sourceUrl":"https://mycorp.com/my-repo.git",
    "documentationUrl":"https://mycorp.com/documentation",
    "typeConfiguration":{
        "properties":{
            "minBuckets":{
                "description":"Minimum number of compliant buckets",
                "type":"string"
            },
            "minQueues":{
                "description":"Minimum number of compliant queues",
                "type":"string"
            },
            "encryptionAlgorithm":{
                "description":"Encryption algorithm for SSE",
                "default":"AES256",
                "type":"string",
                "pattern": "[a-zA-Z]*[1-9]"
            }
        },
        "required":[
            
        ],
        "additionalProperties":false
    },
    "handlers":{
        "preCreate":{
            "targetNames":[
                "AWS::S3::Bucket",
                "AWS::SQS::Queue"
            ],
            "permissions":[
                
            ]
        },
        "preUpdate":{
            "targetNames":[
                "AWS::S3::Bucket",
                "AWS::SQS::Queue"
            ],
            "permissions":[
                
            ]
        },
        "preDelete":{
            "targetNames":[
                "AWS::S3::Bucket",
                "AWS::SQS::Queue"
            ],
            "permissions":[
                "s3:ListBucket",
                "s3:ListAllMyBuckets",
                "s3:GetEncryptionConfiguration",
                "sqs:ListQueues",
                "sqs:GetQueueAttributes",
                "sqs:GetQueueUrl"
            ]
        }
    },
    "additionalProperties":false
}
```

 **Beispiel 2** 

Das folgende Beispiel ist ein Schema, das `STACK` und `CHANGE_SET` for verwendet, `targetNames` um auf eine Stack-Vorlage und einen Change-Set-Vorgang abzuzielen.

```
{
    "typeName":"MyCompany::Testing::MyTestHook",
    "description":"Verifies Stack and Change Set properties before create and update",
    "sourceUrl":"https://mycorp.com/my-repo.git",
    "documentationUrl":"https://mycorp.com/documentation",
    "typeConfiguration":{
        "properties":{
            "minBuckets":{
                "description":"Minimum number of compliant buckets",
                "type":"string"
            },
            "minQueues":{
                "description":"Minimum number of compliant queues",
                "type":"string"
            },
            "encryptionAlgorithm":{
                "description":"Encryption algorithm for SSE",
                "default":"AES256",
                "type":"string",
                "pattern": "[a-zA-Z]*[1-9]"
            }
        },
        "required":[
        ],
        "additionalProperties":false
    },
    "handlers":{
        "preCreate":{
            "targetNames":[
                "STACK",
                "CHANGE_SET"
            ],
            "permissions":[  
            ]
        },
        "preUpdate":{
            "targetNames":[
                "STACK"
            ],
            "permissions":[
            ]
        },
        "preDelete":{
            "targetNames":[
                "STACK"
            ],
            "permissions":[
                
            ]
        }
    },
    "additionalProperties":false
}
```