

• AWS Systems Manager CloudWatch 控制面板在 2026 年 4 月 30 日之后将不再可用。客户可以像现在一样继续使用 Amazon CloudWatch 控制台来查看、创建和管理其 Amazon CloudWatch 控制面板。有关更多信息，请参阅 [Amazon CloudWatch 控制面板文档](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Dashboards.html)。

# 创建您自己的运行手册
<a name="automation-documents"></a>

自动化运行手册定义了自动化运行时 Systems Manager 在托管实例和其他 AWS 资源上执行的*操作*。Automation 是 AWS Systems Manager 中的一项工具。运行手册包含一个或多个按顺序运行的步骤。每个步骤是根据单个操作生成的。可以将一个步骤的输出作为后面步骤的输入。

运行这些操作及其步骤的过程称为*自动化*。

让您可以在 AWS 环境中自动完成各种不同操作的运行手册支持的操作类型。例如，通过使用 `executeScript` 操作类型，您可以直接在运行手册中嵌入 Python 或 PowerShell 脚本。（在创建自定义运行手册时，您可以按内联方式添加脚本，或者从 S3 存储桶或本地计算机中附加脚本。） 您可以使用 `createStack` 和 `deleteStack` 操作类型自动管理 AWS CloudFormation 资源。此外，通过使用 `executeAwsApi` 操作类型，步骤可以在任意 AWS 服务中运行*任意* API 操作，包括创建或删除 AWS 资源、启动其他进程、触发通知等。

有关自动化支持的所有 20 种操作类型的列表，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。

AWS Systems Manager 自动化提供了几个包含预定义步骤的文档，您可以使用这些步骤执行常见任务，例如重新启动一个或多个 Amazon Elastic Compute Cloud (Amazon EC2) 实例或创建 Amazon Machine Image (AMI)。您还可以创建自己的运行手册并与其他 AWS 账户 共享，或者向所有自动化用户公开。

运行手册是使用 YAML 或 JSON 编写的。但是，通过使用 Systems Manager 自动化中的**文档生成器**，您可以创建运行手册，而无需使用本机 JSON 或 YAML 创作。

**重要**  
如果您运行使用 AWS Identity and Access Management (IAM) 服务角色调用其他服务的自动化工作流程，请注意必须使用权限将该服务角色配置为调用这些服务。该要求适用于所有 AWS 自动化运行手册（`AWS-*` 运行手册），例如 `AWS-ConfigureS3BucketLogging`、`AWS-CreateDynamoDBBackup` 和 `AWS-RestartEC2Instance` 运行手册等。对于您创建的任何自定义自动化运行手册，如果这些文档使用调用其他服务的操作来调用其他 AWS 服务，则此要求同样适用。例如，如果使用 `aws:executeAwsApi`、`aws:createStack` 或 `aws:copyImage` 操作，则您必须配置具有权限的服务角色来调用这些服务。您可以将 IAM 内联策略添加到该角色，从而向其他 AWS 服务授予权限。有关更多信息，请参阅 [（可选）添加自动化内联策略或客户管理型策略来调用其他 AWS 服务](automation-setup-iam.md#add-inline-policy)。

有关可在运行手册中指定的操作的信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。

有关使用 AWS Toolkit for Visual Studio Code 创建运行手册的信息，请参阅 *《AWS Toolkit for Visual Studio Code 用户指南》* 中的 [使用 Systems Manager 自动化文档](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/systems-manager-automation-docs.html)。

有关使用视觉设计器创建自定义运行手册的信息，请参阅 [自动化运行手册的视觉对象设计体验](automation-visual-designer.md)。

**Contents**
+ [自动化运行手册的视觉对象设计体验](automation-visual-designer.md)
  + [视觉对象设计体验界面概述](visual-designer-interface-overview.md)
    + [操作浏览器](visual-designer-interface-overview.md#visual-designer-actions)
    + [Canvas](visual-designer-interface-overview.md#visual-designer-canvas)
    + [表单](visual-designer-interface-overview.md#visual-designer-form)
    + [键盘快捷键](visual-designer-interface-overview.md#visual-designer-keyboard-shortcuts)
  + [使用视觉对象设计体验](visual-designer-use.md)
    + [创建运行手册工作流程](visual-designer-use.md#visual-designer-create-runbook-workflow)
    + [设计运行手册](visual-designer-use.md#visual-designer-build)
    + [更新运行手册](visual-designer-use.md#visual-designer-update-runbook)
    + [导出运行手册](visual-designer-use.md#visual-designer-export-runbook)
  + [配置操作的输入和输出](visual-designer-action-inputs-outputs.md)
    + [为操作提供输入数据](visual-designer-action-inputs-outputs.md#providing-input)
    + [定义操作的输出数据](visual-designer-action-inputs-outputs.md#defining-output)
  + [视觉对象设计体验中的错误处理](visual-designer-error-handling.md)
    + [出现错误时重试操作](visual-designer-error-handling.md#retry-actions)
    + [超时](visual-designer-error-handling.md#timeout-seconds)
    + [失败的操作](visual-designer-error-handling.md#failure-actions)
    + [取消的操作](visual-designer-error-handling.md#cancel-actions)
    + [关键操作](visual-designer-error-handling.md#critical-actions)
    + [结束操作](visual-designer-error-handling.md#end-actions)
  + [教程：使用视觉对象设计体验创建运行手册](visual-designer-tutorial.md)
    + [步骤 1：导航到视觉对象设计体验](visual-designer-tutorial.md#navigate-console)
    + [步骤 2：创建工作流程](visual-designer-tutorial.md#create-workflow)
    + [步骤 3：查看自动生成的代码](visual-designer-tutorial.md#view-generated-code)
    + [步骤 4：运行新的运行手册](visual-designer-tutorial.md#use-tutorial-runbook)
    + [第 5 步：清理](visual-designer-tutorial.md#cleanup-tutorial-runbook)
+ [创作自动化运行手册](automation-authoring-runbooks.md)
  + [识别您的使用案例](automation-authoring-runbooks.md#automation-authoring-runbooks-use-case)
  + [设置开发环境](automation-authoring-runbooks.md#automation-authoring-runbooks-environment)
  + [开发运行手册内容](automation-authoring-runbooks.md#automation-authoring-runbooks-developing-content)
  + [示例 1：创建父子运行手册](automation-authoring-runbooks-parent-child-example.md)
    + [创建子运行手册](automation-authoring-runbooks-parent-child-example.md#automation-authoring-runbooks-child-runbook)
    + [创建父运行手册](automation-authoring-runbooks-parent-child-example.md#automation-authoring-runbooks-parent-runbook)
  + [示例 2：脚本化运行手册](automation-authoring-runbooks-scripted-example.md)
  + [其他运行手册示例](automation-document-examples.md)
    + [部署 VPC 架构和 Microsoft Active Directory 域控制器](automation-document-architecture-deployment-example.md)
    + [从最新快照还原根卷](automation-document-instance-recovery-example.md)
    + [创建 AMI 和跨区域副本](automation-document-backup-maintenance-example.md)
+ [创建填充 AWS 资源的输入参数](populating-input-parameters.md)
+ [正在使用文档生成器创建运行手册](automation-document-builder.md)
  + [使用文档生成器创建运行手册](automation-document-builder.md#create-runbook)
  + [创建运行脚本的运行手册](automation-document-builder.md#create-runbook-scripts)
+ [在运行手册中使用脚本](automation-document-script-considerations.md)
  + [使用运行手册的权限](automation-document-script-considerations.md#script-permissions)
  + [将脚本添加到运行手册](automation-document-script-considerations.md#adding-scripts)
  + [运行手册的脚本限制](automation-document-script-considerations.md#script-constraints)
+ [在运行手册中使用条件语句](automation-branch-condition.md)
  + [使用 `aws:branch` 操作](automation-branch-condition.md#branch-action-explained)
    + [在运行手册中创建 `aws:branch` 步骤](automation-branch-condition.md#create-branch-action)
      + [关于创建输出变量](automation-branch-condition.md#branch-action-output)
    + [示例 `aws:branch` 运行手册](automation-branch-condition.md#branch-runbook-examples)
    + [使用运算符创建复杂的分支自动化](automation-branch-condition.md#branch-operators)
  + [如何使用条件选项的示例](automation-branch-condition.md#conditional-examples)
+ [使用操作输出作为输入](automation-action-outputs-inputs.md)
  + [在运行手册中使用 JSONPath](automation-action-outputs-inputs.md#automation-action-json-path)
+ [为 Automation 创建 Webhook 集成](creating-webhook-integrations.md)
  + [创建集成（控制台）](creating-webhook-integrations.md#creating-integrations-console)
  + [创建集成（命令行）](creating-webhook-integrations.md#creating-integrations-commandline)
  + [为集成创建 Webhooks](creating-webhook-integrations.md#creating-webhooks)
+ [处理运行手册中的超时](automation-handling-timeouts.md)

# 自动化运行手册的视觉对象设计体验
<a name="automation-visual-designer"></a>

AWS Systems Manager Automation 提供低代码的视觉对象设计体验，可帮助您创建自动化运行手册。视觉对象设计体验提供拖放界面，可以选择添加自己的代码，这样您就可以更轻松地创建和编辑运行手册。借助视觉对象设计体验，您可以执行以下操作：
+ 控制条件语句。
+ 控制如何筛选或转换每个操作的输入和输出。
+ 配置错误处理。
+ 制作新运行手册的原型。
+ 使用原型运行手册作为采用 AWS Toolkit for Visual Studio Code 进行本地开发的起点。

创建或编辑运行手册时，您可以从 [Automation 控制台](https://console.aws.amazon.com/systems-manager/automation/home?region=us-east-1#/)访问视觉对象设计体验。创建运行手册时，视觉对象设计体验会验证您的工作并自动生成代码。您可以查看生成的代码，也可以将其导出以供本地开发。完成后，您可以保存并运行运行手册以及在 Systems Manager Automation 控制台中检查结果。

**Topics**
+ [界面概述](visual-designer-interface-overview.md)
+ [使用视觉对象设计体验](visual-designer-use.md)
+ [配置输入和输出](visual-designer-action-inputs-outputs.md)
+ [视觉对象设计体验中的错误处理](visual-designer-error-handling.md)
+ [教程：使用视觉对象设计体验创建运行手册](visual-designer-tutorial.md)

# 视觉对象设计体验界面概述
<a name="visual-designer-interface-overview"></a>

Systems Manager Automation 的视觉对象设计体验是一种低代码的视觉对象工作流程设计器，可帮助您创建自动化运行手册。

通过界面组件的概述了解视觉对象设计体验：

![\[视觉对象设计体验组件\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_overview.png)

+ **操作**浏览器包含**操作**、**AWS API** 和**运行手册**选项卡。
+ 在*画布*中，您可以将操作拖放到工作流程图中、更改操作顺序以及选择要配置或查看的操作。
+ 在**表单**面板中，您可以查看和编辑在画布上选择的任何操作的属性。选择**内容**开关以查看运行手册的 YAML 或 JSON，并突出显示当前选定的操作。

当您需要帮助时，**信息**链接会打开一个包含上下文信息的面板。这些面板还包括指向 Systems Manager Automation 文档中相关主题的链接。

## 操作浏览器
<a name="visual-designer-actions"></a>

在**操作**浏览器中，您可以选择要拖放到工作流程图中的操作。您可以使用**操作**浏览器顶部的搜索字段搜索所有操作。**操作**浏览器包含以下选项卡：
+ **操作**选项卡提供了自动化操作列表，您可以将这些操作拖放到画布中运行手册的工作流程图中。
+ **AWS API** 选项卡提供了 AWS API 列表，您可以将这些 API 拖放到画布中运行手册的工作流程图中。
+ **运行手册**选项卡提供了几个随时可用、可重复使用的运行手册作为构建块，您可以将其用于各种用例。例如，您可以使用运行手册在工作流程中对 Amazon EC2 实例执行常见的修复任务，而无需重新创建相同的操作。

![\[视觉对象设计体验操作浏览器\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_actions_multi_view.png)


## Canvas
<a name="visual-designer-canvas"></a>

选择要添加到自动化中的操作后，将其拖动到画布并放入工作流程图中。您还可以拖放操作，将其移动到运行手册工作流程中的不同位置。如果您的工作流程很复杂，您可能无法在画布面板上查看其所有内容。使用画布顶部的控件来放大或缩小。要查看工作流程的不同部分，您可以在画布中拖动工作流程图。

在**操作**浏览器中拖动一项操作，将其放入运行手册的工作流程图中。有一条线将显示其在您工作流程中的放置位置。要更改操作的顺序，您可以将其拖动到工作流程中的其他位置。新操作已添加到您的工作流程中，其代码已自动生成。

![\[视觉对象设计体验画布\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_canvas.png)


## 表单
<a name="visual-designer-form"></a>

在运行手册工作流程中添加操作后，您可以对其进行配置以满足您的用例。选择希望配置的操作，您将在**表单**面板中看到其参数和选项。您还可以通过选择**内容**开关来查看 YAML 或 JSON 代码。与您所选操作关联的代码会突出显示。

![\[视觉对象设计体验表单面板\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_form.png)


![\[视觉对象设计体验内容面板\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_content.png)


## 键盘快捷键
<a name="visual-designer-keyboard-shortcuts"></a>

视觉对象设计体验支持下表中所示的键盘快捷键。


| 键盘快捷键 | 函数 | 
| --- | --- | 
| Ctrl\$1Z | 撤销上次的操作。 | 
| Ctrl\$1Shift\$1Z | 重做上次的操作。 | 
| Alt\$1C | 将工作流程置于画布中心。 | 
| 退格键 | 删除所有选定状态。 | 
| 删除 | 删除所有选定状态。 | 
| Ctrl\$1D | 复制所选状态。 | 

# 使用视觉对象设计体验
<a name="visual-designer-use"></a>

学习使用视觉对象设计体验创建、编辑和运行运行手册工作流程。工作流程准备就绪后，您可以将其保存或导出。您还可以使用视觉对象设计体验进行快速原型制作。

## 创建运行手册工作流程
<a name="visual-designer-create-runbook-workflow"></a>

1. 登录 [Systems Manager Automation 控制台](https://console.aws.amazon.com/systems-manager/automation/home?region=us-east-1#/)。

1. 选择**创建运行手册**。

1. 在**名称**方框中，输入运行手册的名称，例如 `MyNewRunbook`。

1. 在**设计**和**代码**切换开关旁边，选择铅笔图标并输入运行手册的名称。

现在，您可以为新的运行手册设计工作流程。

## 设计运行手册
<a name="visual-designer-build"></a>

 要使用视觉对象设计体验设计运行手册工作流程，您可以将自动化操作从**操作**浏览器拖动到画布中，将其放置在运行手册工作流程中所需的位置。您也可以通过将操作拖动到不同的位置来重新排序工作流程中的操作。当您将操作拖动到画布上时，工作流程中可以放置该操作的任何位置都会显示一条线。将操作放入画布后，其代码将自动生成并添加到运行手册的内容中。

如果您知道希望添加的操作的名称，请使用**操作**浏览器顶部的搜索框来查找该操作。

将操作放入画布后，使用右侧的**表单**面板对其进行配置。此面板包含您在画布上放置的每个自动化操作或 API 操作的**常规**、**输入**、**输出**和**配置**选项卡。例如，**常规**选项卡由以下部分组成：
+ **步骤名称**用于标识该步骤。为步骤名称指定唯一值。
+ **描述**可帮助您描述在运行手册的工作流程中操作正在执行的内容。

**输入**选项卡包含因操作而异的字段。例如，`aws:executeScript` 自动化操作由以下部分组成：
+ **运行时系统**是运行所提供脚本的语言。
+ **处理程序**是函数的名称。您必须确保在处理程序中定义的函数具有两个参数：`events` 和 `context`。PowerShell 运行时系统不支持此参数。
+ **脚本**是您想要在工作流程期间运行的嵌入式脚本。
+ （可选）**附件**适用于可由操作调用的独立脚本或 .zip 文件。对于 JSON 运行手册，此参数为必需项。

**输出**选项卡可帮助您指定希望从操作中输出的值。您可以在工作流程的后续操作中引用输出值，也可以根据操作生成输出以用于日志记录。并非所有操作都将有**输出**选项卡，因为并非所有操作都支持输出。例如，`aws:pause` 操作就不支持输出。对于支持输出的操作，**输出**选项卡由以下部分组成：
+ **名称**是用于输出值的名称。您可以在工作流程的后续操作中引用输出。
+ **选择器**是以 `"$."` 开头的 JSONPath 表达式字符串，用于选择 JSON 元素中的一个或多个组件。
+ **类型**是输出值的数据类型。例如，`String` 或 `Integer` 数据类型。

**配置**选项卡包含所有自动化操作均可使用的属性和选项。该操作由以下部分组成：
+ **最大尝试次数**属性是操作失败时重试的次数。
+ **超时秒数**属性指定操作的超时值。
+ **至关重要**属性确定操作失败是否会停止整个自动化。
+ **下一步**属性确定自动化在运行手册中接下来要执行的操作。
+ **失败时**属性确定操作失败时自动化在运行手册中接下来要执行的操作。
+ **取消时**属性确定用户取消自动操作时自动化在运行手册中接下来要执行的操作。

要删除操作，您可以使用画布上方工具栏中的退格键，或者右键单击并选择**删除操作**。

随着工作流程的发展，其可能不适合画布。为帮助使工作流程适合画布，请尝试以下选项之一：
+ 使用侧面板上的控件调整面板大小或关闭面板。
+ 使用画布顶部的工具栏放大或缩小工作流程图。

## 更新运行手册
<a name="visual-designer-update-runbook"></a>

您可以通过创建新版本的运行手册来更新现有的运行手册工作流程。可以通过使用视觉对象设计体验或直接编辑代码来更新运行手册。要更新现有运行手册，请参照以下过程：

1. 登录 [Systems Manager Automation 控制台](https://console.aws.amazon.com/systems-manager/automation/home?region=us-east-1#/)。

1. 选择要更新的运行手册。

1. 选择 **Create new version**。

1. 视觉对象设计体验有两个窗格：代码窗格和视觉对象工作流程窗格。在视觉对象工作流程窗格中选择**设计**，以使用视觉对象设计体验编辑您的工作流程。完成后，选择**创建新版本**以保存更改并退出。

1. （可选）使用代码窗格以 YAML 或 JSON 格式编辑运行手册内容。

## 导出运行手册
<a name="visual-designer-export-runbook"></a>

要导出运行手册的工作流程 YAML 或 JSON 代码以及工作流程图，请参照以下过程：

1. 在**文档**控制台中选择您的运行手册。

1. 选择 **Create new version**。

1. 在**操作**下拉菜单中，选择是否要导出图表或运行手册，以及您喜欢的格式。

# 配置操作的输入和输出
<a name="visual-designer-action-inputs-outputs"></a>

每个自动化操作都会根据其收到的输入进行响应。在大多数情况下，您随后会将输出传递给后续操作。在视觉对象设计体验中，您可以在**表单**面板的**输入**和**输出**选项卡中配置操作的输入和输出数据。

有关如何定义和使用自动化操作输出的详细信息，请参阅 [使用操作输出作为输入](automation-action-outputs-inputs.md)。

## 为操作提供输入数据
<a name="providing-input"></a>

每个自动化操作都有一个或多个输入，您必须为其提供值。您为操作输入提供的值由操作接受的数据类型和格式决定。例如，这些 `aws:sleep` 操作要求 `Duration` 输入采用 ISO 8601 格式的字符串值。

通常，您在运行手册的工作流程中使用操作，这些操作将返回要在后续操作中使用的输出。请务必确保输入值正确，以避免运行手册的工作流程出现错误。输入值也很重要，因为其决定了操作是否返回预期的输出。例如，在使用 `aws:executeAwsApi` 操作时，您需要确保为 API 操作提供了正确的值。

## 定义操作的输出数据
<a name="defining-output"></a>

一些自动化操作在执行其定义的操作后会返回输出。返回输出的操作要么具有预定义的输出，要么允许您自己定义输出。例如，`aws:createImage` 操作具有返回 `ImageId` 和 `ImageState` 的预定义输出。相比之下，通过 `aws:executeAwsApi` 操作，您可以定义您想要从指定 API 操作中获得的输出。因此，您可以从单个 API 操作中返回一个或多个值，以便在后续操作中使用。

为自动化操作定义自己的输出需要您指定输出的名称、数据类型和输出值。要继续使用 `aws:executeAwsApi` 操作作为示例，假设您正在从 Amazon EC2 调用 `DescribeInstances` API 操作。在此示例中，您想要返回或输出 Amazon EC2 实例的 `State`，并根据输出对运行手册的工作流程进行分支。您可以选择命名输出 **InstanceState**，然后使用 **String** 数据类型。

定义输出实际值的过程因操作而异。例如，如果您使用的是 `aws:executeScript` 操作，则必须在函数中使用 `return` 语句为输出提供数据。对于 `aws:executeAwsApi`、`aws:waitForAwsResourceProperty` 和 `aws:assertAwsResourceProperty` 等其他操作，则需要 `Selector`。`Selector`（或某些操作所指的 `PropertySelector`），是一个 JSONPath 字符串，用于处理来自 API 操作的 JSON 响应。了解 API 操作的 JSON 响应对象的结构至关重要，这样您才能为输出选择正确的值。使用前面提到 `DescribeInstances` API 操作，请参阅以下 JSON 响应示例：

```
{
  "reservationSet": {
    "item": {
      "reservationId": "r-1234567890abcdef0",
      "ownerId": 123456789012,
      "groupSet": "",
      "instancesSet": {
        "item": {
          "instanceId": "i-1234567890abcdef0",
          "imageId": "ami-bff32ccc",
          "instanceState": {
            "code": 16,
            "name": "running"
          },
          "privateDnsName": "ip-192-168-1-88.eu-west-1.compute.internal",
          "dnsName": "ec2-54-194-252-215.eu-west-1.compute.amazonaws.com",
          "reason": "",
          "keyName": "my_keypair",
          "amiLaunchIndex": 0,
          "productCodes": "",
          "instanceType": "t2.micro",
          "launchTime": "2018-05-08T16:46:19.000Z",
          "placement": {
            "availabilityZone": "eu-west-1c",
            "groupName": "",
            "tenancy": "default"
          },
          "monitoring": {
            "state": "disabled"
          },
          "subnetId": "subnet-56f5f000",
          "vpcId": "vpc-11112222",
          "privateIpAddress": "192.168.1.88",
          "ipAddress": "54.194.252.215",
          "sourceDestCheck": true,
          "groupSet": {
            "item": {
              "groupId": "sg-e4076000",
              "groupName": "SecurityGroup1"
            }
          },
          "architecture": "x86_64",
          "rootDeviceType": "ebs",
          "rootDeviceName": "/dev/xvda",
          "blockDeviceMapping": {
            "item": {
              "deviceName": "/dev/xvda",
              "ebs": {
                "volumeId": "vol-1234567890abcdef0",
                "status": "attached",
                "attachTime": "2015-12-22T10:44:09.000Z",
                "deleteOnTermination": true
              }
            }
          },
          "virtualizationType": "hvm",
          "clientToken": "xMcwG14507example",
          "tagSet": {
            "item": {
              "key": "Name",
              "value": "Server_1"
            }
          },
          "hypervisor": "xen",
          "networkInterfaceSet": {
            "item": {
              "networkInterfaceId": "eni-551ba000",
              "subnetId": "subnet-56f5f000",
              "vpcId": "vpc-11112222",
              "description": "Primary network interface",
              "ownerId": 123456789012,
              "status": "in-use",
              "macAddress": "02:dd:2c:5e:01:69",
              "privateIpAddress": "192.168.1.88",
              "privateDnsName": "ip-192-168-1-88.eu-west-1.compute.internal",
              "sourceDestCheck": true,
              "groupSet": {
                "item": {
                  "groupId": "sg-e4076000",
                  "groupName": "SecurityGroup1"
                }
              },
              "attachment": {
                "attachmentId": "eni-attach-39697adc",
                "deviceIndex": 0,
                "status": "attached",
                "attachTime": "2018-05-08T16:46:19.000Z",
                "deleteOnTermination": true
              },
              "association": {
                "publicIp": "54.194.252.215",
                "publicDnsName": "ec2-54-194-252-215.eu-west-1.compute.amazonaws.com",
                "ipOwnerId": "amazon"
              },
              "privateIpAddressesSet": {
                "item": {
                  "privateIpAddress": "192.168.1.88",
                  "privateDnsName": "ip-192-168-1-88.eu-west-1.compute.internal",
                  "primary": true,
                  "association": {
                    "publicIp": "54.194.252.215",
                    "publicDnsName": "ec2-54-194-252-215.eu-west-1.compute.amazonaws.com",
                    "ipOwnerId": "amazon"
                  }
                }
              },
              "ipv6AddressesSet": {
                "item": {
                  "ipv6Address": "2001:db8:1234:1a2b::123"
                }
              }
            }
          },
          "iamInstanceProfile": {
            "arn": "arn:aws:iam::123456789012:instance-profile/AdminRole",
            "id": "ABCAJEDNCAA64SSD123AB"
          },
          "ebsOptimized": false,
          "cpuOptions": {
            "coreCount": 1,
            "threadsPerCore": 1
          }
        }
      }
    }
  }
}
```

在 JSON 响应对象中，实例 `State` 嵌套在 `Instances` 对象中，而该对象嵌套在 `Reservations` 对象中。要返回实例 `State` 的值，请使用以下字符串作为 `Selector`，以便可以在输出中使用该值：**\$1.Reservations[0].Instances[0].State.Name**。

要在运行手册工作流程的后续操作中引用输出值，请使用以下格式：`{{ StepName.NameOfOutput }}`。例如 **\$1\$1 GetInstanceState.InstanceState \$1\$1**。在视觉对象设计体验中，您可以使用输入下拉菜单选择要在后续操作中使用的输出值。在后续操作中使用输出时，输出的数据类型必须与输入的数据类型相匹配。在此示例中，`InstanceState` 输出为 `String`。因此，要在后续操作的输入中使用该值，输入必须接受 `String`。

# 视觉对象设计体验中的错误处理
<a name="visual-designer-error-handling"></a>

默认情况下，当操作报告错误时，自动化会完全停止运行手册的工作流程。这是因为所有操作 `onFailure` 属性的默认值为 `Abort`。您可以配置自动化如何处理运行手册工作流程中错误。即使您配置了错误处理，某些错误仍可能导致自动化失败。有关更多信息，请参阅 [Systems Manager 自动化故障排除](automation-troubleshooting.md)。在视觉对象设计体验中，您可以在**配置**面板中配置错误处理。

![\[错误处理选项\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_error_handling.png)


## 出现错误时重试操作
<a name="retry-actions"></a>

要在出现错误时重试操作，请为**最大尝试次数**属性指定一个值。默认值是 1。如果您指定的值大于 1，则直到所有重试尝试失败后，才会将此操作视为失败。

## 超时
<a name="timeout-seconds"></a>

您可以为操作配置超时，以设置操作在失败之前可以运行的最大秒数。要配置超时，请在**超时秒数**属性中输入操作在失败之前应等待的秒数。如果达到超时并且操作的 `Max attempts` 值大于 1，则在重试完成之前该步骤不会被视为已超时。

## 失败的操作
<a name="failure-actions"></a>

默认情况下，当操作失败时，自动化会完全停止运行手册的工作流程。您可以通过为运行手册中操作的**失败时**属性指定替代值来修改此行为。如果您希望工作流程继续到运行手册中的下一步，请选择**继续**。如果您希望工作流程跳至运行手册中的其他后续步骤，请选择**步骤**，然后输入该步骤的名称。

## 取消的操作
<a name="cancel-actions"></a>

默认情况下，当用户取消操作时，自动化会完全停止运行手册的工作流程。您可以通过为运行手册中操作的**取消时**属性指定替代值来修改此行为。如果您希望工作流程跳至运行手册中的其他后续步骤，请选择**步骤**，然后输入该步骤的名称。

## 关键操作
<a name="critical-actions"></a>

您可以将某项操作指定为*关键*操作，这意味着其决定了自动化的总体报告状态。如果具有此指定的步骤失败，则无论其他操作是否成功，自动化都会将最终状态报告为 `Failed`。要将某项操作配置为关键，请将**至关重要**属性的默认值保留为 **True**。

## 结束操作
<a name="end-actions"></a>

**结束**属性在指定操作结束时停止自动化。此属性的默认值为 `false`。如果您为操作配置此属性，则无论操作成功还是失败，自动化都会停止。此属性最常与 `aws:branch` 操作一起使用，以处理意外或未定义的输入值。以下示例显示了期望实例状态为 `running`、`stopping` 或 `stopped` 的运行手册。如果实例处于不同的状态，则自动化结束。

![\[视觉对象设计体验就是结束示例\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_is_end_example.png)


# 教程：使用视觉对象设计体验创建运行手册
<a name="visual-designer-tutorial"></a>

在本教程中，您将学习使用 Systems Manager Automation 提供的视觉对象设计体验的基础知识。在视觉对象设计体验中，您可以创建使用多个操作的运行手册。您可以使用拖放功能在画布上排列操作。您还可以搜索、选择和配置这些操作。然后，您可以查看为运行手册工作流程自动生成的 YAML 代码、退出视觉对象设计体验、运行运行手册并查看执行详情。

本教程还向您展示了如何更新运行手册和查看新版本。在本教程的最后，您将执行清理步骤并删除运行手册。

完成本教程的学习后，您将知道如何使用视觉对象设计体验来创建运行手册。您还将了解如何更新、运行和删除运行手册。

**注意**  
在开始学习本教程之前，请确保完成 [设置自动化](automation-setup.md)。

**Topics**
+ [步骤 1：导航到视觉对象设计体验](#navigate-console)
+ [步骤 2：创建工作流程](#create-workflow)
+ [步骤 3：查看自动生成的代码](#view-generated-code)
+ [步骤 4：运行新的运行手册](#use-tutorial-runbook)
+ [第 5 步：清理](#cleanup-tutorial-runbook)

## 步骤 1：导航到视觉对象设计体验
<a name="navigate-console"></a>

1. 登录 [Systems Manager Automation 控制台](https://console.aws.amazon.com/systems-manager/automation/home?region=us-east-1#/)。

1. 选择**创建自动化运行手册**。

## 步骤 2：创建工作流程
<a name="create-workflow"></a>

在视觉对象设计体验中，工作流程是画布上运行手册的图形表示。您可以使用视觉对象设计体验来定义、配置和检查运行手册的各个操作。

**创建工作流程**

1. 在**设计**和**代码**切换开关旁边，选择铅笔图标并输入运行手册的名称。在本教程中，请输入 **VisualDesignExperienceTutorial**。  
![\[视觉对象设计体验为您的运行手册命名\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_tutorial_name.png)

1. 在**表单**面板的**文档属性**部分，展开**输入参数**下拉菜单，然后选择**添加参数**。

   1. 在**参数名称**字段中，输入 **InstanceId**。

   1. 在**类型**下拉菜单中，选择 **AWS::EC2::Instance**。

   1. 选择**必需**开关。  
![\[创建运行手册的参数\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_actions_tutorial_parameter.png)

1. 在 **AWS API** 浏览器中，在搜索栏中输入 **DescribeInstances**。

1. 将 **Amazon EC2 – DescribeInstances** 操作拖动到空白画布上。

1. 对于**步骤名称**，请输入一个值。在本教程中，您可以使用名称 **GetInstanceState**。  
![\[选择 Amazon EC2 描述实例 API 操作。\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_tutorial_api_action.png)

   1. 展开**其他输入**下拉菜单，然后在**输入名称**字段中输入 **InstanceIds**。

   1. 选择**输入**选项卡。

   1. 在**输入值**字段中，选择 **InstanceId** 文档输入。这将引用在过程开始时创建的输入参数的值。鉴于 `DescribeInstances` 操作的 **InstanceIds** 输入接受 `StringList` 值，必须将 **InstanceId** 输入用方括号括起来。**输入值**的 YAML 应与以下内容匹配：**['\$1\$1 InstanceId \$1\$1']**。

   1. 在**输出**选项卡中，请选择**添加输出**，然后在**名称**字段中输入 **InstanceState**。

   1. 在**选择器**字段中，请输入 **\$1.Reservations[0].Instances[0].State.Name**。

   1. 在**类型**下拉菜单中，请选择**字符串**。

1. 从**操作**浏览器中拖动**分支**操作，然后将其放入 **`GetInstanceState`** 步骤下方。

1. 对于**步骤名称**，请输入一个值。在本教程中，请使用名称 `BranchOnInstanceState`。

   要定义分支逻辑，请执行以下操作：

   1. 在画布上选择 **`Branch`** 状态。然后，在**输入**和**选择**下，选择铅笔图标以编辑**规则 \$11**。

   1. 选择**添加条件**。

   1. 在**规则 \$11 的条件**对话框中，从**变量**下拉菜单中选择 **GetInstanceState.InstanceState** 步骤输出。

   1. 对于**运算符**，请选择**等于**。

   1. 对于**值**，请从下拉列表中选择**字符串**。输入 **stopped**。  
![\[定义分支操作的条件。\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_tutorial_condition.png)

   1. 选择**保存条件**。

   1. 选择**添加新的选择规则**。

   1. 为**规则 \$12** 选择**添加条件**。

   1. 在**规则 \$12 的条件**对话框中，从**变量**下拉菜单中选择 **GetInstanceState.InstanceState** 步骤输出。

   1. 对于**运算符**，请选择**等于**。

   1. 对于**值**，请从下拉列表中选择**字符串**。输入 **stopping**。

   1. 选择**保存条件**。

   1. 选择**添加新的选择规则**。

   1. 对于**规则 \$13**，请选择**添加条件**。

   1. 在**规则 \$13 的条件**对话框中，从**变量**下拉菜单中选择 **GetInstanceState.InstanceState** 步骤输出。

   1. 对于**运算符**，请选择**等于**。

   1. 对于**值**，请从下拉列表中选择**字符串**。输入 **running**。

   1. 选择**保存条件**。

   1. 在**默认规则**中，选择**转到结尾**作为**默认步骤**。

1. 将**更改实例状态**操作拖动到 **\$1\$1 GetInstanceState.InstanceState \$1\$1 == "stopped"** 条件下的**将操作拖动到此处**空白框中。

   1. 对于**步骤名称**，请输入 **StartInstance**。

   1. 在**输入**选项卡的**实例 ID** 下，从下拉菜单中选择 **InstanceId** 文档输入值。

   1. 对于**期望状态**，请指定 **`running`**。

1. 将**等待 AWS 资源**操作拖动到 **\$1\$1 GetInstanceState.InstanceState \$1\$1 == "stopping"** 条件下的**将操作拖动到此处**空白框中。

1. 对于**步骤名称**，请输入一个值。在本教程中，请使用名称 `WaitForInstanceStop`。

   1. 对于**服务**字段，请选择 **Amazon EC2**。

   1. 对于 **API** 字段，请选择 **DescribeInstances**。

   1. 对于**属性选择器**字段，请输入 **\$1.Reservations[0].Instances[0].State.Name**。

   1. 对于**期望值**参数，请输入 **`["stopped"]`**。

   1. 在 **WaitForInstanceStop** 操作的**配置**选项卡中，从**下一步**下拉菜单中选择 **StartInstance**。

1. 将**在实例上运行命令**操作拖动到 **\$1\$1 GetInstanceState.InstanceState \$1\$1 == "running"** 条件下的**将操作拖动到此处**空白框中。

1. 对于**步骤名称**，请输入 **SayHello**。

   1. 在**输入**选项卡中，输入 **AWS-RunShellScript** 作为**文档名称**参数。

   1. 对于 **InstanceId**，请从下拉菜单中选择 **InstanceId** 文档输入值。

   1. 展开**其他输入**下拉菜单，在**输入名称**下拉菜单中，选择**参数**。

   1. 在**输入值**字段中，请输入 **`{"commands": "echo 'Hello World'"}`**。

1. 在画布中查看已完成的运行手册，然后选择**创建运行手册**以保存教程运行手册。  
![\[查看和创建运行手册。\]](http://docs.aws.amazon.com/zh_cn/systems-manager/latest/userguide/images/visual_designer_tutorial_complete.png)

## 步骤 3：查看自动生成的代码
<a name="view-generated-code"></a>

当您将操作从**操作**浏览器拖放到画布上时，视觉对象设计体验会自动实时编写运行手册的 YAML 或 JSON 内容。您可以查看和编辑此代码。要查看自动生成的代码，在**设计**和**代码**切换开关中选择**代码**。

## 步骤 4：运行新的运行手册
<a name="use-tutorial-runbook"></a>

创建运行手册后，您可以运行自动化。

**运行新的自动化运行手册**

1. 访问 [https://console.aws.amazon.com/systems-manager/](https://console.aws.amazon.com/systems-manager/)，打开 AWS Systems Manager 控制台。

1. 在导航窗格中，选择**自动化**，然后选择**执行自动化**。

1. 在**自动化文档**列表中，请选择运行手册。在**文档类别**窗格中选择一个或多个选项，以便根据 SSM 文档的用途对其进行筛选。要查看您拥有的运行手册，请选择**我拥有的**选项卡。要查看与您的账户共享的运行手册，请选择**与我共享**选项卡。要查看所有运行手册，请选择**所有文档**选项卡。
**注意**  
您可以通过选择运行手册名称来查看有关该手册的信息。

1. 在**文档详细信息**部分中，验证**文档版本**已设置为要运行的版本。系统包括以下版本选项：
   + **运行时的默认版本** – 如果定期更新自动化运行手册并分配新的默认版本，请选择此选项。
   + **运行时的最新版本** – 如果定期更新自动化运行手册并且想要运行最新更新的版本，请选择此选项。
   + **1（默认）** – 选择此选项可执行文档的第一个版本，即默认版本。

1. 选择**下一步**。

1. 在**执行自动化运行手册**部分，请选择**简单执行**。

1. 在 **输入参数** 部分中，指定所需的输入。或者，您也可以从 **AutomationAssumeRole** 列表选择一个 IAM 服务角色。

1. （可选）选择一个 Amazon CloudWatch 警报应用于您的自动化，以便进行监控。要将 CloudWatch 警报附加到自动化，启动自动化的 IAM 主体必须具有 `iam:createServiceLinkedRole` 操作的权限。有关 CloudWatch 警报的更多信息，请参阅[使用 Amazon CloudWatch 警报](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html)。如果您的警报激活，自动化将停止。如果使用 AWS CloudTrail，您将在跟踪中看到 API 调用。

1. 选择**执行**。

## 第 5 步：清理
<a name="cleanup-tutorial-runbook"></a>

**删除您的运行手册**

1. 访问 [https://console.aws.amazon.com/systems-manager/](https://console.aws.amazon.com/systems-manager/)，打开 AWS Systems Manager 控制台。

1. 在导航窗格中，选择**文档**。

1. 选择**我拥有的**选项卡。

1. 找到 **VisualDesignExperienceTutorial** 运行手册。

1. 在文档卡页面上选择按钮，然后从**操作**下拉菜单中选择**删除文档**。

# 创作自动化运行手册
<a name="automation-authoring-runbooks"></a>

Automation 是 AWS Systems Manager 中的一项工具，其中的每个运行手册定义了一个自动化。自动化运行手册定义了在自动化过程中执行的操作。在运行手册内容中，您可以定义 Systems Manager 对托管实例和 AWS 资源执行的输入参数、输出和操作。

自动化包含几个预定义的运行手册，您可以用它们来执行常见任务，例如重启一个或多个 Amazon Elastic Compute Cloud (Amazon EC2) 实例，或创建 Amazon Machine Image (AMI)。但是，您的使用案例可能会超出预定义运行手册的功能。如果是这种情况，您可以创建自己的运行手册并根据需要修改它们。

运行手册由自动化操作、这些操作的参数以及您指定的输入参数组成。运行手册的内容是以 YAML 或 JSON 格式编写的。如果不熟悉 YAML 或 JSON，建议使用视觉设计器，或者在尝试创作自己的运行手册之前了解更多关于任一标记语言的信息。有关视觉设计器的更多信息，请参阅 [自动化运行手册的视觉对象设计体验](automation-visual-designer.md)。

以下部分将帮助您创作第一个运行手册。

## 识别您的使用案例
<a name="automation-authoring-runbooks-use-case"></a>

创作运行手册的第一步是确定您的使用案例。例如，您计划了 `AWS-CreateImage` 运行手册，以便每天在您的所有生产 Amazon EC2 实例上运行。月底时，您决定拥有的映像数量超过恢复点所需的映像。接下来，您希望在创建新 AMI 时，自动删除 Amazon EC2 实例的最早 AMI。为了实现此目的，您可以创建执行以下操作的新运行手册：

1. 运行 `aws:createImage` 操作，并在映像描述中指定实例 ID。

1. 运行 `aws:waitForAwsResourceProperty` 操作来轮询图像的状态，直到此状态为 `available`。

1. 在图像状态变为 `available` 后，`aws:executeScript` 操作会运行一个自定义 Python 脚本，用于收集与 Amazon EC2 实例关联的所有映像的 ID。脚本使用您在创建时指定的映像描述中的实例 ID 进行筛选，完成此操作。然后，脚本根据图像的 `creationDate` 对图像 ID 列表进行排序，并输出最早的 AMI。

1. 最后，`aws:deleteImage` 操作运行，使用上一步输出中的 ID 删除最早的 AMI。

在这种情况下，您已经使用 `AWS-CreateImage` 运行手册，但发现您的使用案例需要更大的灵活性。这是一种常见的情况，因为运行手册和自动化操作之间可能存在重叠。因此，您可能需要调整用于解决您的使用案例的运行手册或操作。

例如，`aws:executeScript` 和 `aws:invokeLambdaFunction` 操作都允许您在自动化过程中运行自定义脚本。要在它们之间进行选择，您可能更喜欢 `aws:invokeLambdaFunction`，因为它还支持其他运行时语言。但是，您可能更喜欢 `aws:executeScript`，因为它允许您直接在 YAML 运行手册中创作脚本内容，并提供脚本内容作为 JSON 运行手册的附件。您也可以考虑 `aws:executeScript`，因为其 AWS Identity and Access Management (IAM) 设置更简单。因为它使用 `AutomationAssumeRole` 提供的权限，`aws:executeScript` 不需要使用额外的 AWS Lambda 函数执行角色。

在任何给定的情况下，一个操作可能会比另一个操作提供更大的灵活性或更多功能。因此，我们建议您查看要使用的运行手册或操作的可用输入参数，以确定哪些参数最适合您的使用案例和首选项。

## 设置开发环境
<a name="automation-authoring-runbooks-environment"></a>

确定您的使用案例以及要在运行手册中使用的预定义运行手册或自动化操作后，请为运行手册的内容设置开发环境。要开发您的运行手册内容，我们建议使用 AWS Toolkit for Visual Studio Code 而不是 Systems Manager 文档控制台。

Toolkit for VS Code 是 Visual Studio 代码（VS 代码）的开源扩展，提供了比 Systems Manager 文档控制台更多的功能。有用的功能包括针对 YAML 和 JSON 的模式验证、自动化操作类型的代码片段以及对各种 YAML 和 JSON 格式选项的自动完成支持。

有关安装 Toolkit for VS Code 的更多信息，请参阅[安装 AWS Toolkit for Visual Studio Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/setup-toolkit.html)。有关使用适用于 Toolkit for VS Code 开发运行手册的更多信息，请参阅 *AWS Toolkit for Visual Studio Code 用户指南*中的[使用 Systems Manager 运行手册](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/systems-manager-automation-docs.html)

## 开发运行手册内容
<a name="automation-authoring-runbooks-developing-content"></a>

确定使用案例并设置环境后，您便做好了开发适用于运行手册内容的准备。您的使用案例和首选项在很大程度上决定了您在运行手册内容中使用的自动化操作或运行手册。与允许您完成类似任务的其他操作相比，某些操作仅支持输入参数的子集。其他操作具有特定的输出，例如 `aws:createImage`，其中一些操作允许您定义自己的输出，例如 `aws:executeAwsApi`。

如果您不确定如何在运行手册中使用特定操作，我们建议您查看 [Systems Manager 自动化操作参考](automation-actions.md) 中的操作对应条目。我们还建议您查看预定义运行手册的内容，以查看有关如何使用这些操作的实例。有关运行手册的实际应用程序的更多实例，请参阅 [其他运行手册示例](automation-document-examples.md)。

为了展示运行手册内容在简单性和灵活性方面的差异，以下教程提供了如何分阶段修补 Amazon EC2 实例组的示例：
+ [示例 1：创建父子运行手册](automation-authoring-runbooks-parent-child-example.md) - 在此示例中，父子关系中使用了两个运行手册。父运行手册启动子运行手册的速率控制自动化。
+ [示例 2：脚本化运行手册](automation-authoring-runbooks-scripted-example.md) - 此示例演示如何通过将内容压缩到单个运行手册中并使用运行手册中的脚本来完成示例 1 的相同任务。

# 示例 1：创建父子运行手册
<a name="automation-authoring-runbooks-parent-child-example"></a>

以下示例演示如何创建两个运行手册，以便分阶段修补已标记的 Amazon Elastic Compute Cloud (Amazon EC2) 实例组。这些运行手册用于父子关系，其中父运行手册用于启动子运行手册的速率控制自动化。有关使用速率控制自动化的更多信息，请参阅 [大规模运行自动化操作](running-automations-scale.md)。有关此示例中使用的自动化操作的更多信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。

## 创建子运行手册
<a name="automation-authoring-runbooks-child-runbook"></a>

此示例运行手册解决以下情形。Emily 是 AnyCompany Consultants, LLC 的系统工程师。她需要为托管主数据库和辅助数据库的 Amazon Elastic Compute Cloud (Amazon EC2) 实例组配置补丁程序。应用程序每天 24 小时访问这些数据库，因此两个数据库实例中必须有一个始终可用。

她确定分阶段修补实例是最佳方法。首先将对数据库实例的主组进行修补，然后修补数据库实例的辅助组。此外，为了避免由于使之前已停止的实例运行而产生额外的成本，Emily 希望在进行修补之前将修补的实例返回到其原始状态。

Emily 通过与实例关联的标签来标识数据库实例的主组和辅助组。她决定创建一个父运行手册，用于启动子运行手册的速率控制自动化。通过这种方法，她可以将与数据库实例的主组和辅助组关联的标签设置为目标，并管理子自动化的并发性。查看了可用于修补的 Systems Manager (SSM) 文档后，她选择了 `AWS-RunPatchBaseline` 文档。通过使用此 SSM 文档，她的同事可以在修补操作完成后查看相关的修补程序合规性信息。

为了开始创建运行手册内容，Emily 查看了可用的自动化操作，并开始为子运行手册创作内容，如下所示：

1. 首先，她为运行手册的架构和描述提供值，并定义子运行手册的输入参数。

   通过使用 `AutomationAssumeRole` 参数，Emily 和她的同事可以使用现有的 IAM 角色，以允许自动化代表他们执行运行手册中的操作。Emily 使用 `InstanceId` 参数来确定应修补的实例。（可选）`Operation`、`RebootOption` 和 `SnapshotId` 参数可用于为 `AWS-RunPatchBaseline` 的文档参数提供值。为了防止向这些运行手册参数提供无效值，她根据需要定义了 `allowedValues`。

------
#### [ YAML ]

   ```
   schemaVersion: '0.3'
   description: 'An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.'
   assumeRole: '{{AutomationAssumeRole}}'
   parameters:
     AutomationAssumeRole:
       type: String
       description: >-
         '(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the
         actions on your behalf. If no role is specified, Systems Manager
         Automation uses your IAM permissions to operate this runbook.'
       default: ''
     InstanceId:
       type: String
       description: >-
         '(Required) The instance you want to patch.'
     SnapshotId:
       type: String
       description: '(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.'
       default: ''
     RebootOption:
       type: String
       description: '(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.'
       allowedValues:
         - NoReboot
         - RebootIfNeeded
       default: RebootIfNeeded
     Operation:
       type: String
       description: '(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.'
       allowedValues:
         - Install
         - Scan
       default: Install
   ```

------
#### [ JSON ]

   ```
   {
      "schemaVersion":"0.3",
      "description":"An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.",
      "assumeRole":"{{AutomationAssumeRole}}",
      "parameters":{
         "AutomationAssumeRole":{
            "type":"String",
            "description":"(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.",
            "default":""
         },
         "InstanceId":{
            "type":"String",
            "description":"(Required) The instance you want to patch."
         },
         "SnapshotId":{
            "type":"String",
            "description":"(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.",
            "default":""
         },
         "RebootOption":{
            "type":"String",
            "description":"(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.",
            "allowedValues":[
               "NoReboot",
               "RebootIfNeeded"
            ],
            "default":"RebootIfNeeded"
         },
         "Operation":{
            "type":"String",
            "description":"(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.",
            "allowedValues":[
               "Install",
               "Scan"
            ],
            "default":"Install"
         }
      }
   },
   ```

------

1. 定义完顶层元素后，Emily 继续创作构成运行手册的 `mainSteps` 的操作。第一步使用 `aws:executeAwsApi` 操作输出 `InstanceId` 输入参数中指定的目标实例的当前状态。此操作的输出将用于后续操作。

------
#### [ YAML ]

   ```
   mainSteps:
     - name: getInstanceState
       action: 'aws:executeAwsApi'
       onFailure: Abort
       inputs:
         inputs:
         Service: ec2
         Api: DescribeInstances
         InstanceIds:
           - '{{InstanceId}}'
       outputs:
         - Name: instanceState
           Selector: '$.Reservations[0].Instances[0].State.Name'
           Type: String
       nextStep: branchOnInstanceState
   ```

------
#### [ JSON ]

   ```
   "mainSteps":[
         {
            "name":"getInstanceState",
            "action":"aws:executeAwsApi",
            "onFailure":"Abort",
            "inputs":{
               "inputs":null,
               "Service":"ec2",
               "Api":"DescribeInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ]
            },
            "outputs":[
               {
                  "Name":"instanceState",
                  "Selector":"$.Reservations[0].Instances[0].State.Name",
                  "Type":"String"
               }
            ],
            "nextStep":"branchOnInstanceState"
         },
   ```

------

1. Emily 没有手动启动和跟踪需要修补的每个实例的原始状态，而是根据目标实例的状态使用上一操作的输出对自动化进行分支。这允许自动化根据 `aws:branch` 操作中定义的条件运行不同的步骤，并提高了自动化的整体效率，无需人工干预。

   如果实例状态已为 `running`，则自动化将继续使用 `aws:runCommand` 操作，利用 `AWS-RunPatchBaseline` 文档修补实例。

   如果实例的状态为 `stopping`，则自动化使用 `aws:waitForAwsResourceProperty` 操作轮询实例以达到 `stopped` 状态，使用 `executeAwsApi` 操作启动实例，并轮询实例以达到 `running` 状态，然后再修补实例。

   如果实例的状态为 `stopped`，则自动化将启动实例并轮询实例以达到 `running` 状态，然后再使用相同的操作修补实例。

------
#### [ YAML ]

   ```
   - name: branchOnInstanceState
       action: 'aws:branch'
       onFailure: Abort
       inputs:
         Choices:
           - NextStep: startInstance
              Variable: '{{getInstanceState.instanceState}}'
              StringEquals: stopped
            - NextStep: verifyInstanceStopped
              Variable: '{{getInstanceState.instanceState}}'
              StringEquals: stopping
            - NextStep: patchInstance
              Variable: '{{getInstanceState.instanceState}}'
              StringEquals: running
       isEnd: true
     - name: startInstance
       action: 'aws:executeAwsApi'
       onFailure: Abort
       inputs:
         Service: ec2
         Api: StartInstances
         InstanceIds:
           - '{{InstanceId}}'
       nextStep: verifyInstanceRunning
     - name: verifyInstanceRunning
       action: 'aws:waitForAwsResourceProperty'
       timeoutSeconds: 120
       inputs:
         Service: ec2
         Api: DescribeInstances
         InstanceIds:
           - '{{InstanceId}}'
         PropertySelector: '$.Reservations[0].Instances[0].State.Name'
         DesiredValues:
           - running
       nextStep: patchInstance
     - name: verifyInstanceStopped
       action: 'aws:waitForAwsResourceProperty'
       timeoutSeconds: 120
       inputs:
         Service: ec2
         Api: DescribeInstances
         InstanceIds:
           - '{{InstanceId}}'
         PropertySelector: '$.Reservations[0].Instances[0].State.Name'
         DesiredValues:
           - stopped
         nextStep: startInstance
     - name: patchInstance
       action: 'aws:runCommand'
       onFailure: Abort
       timeoutSeconds: 5400
       inputs:
         DocumentName: 'AWS-RunPatchBaseline'
         InstanceIds: 
         - '{{InstanceId}}'
         Parameters:
           SnapshotId: '{{SnapshotId}}'
           RebootOption: '{{RebootOption}}'
           Operation: '{{Operation}}'
   ```

------
#### [ JSON ]

   ```
   {
            "name":"branchOnInstanceState",
            "action":"aws:branch",
            "onFailure":"Abort",
            "inputs":{
               "Choices":[
                  {
                     "NextStep":"startInstance",
                     "Variable":"{{getInstanceState.instanceState}}",
                     "StringEquals":"stopped"
                  },
                  {
                     "Or":[
                        {
                           "Variable":"{{getInstanceState.instanceState}}",
                           "StringEquals":"stopping"
                        }
                     ],
                     "NextStep":"verifyInstanceStopped"
                  },
                  {
                     "NextStep":"patchInstance",
                     "Variable":"{{getInstanceState.instanceState}}",
                     "StringEquals":"running"
                  }
               ]
            },
            "isEnd":true
         },
         {
            "name":"startInstance",
            "action":"aws:executeAwsApi",
            "onFailure":"Abort",
            "inputs":{
               "Service":"ec2",
               "Api":"StartInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ]
            },
            "nextStep":"verifyInstanceRunning"
         },
         {
            "name":"verifyInstanceRunning",
            "action":"aws:waitForAwsResourceProperty",
            "timeoutSeconds":120,
            "inputs":{
               "Service":"ec2",
               "Api":"DescribeInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ],
               "PropertySelector":"$.Reservations[0].Instances[0].State.Name",
               "DesiredValues":[
                  "running"
               ]
            },
            "nextStep":"patchInstance"
         },
         {
            "name":"verifyInstanceStopped",
            "action":"aws:waitForAwsResourceProperty",
            "timeoutSeconds":120,
            "inputs":{
               "Service":"ec2",
               "Api":"DescribeInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ],
               "PropertySelector":"$.Reservations[0].Instances[0].State.Name",
               "DesiredValues":[
                  "stopped"
               ],
               "nextStep":"startInstance"
            }
         },
         {
            "name":"patchInstance",
            "action":"aws:runCommand",
            "onFailure":"Abort",
            "timeoutSeconds":5400,
            "inputs":{
               "DocumentName":"AWS-RunPatchBaseline",
               "InstanceIds":[
                  "{{InstanceId}}"
               ],
               "Parameters":{
                  "SnapshotId":"{{SnapshotId}}",
                  "RebootOption":"{{RebootOption}}",
                  "Operation":"{{Operation}}"
               }
            }
         },
   ```

------

1. 修补操作完成后，Emily 希望自动化将目标实例返回到自动化开始之前的状态。她通过再次使用第一个动作的输出来完成此操作。自动化根据目标实例的原始状态，使用 `aws:branch` 操作进行分支。如果实例之前处于 `running` 以外的任何状态，实例将停止。否则，如果实例状态为 `running`，则自动化结束。

------
#### [ YAML ]

   ```
   - name: branchOnOriginalInstanceState
       action: 'aws:branch'
       onFailure: Abort
       inputs:
         Choices:
           - NextStep: stopInstance
             Not: 
               Variable: '{{getInstanceState.instanceState}}'
               StringEquals: running
       isEnd: true
     - name: stopInstance
       action: 'aws:executeAwsApi'
       onFailure: Abort
       inputs:
         Service: ec2
         Api: StopInstances
         InstanceIds:
           - '{{InstanceId}}'
   ```

------
#### [ JSON ]

   ```
   {
            "name":"branchOnOriginalInstanceState",
            "action":"aws:branch",
            "onFailure":"Abort",
            "inputs":{
               "Choices":[
                  {
                     "NextStep":"stopInstance",
                     "Not":{
                        "Variable":"{{getInstanceState.instanceState}}",
                        "StringEquals":"running"
                     }
                  }
               ]
            },
            "isEnd":true
         },
         {
            "name":"stopInstance",
            "action":"aws:executeAwsApi",
            "onFailure":"Abort",
            "inputs":{
               "Service":"ec2",
               "Api":"StopInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ]
            }
         }
      ]
   }
   ```

------

1. Emily 检查完成的子运行手册内容，并在同一 AWS 账户 和 AWS 区域 中创建运行手册作为目标实例。现在，她已经准备好继续创建父运行手册的内容。下面是完成的子运行手册内容。

------
#### [ YAML ]

   ```
   schemaVersion: '0.3'
   description: 'An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.'
   assumeRole: '{{AutomationAssumeRole}}'
   parameters:
     AutomationAssumeRole:
       type: String
       description: >-
         '(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the
         actions on your behalf. If no role is specified, Systems Manager
         Automation uses your IAM permissions to operate this runbook.'
       default: ''
     InstanceId:
       type: String
       description: >-
         '(Required) The instance you want to patch.'
     SnapshotId:
       type: String
       description: '(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.'
       default: ''
     RebootOption:
       type: String
       description: '(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.'
       allowedValues:
         - NoReboot
         - RebootIfNeeded
       default: RebootIfNeeded
     Operation:
       type: String
       description: '(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.'
       allowedValues:
         - Install
         - Scan
       default: Install
   mainSteps:
     - name: getInstanceState
       action: 'aws:executeAwsApi'
       onFailure: Abort
       inputs:
         inputs:
         Service: ec2
         Api: DescribeInstances
         InstanceIds:
           - '{{InstanceId}}'
       outputs:
         - Name: instanceState
           Selector: '$.Reservations[0].Instances[0].State.Name'
           Type: String
       nextStep: branchOnInstanceState
     - name: branchOnInstanceState
       action: 'aws:branch'
       onFailure: Abort
       inputs:
         Choices:
           - NextStep: startInstance
             Variable: '{{getInstanceState.instanceState}}'
             StringEquals: stopped
           - Or:
               - Variable: '{{getInstanceState.instanceState}}'
                 StringEquals: stopping
             NextStep: verifyInstanceStopped
           - NextStep: patchInstance
             Variable: '{{getInstanceState.instanceState}}'
             StringEquals: running
       isEnd: true
     - name: startInstance
       action: 'aws:executeAwsApi'
       onFailure: Abort
       inputs:
         Service: ec2
         Api: StartInstances
         InstanceIds:
           - '{{InstanceId}}'
       nextStep: verifyInstanceRunning
     - name: verifyInstanceRunning
       action: 'aws:waitForAwsResourceProperty'
       timeoutSeconds: 120
       inputs:
         Service: ec2
         Api: DescribeInstances
         InstanceIds:
           - '{{InstanceId}}'
         PropertySelector: '$.Reservations[0].Instances[0].State.Name'
         DesiredValues:
           - running
       nextStep: patchInstance
     - name: verifyInstanceStopped
       action: 'aws:waitForAwsResourceProperty'
       timeoutSeconds: 120
       inputs:
         Service: ec2
         Api: DescribeInstances
         InstanceIds:
           - '{{InstanceId}}'
         PropertySelector: '$.Reservations[0].Instances[0].State.Name'
         DesiredValues:
           - stopped
         nextStep: startInstance
     - name: patchInstance
       action: 'aws:runCommand'
       onFailure: Abort
       timeoutSeconds: 5400
       inputs:
         DocumentName: 'AWS-RunPatchBaseline'
         InstanceIds: 
         - '{{InstanceId}}'
         Parameters:
           SnapshotId: '{{SnapshotId}}'
           RebootOption: '{{RebootOption}}'
           Operation: '{{Operation}}'
     - name: branchOnOriginalInstanceState
       action: 'aws:branch'
       onFailure: Abort
       inputs:
         Choices:
           - NextStep: stopInstance
             Not: 
               Variable: '{{getInstanceState.instanceState}}'
               StringEquals: running
       isEnd: true
     - name: stopInstance
       action: 'aws:executeAwsApi'
       onFailure: Abort
       inputs:
         Service: ec2
         Api: StopInstances
         InstanceIds:
           - '{{InstanceId}}'
   ```

------
#### [ JSON ]

   ```
   {
      "schemaVersion":"0.3",
      "description":"An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.",
      "assumeRole":"{{AutomationAssumeRole}}",
      "parameters":{
         "AutomationAssumeRole":{
            "type":"String",
            "description":"'(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.'",
            "default":""
         },
         "InstanceId":{
            "type":"String",
            "description":"'(Required) The instance you want to patch.'"
         },
         "SnapshotId":{
            "type":"String",
            "description":"(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.",
            "default":""
         },
         "RebootOption":{
            "type":"String",
            "description":"(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.",
            "allowedValues":[
               "NoReboot",
               "RebootIfNeeded"
            ],
            "default":"RebootIfNeeded"
         },
         "Operation":{
            "type":"String",
            "description":"(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.",
            "allowedValues":[
               "Install",
               "Scan"
            ],
            "default":"Install"
         }
      },
      "mainSteps":[
         {
            "name":"getInstanceState",
            "action":"aws:executeAwsApi",
            "onFailure":"Abort",
            "inputs":{
               "inputs":null,
               "Service":"ec2",
               "Api":"DescribeInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ]
            },
            "outputs":[
               {
                  "Name":"instanceState",
                  "Selector":"$.Reservations[0].Instances[0].State.Name",
                  "Type":"String"
               }
            ],
            "nextStep":"branchOnInstanceState"
         },
         {
            "name":"branchOnInstanceState",
            "action":"aws:branch",
            "onFailure":"Abort",
            "inputs":{
               "Choices":[
                  {
                     "NextStep":"startInstance",
                     "Variable":"{{getInstanceState.instanceState}}",
                     "StringEquals":"stopped"
                  },
                  {
                     "Or":[
                        {
                           "Variable":"{{getInstanceState.instanceState}}",
                           "StringEquals":"stopping"
                        }
                     ],
                     "NextStep":"verifyInstanceStopped"
                  },
                  {
                     "NextStep":"patchInstance",
                     "Variable":"{{getInstanceState.instanceState}}",
                     "StringEquals":"running"
                  }
               ]
            },
            "isEnd":true
         },
         {
            "name":"startInstance",
            "action":"aws:executeAwsApi",
            "onFailure":"Abort",
            "inputs":{
               "Service":"ec2",
               "Api":"StartInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ]
            },
            "nextStep":"verifyInstanceRunning"
         },
         {
            "name":"verifyInstanceRunning",
            "action":"aws:waitForAwsResourceProperty",
            "timeoutSeconds":120,
            "inputs":{
               "Service":"ec2",
               "Api":"DescribeInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ],
               "PropertySelector":"$.Reservations[0].Instances[0].State.Name",
               "DesiredValues":[
                  "running"
               ]
            },
            "nextStep":"patchInstance"
         },
         {
            "name":"verifyInstanceStopped",
            "action":"aws:waitForAwsResourceProperty",
            "timeoutSeconds":120,
            "inputs":{
               "Service":"ec2",
               "Api":"DescribeInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ],
               "PropertySelector":"$.Reservations[0].Instances[0].State.Name",
               "DesiredValues":[
                  "stopped"
               ],
               "nextStep":"startInstance"
            }
         },
         {
            "name":"patchInstance",
            "action":"aws:runCommand",
            "onFailure":"Abort",
            "timeoutSeconds":5400,
            "inputs":{
               "DocumentName":"AWS-RunPatchBaseline",
               "InstanceIds":[
                  "{{InstanceId}}"
               ],
               "Parameters":{
                  "SnapshotId":"{{SnapshotId}}",
                  "RebootOption":"{{RebootOption}}",
                  "Operation":"{{Operation}}"
               }
            }
         },
         {
            "name":"branchOnOriginalInstanceState",
            "action":"aws:branch",
            "onFailure":"Abort",
            "inputs":{
               "Choices":[
                  {
                     "NextStep":"stopInstance",
                     "Not":{
                        "Variable":"{{getInstanceState.instanceState}}",
                        "StringEquals":"running"
                     }
                  }
               ]
            },
            "isEnd":true
         },
         {
            "name":"stopInstance",
            "action":"aws:executeAwsApi",
            "onFailure":"Abort",
            "inputs":{
               "Service":"ec2",
               "Api":"StopInstances",
               "InstanceIds":[
                  "{{InstanceId}}"
               ]
            }
         }
      ]
   }
   ```

------

有关此示例中使用的自动化操作的更多信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。

## 创建父运行手册
<a name="automation-authoring-runbooks-parent-runbook"></a>

此示例运行手册继续了上一节介绍的场景。现在 Emily 已经创建了子运行手册，她开始为父运行手册创作内容，如下所示：

1. 首先，她为运行手册的架构和描述提供值，并定义父运行手册的输入参数。

   通过使用 `AutomationAssumeRole` 参数，Emily 和她的同事可以使用现有的 IAM 角色，使自动化代表他们执行运行手册中的操作。Emily 使用 `PatchGroupPrimaryKey` 和 `PatchGroupPrimaryValue` 参数来指定与将要修补的数据库实例的主组关联的标签。她使用 `PatchGroupSecondaryKey` 和 `PatchGroupSecondaryValue` 参数来指定与将要修补的数据库实例的辅助组关联的标签。

------
#### [ YAML ]

   ```
   description: 'An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.'
   schemaVersion: '0.3'
   assumeRole: '{{AutomationAssumeRole}}'
   parameters:
     AutomationAssumeRole:
       type: String
       description: '(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.'
       default: ''
     PatchGroupPrimaryKey:
       type: String
       description: '(Required) The key of the tag for the primary group of instances you want to patch.''
     PatchGroupPrimaryValue:
       type: String
       description: '(Required) The value of the tag for the primary group of instances you want to patch.'
     PatchGroupSecondaryKey:
       type: String
       description: '(Required) The key of the tag for the secondary group of instances you want to patch.'
     PatchGroupSecondaryValue:
       type: String
       description: '(Required) The value of the tag for the secondary group of instances you want to patch.'
   ```

------
#### [ JSON ]

   ```
   {
      "schemaVersion": "0.3",
      "description": "An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.",
      "assumeRole": "{{AutomationAssumeRole}}",
      "parameters": {
         "AutomationAssumeRole": {
            "type": "String",
            "description": "(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.",
            "default": ""
         },
         "PatchGroupPrimaryKey": {
            "type": "String",
            "description": "(Required) The key of the tag for the primary group of instances you want to patch."
         },
         "PatchGroupPrimaryValue": {
            "type": "String",
            "description": "(Required) The value of the tag for the primary group of instances you want to patch."
         },
         "PatchGroupSecondaryKey": {
            "type": "String",
            "description": "(Required) The key of the tag for the secondary group of instances you want to patch."
         },
         "PatchGroupSecondaryValue": {
            "type": "String",
            "description": "(Required) The value of the tag for the secondary group of instances you want to patch."
         }
      }
   },
   ```

------

1. 定义顶层元素后，Emily 继续创作构成运行手册的 `mainSteps` 的操作。

   第一个操作使用她刚刚创建的子运行手册启动速率控制自动化，该子运行手册将与在 `PatchGroupPrimaryKey` 和 `PatchGroupPrimaryValue` 输入参数中所指定标签关联的实例设置为目标。她使用提供给输入参数的值来指定与要修补的主数据库实例组相关联的标签的键和值。

   第一个自动化完成后，第二个操作将使用子运行手册启动另一个速率控制自动化，该子运行手册将与在 `PatchGroupSecondaryKey` 和 `PatchGroupSecondaryValue` 输入参数中所指定标签关联的实例设置为目标。她使用提供给输入参数的值来指定与要修补的辅助数据库实例组相关联的标签的键和值。

------
#### [ YAML ]

   ```
   mainSteps:
     - name: patchPrimaryTargets
       action: 'aws:executeAutomation'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: RunbookTutorialChildAutomation
         Targets:
           - Key: 'tag:{{PatchGroupPrimaryKey}}'
             Values:
               - '{{PatchGroupPrimaryValue}}'
         TargetParameterName: 'InstanceId'
     - name: patchSecondaryTargets
       action: 'aws:executeAutomation'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: RunbookTutorialChildAutomation
         Targets:
           - Key: 'tag:{{PatchGroupSecondaryKey}}'
             Values:
               - '{{PatchGroupSecondaryValue}}'
         TargetParameterName: 'InstanceId'
   ```

------
#### [ JSON ]

   ```
   "mainSteps":[
         {
            "name":"patchPrimaryTargets",
            "action":"aws:executeAutomation",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"RunbookTutorialChildAutomation",
               "Targets":[
                  {
                     "Key":"tag:{{PatchGroupPrimaryKey}}",
                     "Values":[
                        "{{PatchGroupPrimaryValue}}"
                     ]
                  }
               ],
               "TargetParameterName":"InstanceId"
            }
         },
         {
            "name":"patchSecondaryTargets",
            "action":"aws:executeAutomation",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"RunbookTutorialChildAutomation",
               "Targets":[
                  {
                     "Key":"tag:{{PatchGroupSecondaryKey}}",
                     "Values":[
                        "{{PatchGroupSecondaryValue}}"
                     ]
                  }
               ],
               "TargetParameterName":"InstanceId"
            }
         }
      ]
   }
   ```

------

1. Emily 检查完成的父运行手册内容，并在相同的 AWS 账户和 AWS 区域中创建运行手册作为目标实例。现在，她已经准备好测试自己的运行手册，以确保在将自动化实施到生产环境之前，自动化能够按需运行。下面是完成的父运行手册内容。

------
#### [ YAML ]

   ```
   description: An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.
   schemaVersion: '0.3'
   assumeRole: '{{AutomationAssumeRole}}'
   parameters:
     AutomationAssumeRole:
       type: String
       description: '(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.'
       default: ''
     PatchGroupPrimaryKey:
       type: String
       description: (Required) The key of the tag for the primary group of instances you want to patch.
     PatchGroupPrimaryValue:
       type: String
       description: '(Required) The value of the tag for the primary group of instances you want to patch. '
     PatchGroupSecondaryKey:
       type: String
       description: (Required) The key of the tag for the secondary group of instances you want to patch.
     PatchGroupSecondaryValue:
       type: String
       description: '(Required) The value of the tag for the secondary group of instances you want to patch.  '
   mainSteps:
     - name: patchPrimaryTargets
       action: 'aws:executeAutomation'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: RunbookTutorialChildAutomation
         Targets:
           - Key: 'tag:{{PatchGroupPrimaryKey}}'
             Values:
               - '{{PatchGroupPrimaryValue}}'
         TargetParameterName: 'InstanceId'
     - name: patchSecondaryTargets
       action: 'aws:executeAutomation'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: RunbookTutorialChildAutomation
         Targets:
           - Key: 'tag:{{PatchGroupSecondaryKey}}'
             Values:
               - '{{PatchGroupSecondaryValue}}'
         TargetParameterName: 'InstanceId'
   ```

------
#### [ JSON ]

   ```
   {
      "description":"An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.",
      "schemaVersion":"0.3",
      "assumeRole":"{{AutomationAssumeRole}}",
      "parameters":{
         "AutomationAssumeRole":{
            "type":"String",
            "description":"(Optional) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.",
            "default":""
         },
         "PatchGroupPrimaryKey":{
            "type":"String",
            "description":"(Required) The key of the tag for the primary group of instances you want to patch."
         },
         "PatchGroupPrimaryValue":{
            "type":"String",
            "description":"(Required) The value of the tag for the primary group of instances you want to patch. "
         },
         "PatchGroupSecondaryKey":{
            "type":"String",
            "description":"(Required) The key of the tag for the secondary group of instances you want to patch."
         },
         "PatchGroupSecondaryValue":{
            "type":"String",
            "description":"(Required) The value of the tag for the secondary group of instances you want to patch.  "
         }
      },
      "mainSteps":[
         {
            "name":"patchPrimaryTargets",
            "action":"aws:executeAutomation",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"RunbookTutorialChildAutomation",
               "Targets":[
                  {
                     "Key":"tag:{{PatchGroupPrimaryKey}}",
                     "Values":[
                        "{{PatchGroupPrimaryValue}}"
                     ]
                  }
               ],
               "TargetParameterName":"InstanceId"
            }
         },
         {
            "name":"patchSecondaryTargets",
            "action":"aws:executeAutomation",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"RunbookTutorialChildAutomation",
               "Targets":[
                  {
                     "Key":"tag:{{PatchGroupSecondaryKey}}",
                     "Values":[
                        "{{PatchGroupSecondaryValue}}"
                     ]
                  }
               ],
               "TargetParameterName":"InstanceId"
            }
         }
      ]
   }
   ```

------

有关此示例中使用的自动化操作的更多信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。

# 示例 2：脚本化运行手册
<a name="automation-authoring-runbooks-scripted-example"></a>

此示例运行手册解决以下情形。Emily 是 AnyCompany Consultants, LLC 的系统工程师。她之前创建了两个运行手册，这两个运行手册用于父子关系，以修补托管主数据库和辅助数据库的 Amazon Elastic Compute Cloud (Amazon EC2) 实例的组。应用程序每天 24 小时访问这些数据库，因此这两个数据库实例中必须有一个始终可用。

基于此要求，她构建了一个解决方案，使用 `AWS-RunPatchBaseline` Systems Manager (SSM) 文档分阶段修补实例。通过使用此 SSM 文档，她的同事可以在修补操作完成后查看相关的修补程序合规性信息。

首先修补数据库实例的主组，然后修补数据库实例的辅助组。此外，为了避免由于运行之前已停止的实例而产生额外的成本，Emily 确保自动化将修补的实例恢复到修补之前的原始状态。Emily 使用与数据库实例的主组和辅助组关联的标签来确定应按照所需顺序修补哪些实例。

她现有的自动化解决方案有效，但她希望尽可能改进自己的解决方案。为了帮助维护运行手册内容并简化故障排除工作，她希望将自动化压缩到一个运行手册中，并简化输入参数的数量。此外，她希望避免创建多个子自动化。

在审查了可用的自动化操作后，Emily 确定可以使用 `aws:executeScript` 操作来运行她的自定义 Python 脚本，从而改进她的解决方案。她现在开始创作运行手册的内容，如下所示：

1. 首先，她为运行手册的架构和描述提供值，并定义父运行手册的输入参数。

   通过使用 `AutomationAssumeRole` 参数，Emily 和她的同事可以使用现有的 IAM 角色，使自动化代表他们执行运行手册中的操作。与[示例 1](automation-authoring-runbooks-parent-child-example.md) 不同的是，`AutomationAssumeRole` 参数现在是必需的，而不是可选的。因为这个运行手册包括 `aws:executeScript` 操作，所以始终需要一个 AWS Identity and Access Management (IAM) 服务角色（或担任角色）。此要求是必要的，因为某些为操作指定的 Python 脚本调用 AWS API 操作。

   Emily 使用 `PrimaryPatchGroupTag` 和 `SecondaryPatchGroupTag` 参数指定与要修补的数据库实例的主要和辅助组关联的标签。为了简化所需的输入参数，她决定使用 `StringMap` 参数，而不是使用多个 `String`参数，因为她在示例 1 运行手册中使用了后者。（可选）`Operation`、`RebootOption` 和 `SnapshotId` 参数可用于为 `AWS-RunPatchBaseline` 的文档参数提供值。为了防止向这些运行手册参数提供无效值，她根据需要定义了 `allowedValues`。

------
#### [ YAML ]

   ```
   description: 'An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.'
   schemaVersion: '0.3'
   assumeRole: '{{AutomationAssumeRole}}'
   parameters:
     AutomationAssumeRole:
       type: String
       description: '(Required) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.'
     PrimaryPatchGroupTag:
       type: StringMap
       description: '(Required) The tag for the primary group of instances you want to patch. Specify a key-value pair. Example: {"key" : "value"}'
     SecondaryPatchGroupTag:
       type: StringMap
       description: '(Required) The tag for the secondary group of instances you want to patch. Specify a key-value pair. Example: {"key" : "value"}'
     SnapshotId:
       type: String
       description: '(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.'
       default: ''
     RebootOption:
       type: String
       description: '(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.'
       allowedValues:
         - NoReboot
         - RebootIfNeeded
       default: RebootIfNeeded
     Operation:
       type: String
       description: '(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.'
       allowedValues:
         - Install
         - Scan
       default: Install
   ```

------
#### [ JSON ]

   ```
   {
      "description":"An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.",
      "schemaVersion":"0.3",
      "assumeRole":"{{AutomationAssumeRole}}",
      "parameters":{
         "AutomationAssumeRole":{
            "type":"String",
            "description":"(Required) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook."
         },
         "PrimaryPatchGroupTag":{
            "type":"StringMap",
            "description":"(Required) The tag for the primary group of instances you want to patch. Specify a key-value pair. Example: {\"key\" : \"value\"}"
         },
         "SecondaryPatchGroupTag":{
            "type":"StringMap",
            "description":"(Required) The tag for the secondary group of instances you want to patch. Specify a key-value pair. Example: {\"key\" : \"value\"}"
         },
         "SnapshotId":{
            "type":"String",
            "description":"(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.",
            "default":""
         },
         "RebootOption":{
            "type":"String",
            "description":"(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.",
            "allowedValues":[
               "NoReboot",
               "RebootIfNeeded"
            ],
            "default":"RebootIfNeeded"
         },
         "Operation":{
            "type":"String",
            "description":"(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.",
            "allowedValues":[
               "Install",
               "Scan"
            ],
            "default":"Install"
         }
      }
   },
   ```

------

1. 定义顶层元素后，Emily 继续创作构成运行手册的 `mainSteps` 的操作。第一步收集与 `PrimaryPatchGroupTag` 参数中指定的标签关联的所有实例的 ID，并输出 `StringMap` 参数，其中包含实例 ID 和实例的当前状态。此操作的输出将用于后续操作。

   请注意，`script` 输入参数不支持 JSON 运行手册。JSON 运行手册必须使用 `attachment` 输入参数提供脚本内容。

------
#### [ YAML ]

   ```
   mainSteps:
     - name: getPrimaryInstanceState
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: getInstanceStates
         InputPayload:
           primaryTag: '{{PrimaryPatchGroupTag}}'
         Script: |-
           def getInstanceStates(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             tag = events['primaryTag']
             tagKey, tagValue = list(tag.items())[0]
             instanceQuery = ec2.describe_instances(
             Filters=[
                 {
                     "Name": "tag:" + tagKey,
                     "Values": [tagValue]
                 }]
             )
             if not instanceQuery['Reservations']:
                 noInstancesForTagString = "No instances found for specified tag."
                 return({ 'noInstancesFound' : noInstancesForTagString })
             else:
                 queryResponse = instanceQuery['Reservations']
                 originalInstanceStates = {}
                 for results in queryResponse:
                     instanceSet = results['Instances']
                     for instance in instanceSet:
                         instanceId = instance['InstanceId']
                         originalInstanceStates[instanceId] = instance['State']['Name']
                 return originalInstanceStates
       outputs:
         - Name: originalInstanceStates
           Selector: $.Payload
           Type: StringMap
       nextStep: verifyPrimaryInstancesRunning
   ```

------
#### [ JSON ]

   ```
   "mainSteps":[
         {
            "name":"getPrimaryInstanceState",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"getInstanceStates",
               "InputPayload":{
                  "primaryTag":"{{PrimaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"originalInstanceStates",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               }
            ],
            "nextStep":"verifyPrimaryInstancesRunning"
         },
   ```

------

1. Emily 将上一个操作的输出用于另一个 `aws:executeScript` 操作，以验证与 `PrimaryPatchGroupTag` 参数中所指定标签相关联的所有实例均处于 `running` 状态。

   如果实例状态已处于 `running` 或者 `shutting-down` 状态，则脚本将继续遍历剩余的实例。

   如果实例的状态为 `stopping`，则脚本轮询实例以达到 `stopped` 状态并启动实例。

   如果实例的状态为 `stopped`，脚本将启动实例。

------
#### [ YAML ]

   ```
   - name: verifyPrimaryInstancesRunning
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: verifyInstancesRunning
         InputPayload:
           targetInstances: '{{getPrimaryInstanceState.originalInstanceStates}}'
         Script: |-
           def verifyInstancesRunning(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped':
                   print("The target instance " + instance + " is stopped. The instance will now be started.")
                   ec2.start_instances(
                       InstanceIds=[instance]
                       )
               elif instanceDict[instance] == 'stopping':
                   print("The target instance " + instance + " is stopping. Polling for instance to reach stopped state.")
                   while instanceDict[instance] != 'stopped':
                       poll = ec2.get_waiter('instance_stopped')
                       poll.wait(
                           InstanceIds=[instance]
                       )
                   ec2.start_instances(
                       InstanceIds=[instance]
                   )
               else:
                 pass
       nextStep: waitForPrimaryRunningInstances
   ```

------
#### [ JSON ]

   ```
   {
            "name":"verifyPrimaryInstancesRunning",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"verifyInstancesRunning",
               "InputPayload":{
                  "targetInstances":"{{getPrimaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"waitForPrimaryRunningInstances"
         },
   ```

------

1. Emily 验证与 `PrimaryPatchGroupTag` 参数中指定的标签相关联的所有实例已启动或已处于 `running` 状态。然后，她使用另一个脚本来验证所有实例（包括在上一操作中启动的实例）已处于 `running` 状态。

------
#### [ YAML ]

   ```
   - name: waitForPrimaryRunningInstances
       action: 'aws:executeScript'
       timeoutSeconds: 300
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: waitForRunningInstances
         InputPayload:
           targetInstances: '{{getPrimaryInstanceState.originalInstanceStates}}'
         Script: |-
           def waitForRunningInstances(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
                 poll = ec2.get_waiter('instance_running')
                 poll.wait(
                     InstanceIds=[instance]
                 )
       nextStep: returnPrimaryTagKey
   ```

------
#### [ JSON ]

   ```
   {
            "name":"waitForPrimaryRunningInstances",
            "action":"aws:executeScript",
            "timeoutSeconds":300,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"waitForRunningInstances",
               "InputPayload":{
                  "targetInstances":"{{getPrimaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"returnPrimaryTagKey"
         },
   ```

------

1. Emily 使用两个脚本来返回键的单个 `String` 值和 `PrimaryPatchGroupTag` 参数中指定的标签的值。这些操作返回的值允许她直接向 `Targets` 文档的参数 `AWS-RunPatchBaseline` 提供值。然后，自动化将继续使用 `aws:runCommand` 操作，利用 `AWS-RunPatchBaseline` 文档修补实例。

------
#### [ YAML ]

   ```
   - name: returnPrimaryTagKey
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           primaryTag: '{{PrimaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['primaryTag']
             tagKey = list(tag)[0]
             stringKey = "tag:" + tagKey
             return {'tagKey' : stringKey}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: primaryPatchGroupKey
           Selector: $.Payload.tagKey
           Type: String
       nextStep: returnPrimaryTagValue
     - name: returnPrimaryTagValue
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           primaryTag: '{{PrimaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['primaryTag']
             tagKey = list(tag)[0]
             tagValue = tag[tagKey]
             return {'tagValue' : tagValue}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: primaryPatchGroupValue
           Selector: $.Payload.tagValue
           Type: String
       nextStep: patchPrimaryInstances
     - name: patchPrimaryInstances
       action: 'aws:runCommand'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: AWS-RunPatchBaseline
         Parameters:
           SnapshotId: '{{SnapshotId}}'
           RebootOption: '{{RebootOption}}'
           Operation: '{{Operation}}'
         Targets:
           - Key: '{{returnPrimaryTagKey.primaryPatchGroupKey}}'
             Values:
               - '{{returnPrimaryTagValue.primaryPatchGroupValue}}'
         MaxConcurrency: 10%
         MaxErrors: 10%
       nextStep: returnPrimaryToOriginalState
   ```

------
#### [ JSON ]

   ```
   {
            "name":"returnPrimaryTagKey",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "primaryTag":"{{PrimaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"primaryPatchGroupKey",
                  "Selector":"$.Payload.tagKey",
                  "Type":"String"
               }
            ],
            "nextStep":"returnPrimaryTagValue"
         },
         {
            "name":"returnPrimaryTagValue",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "primaryTag":"{{PrimaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"primaryPatchGroupValue",
                  "Selector":"$.Payload.tagValue",
                  "Type":"String"
               }
            ],
            "nextStep":"patchPrimaryInstances"
         },
         {
            "name":"patchPrimaryInstances",
            "action":"aws:runCommand",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"AWS-RunPatchBaseline",
               "Parameters":{
                  "SnapshotId":"{{SnapshotId}}",
                  "RebootOption":"{{RebootOption}}",
                  "Operation":"{{Operation}}"
               },
               "Targets":[
                  {
                     "Key":"{{returnPrimaryTagKey.primaryPatchGroupKey}}",
                     "Values":[
                        "{{returnPrimaryTagValue.primaryPatchGroupValue}}"
                     ]
                  }
               ],
               "MaxConcurrency":"10%",
               "MaxErrors":"10%"
            },
            "nextStep":"returnPrimaryToOriginalState"
         },
   ```

------

1. 修补操作完成后，Emily 希望自动化将与 `PrimaryPatchGroupTag` 参数中所指定标签相关联的目标实例返回到自动化开始之前的状态。她通过再次使用脚本中第一个操作的输出来完成此操作。根据目标实例的原始状态，如果该实例以前处于 `running` 以外的任何状态，则实例将停止。否则，如果实例状态为 `running`，则脚本将继续遍历剩余的实例。

------
#### [ YAML ]

   ```
   - name: returnPrimaryToOriginalState
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnToOriginalState
         InputPayload:
           targetInstances: '{{getPrimaryInstanceState.originalInstanceStates}}'
         Script: |-
           def returnToOriginalState(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped' or instanceDict[instance] == 'stopping':
                   ec2.stop_instances(
                       InstanceIds=[instance]
                       )
               else:
                 pass
       nextStep: getSecondaryInstanceState
   ```

------
#### [ JSON ]

   ```
   {
            "name":"returnPrimaryToOriginalState",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnToOriginalState",
               "InputPayload":{
                  "targetInstances":"{{getPrimaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"getSecondaryInstanceState"
         },
   ```

------

1. 针对与 `PrimaryPatchGroupTag` 参数中所指定标签相关联的实例的修补操作已完成。现在 Emily 复制了运行手册内容中以前的所有操作，将与 `SecondaryPatchGroupTag` 参数中所指定标签相关联的实例设置为目标。

------
#### [ YAML ]

   ```
   - name: getSecondaryInstanceState
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: getInstanceStates
         InputPayload:
           secondaryTag: '{{SecondaryPatchGroupTag}}'
         Script: |-
           def getInstanceStates(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             tag = events['secondaryTag']
             tagKey, tagValue = list(tag.items())[0]
             instanceQuery = ec2.describe_instances(
             Filters=[
                 {
                     "Name": "tag:" + tagKey,
                     "Values": [tagValue]
                 }]
             )
             if not instanceQuery['Reservations']:
                 noInstancesForTagString = "No instances found for specified tag."
                 return({ 'noInstancesFound' : noInstancesForTagString })
             else:
                 queryResponse = instanceQuery['Reservations']
                 originalInstanceStates = {}
                 for results in queryResponse:
                     instanceSet = results['Instances']
                     for instance in instanceSet:
                         instanceId = instance['InstanceId']
                         originalInstanceStates[instanceId] = instance['State']['Name']
                 return originalInstanceStates
       outputs:
         - Name: originalInstanceStates
           Selector: $.Payload
           Type: StringMap
       nextStep: verifySecondaryInstancesRunning
     - name: verifySecondaryInstancesRunning
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: verifyInstancesRunning
         InputPayload:
           targetInstances: '{{getSecondaryInstanceState.originalInstanceStates}}'
         Script: |-
           def verifyInstancesRunning(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped':
                   print("The target instance " + instance + " is stopped. The instance will now be started.")
                   ec2.start_instances(
                       InstanceIds=[instance]
                       )
               elif instanceDict[instance] == 'stopping':
                   print("The target instance " + instance + " is stopping. Polling for instance to reach stopped state.")
                   while instanceDict[instance] != 'stopped':
                       poll = ec2.get_waiter('instance_stopped')
                       poll.wait(
                           InstanceIds=[instance]
                       )
                   ec2.start_instances(
                       InstanceIds=[instance]
                   )
               else:
                 pass
       nextStep: waitForSecondaryRunningInstances
     - name: waitForSecondaryRunningInstances
       action: 'aws:executeScript'
       timeoutSeconds: 300
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: waitForRunningInstances
         InputPayload:
           targetInstances: '{{getSecondaryInstanceState.originalInstanceStates}}'
         Script: |-
           def waitForRunningInstances(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
                 poll = ec2.get_waiter('instance_running')
                 poll.wait(
                     InstanceIds=[instance]
                 )
       nextStep: returnSecondaryTagKey
     - name: returnSecondaryTagKey
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           secondaryTag: '{{SecondaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['secondaryTag']
             tagKey = list(tag)[0]
             stringKey = "tag:" + tagKey
             return {'tagKey' : stringKey}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: secondaryPatchGroupKey
           Selector: $.Payload.tagKey
           Type: String
       nextStep: returnSecondaryTagValue
     - name: returnSecondaryTagValue
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           secondaryTag: '{{SecondaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['secondaryTag']
             tagKey = list(tag)[0]
             tagValue = tag[tagKey]
             return {'tagValue' : tagValue}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: secondaryPatchGroupValue
           Selector: $.Payload.tagValue
           Type: String
       nextStep: patchSecondaryInstances
     - name: patchSecondaryInstances
       action: 'aws:runCommand'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: AWS-RunPatchBaseline
         Parameters:
           SnapshotId: '{{SnapshotId}}'
           RebootOption: '{{RebootOption}}'
           Operation: '{{Operation}}'
         Targets:
           - Key: '{{returnSecondaryTagKey.secondaryPatchGroupKey}}'
             Values:
             - '{{returnSecondaryTagValue.secondaryPatchGroupValue}}'
         MaxConcurrency: 10%
         MaxErrors: 10%
       nextStep: returnSecondaryToOriginalState
     - name: returnSecondaryToOriginalState
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnToOriginalState
         InputPayload:
           targetInstances: '{{getSecondaryInstanceState.originalInstanceStates}}'
         Script: |-
           def returnToOriginalState(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped' or instanceDict[instance] == 'stopping':
                   ec2.stop_instances(
                       InstanceIds=[instance]
                       )
               else:
                 pass
   ```

------
#### [ JSON ]

   ```
   {
            "name":"getSecondaryInstanceState",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"getInstanceStates",
               "InputPayload":{
                  "secondaryTag":"{{SecondaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"originalInstanceStates",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               }
            ],
            "nextStep":"verifySecondaryInstancesRunning"
         },
         {
            "name":"verifySecondaryInstancesRunning",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"verifyInstancesRunning",
               "InputPayload":{
                  "targetInstances":"{{getSecondaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"waitForSecondaryRunningInstances"
         },
         {
            "name":"waitForSecondaryRunningInstances",
            "action":"aws:executeScript",
            "timeoutSeconds":300,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"waitForRunningInstances",
               "InputPayload":{
                  "targetInstances":"{{getSecondaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"returnSecondaryTagKey"
         },
         {
            "name":"returnSecondaryTagKey",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "secondaryTag":"{{SecondaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"secondaryPatchGroupKey",
                  "Selector":"$.Payload.tagKey",
                  "Type":"String"
               }
            ],
            "nextStep":"returnSecondaryTagValue"
         },
         {
            "name":"returnSecondaryTagValue",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "secondaryTag":"{{SecondaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"secondaryPatchGroupValue",
                  "Selector":"$.Payload.tagValue",
                  "Type":"String"
               }
            ],
            "nextStep":"patchSecondaryInstances"
         },
         {
            "name":"patchSecondaryInstances",
            "action":"aws:runCommand",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"AWS-RunPatchBaseline",
               "Parameters":{
                  "SnapshotId":"{{SnapshotId}}",
                  "RebootOption":"{{RebootOption}}",
                  "Operation":"{{Operation}}"
               },
               "Targets":[
                  {
                     "Key":"{{returnSecondaryTagKey.secondaryPatchGroupKey}}",
                     "Values":[
                        "{{returnSecondaryTagValue.secondaryPatchGroupValue}}"
                     ]
                  }
               ],
               "MaxConcurrency":"10%",
               "MaxErrors":"10%"
            },
            "nextStep":"returnSecondaryToOriginalState"
         },
         {
            "name":"returnSecondaryToOriginalState",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnToOriginalState",
               "InputPayload":{
                  "targetInstances":"{{getSecondaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            }
         }
      ]
   }
   ```

------

1. Emily 查看已完成的脚本化运行手册内容，并在同一 AWS 账户和 AWS 区域中创建运行手册作为目标实例。现在，她已经准备好测试运行手册，以确保在将自动化实施到生产环境之前自动化能够按需运行。下面是完成的脚本化运行手册内容。

------
#### [ YAML ]

   ```
   description: An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.
   schemaVersion: '0.3'
   assumeRole: '{{AutomationAssumeRole}}'
   parameters:
     AutomationAssumeRole:
       type: String
       description: '(Required) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook.'
     PrimaryPatchGroupTag:
       type: StringMap
       description: '(Required) The tag for the primary group of instances you want to patch. Specify a key-value pair. Example: {"key" : "value"}'
     SecondaryPatchGroupTag:
       type: StringMap
       description: '(Required) The tag for the secondary group of instances you want to patch. Specify a key-value pair. Example: {"key" : "value"}'
     SnapshotId:
       type: String
       description: '(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.'
       default: ''
     RebootOption:
       type: String
       description: '(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.'
       allowedValues:
         - NoReboot
         - RebootIfNeeded
       default: RebootIfNeeded
     Operation:
       type: String
       description: '(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.'
       allowedValues:
         - Install
         - Scan
       default: Install
   mainSteps:
     - name: getPrimaryInstanceState
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: getInstanceStates
         InputPayload:
           primaryTag: '{{PrimaryPatchGroupTag}}'
         Script: |-
           def getInstanceStates(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             tag = events['primaryTag']
             tagKey, tagValue = list(tag.items())[0]
             instanceQuery = ec2.describe_instances(
             Filters=[
                 {
                     "Name": "tag:" + tagKey,
                     "Values": [tagValue]
                 }]
             )
             if not instanceQuery['Reservations']:
                 noInstancesForTagString = "No instances found for specified tag."
                 return({ 'noInstancesFound' : noInstancesForTagString })
             else:
                 queryResponse = instanceQuery['Reservations']
                 originalInstanceStates = {}
                 for results in queryResponse:
                     instanceSet = results['Instances']
                     for instance in instanceSet:
                         instanceId = instance['InstanceId']
                         originalInstanceStates[instanceId] = instance['State']['Name']
                 return originalInstanceStates
       outputs:
         - Name: originalInstanceStates
           Selector: $.Payload
           Type: StringMap
       nextStep: verifyPrimaryInstancesRunning
     - name: verifyPrimaryInstancesRunning
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: verifyInstancesRunning
         InputPayload:
           targetInstances: '{{getPrimaryInstanceState.originalInstanceStates}}'
         Script: |-
           def verifyInstancesRunning(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped':
                   print("The target instance " + instance + " is stopped. The instance will now be started.")
                   ec2.start_instances(
                       InstanceIds=[instance]
                       )
               elif instanceDict[instance] == 'stopping':
                   print("The target instance " + instance + " is stopping. Polling for instance to reach stopped state.")
                   while instanceDict[instance] != 'stopped':
                       poll = ec2.get_waiter('instance_stopped')
                       poll.wait(
                           InstanceIds=[instance]
                       )
                   ec2.start_instances(
                       InstanceIds=[instance]
                   )
               else:
                 pass
       nextStep: waitForPrimaryRunningInstances
     - name: waitForPrimaryRunningInstances
       action: 'aws:executeScript'
       timeoutSeconds: 300
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: waitForRunningInstances
         InputPayload:
           targetInstances: '{{getPrimaryInstanceState.originalInstanceStates}}'
         Script: |-
           def waitForRunningInstances(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
                 poll = ec2.get_waiter('instance_running')
                 poll.wait(
                     InstanceIds=[instance]
                 )
       nextStep: returnPrimaryTagKey
     - name: returnPrimaryTagKey
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           primaryTag: '{{PrimaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['primaryTag']
             tagKey = list(tag)[0]
             stringKey = "tag:" + tagKey
             return {'tagKey' : stringKey}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: primaryPatchGroupKey
           Selector: $.Payload.tagKey
           Type: String
       nextStep: returnPrimaryTagValue
     - name: returnPrimaryTagValue
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           primaryTag: '{{PrimaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['primaryTag']
             tagKey = list(tag)[0]
             tagValue = tag[tagKey]
             return {'tagValue' : tagValue}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: primaryPatchGroupValue
           Selector: $.Payload.tagValue
           Type: String
       nextStep: patchPrimaryInstances
     - name: patchPrimaryInstances
       action: 'aws:runCommand'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: AWS-RunPatchBaseline
         Parameters:
           SnapshotId: '{{SnapshotId}}'
           RebootOption: '{{RebootOption}}'
           Operation: '{{Operation}}'
         Targets:
           - Key: '{{returnPrimaryTagKey.primaryPatchGroupKey}}'
             Values:
               - '{{returnPrimaryTagValue.primaryPatchGroupValue}}'
         MaxConcurrency: 10%
         MaxErrors: 10%
       nextStep: returnPrimaryToOriginalState
     - name: returnPrimaryToOriginalState
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnToOriginalState
         InputPayload:
           targetInstances: '{{getPrimaryInstanceState.originalInstanceStates}}'
         Script: |-
           def returnToOriginalState(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped' or instanceDict[instance] == 'stopping':
                   ec2.stop_instances(
                       InstanceIds=[instance]
                       )
               else:
                 pass
       nextStep: getSecondaryInstanceState
     - name: getSecondaryInstanceState
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: getInstanceStates
         InputPayload:
           secondaryTag: '{{SecondaryPatchGroupTag}}'
         Script: |-
           def getInstanceStates(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             tag = events['secondaryTag']
             tagKey, tagValue = list(tag.items())[0]
             instanceQuery = ec2.describe_instances(
             Filters=[
                 {
                     "Name": "tag:" + tagKey,
                     "Values": [tagValue]
                 }]
             )
             if not instanceQuery['Reservations']:
                 noInstancesForTagString = "No instances found for specified tag."
                 return({ 'noInstancesFound' : noInstancesForTagString })
             else:
                 queryResponse = instanceQuery['Reservations']
                 originalInstanceStates = {}
                 for results in queryResponse:
                     instanceSet = results['Instances']
                     for instance in instanceSet:
                         instanceId = instance['InstanceId']
                         originalInstanceStates[instanceId] = instance['State']['Name']
                 return originalInstanceStates
       outputs:
         - Name: originalInstanceStates
           Selector: $.Payload
           Type: StringMap
       nextStep: verifySecondaryInstancesRunning
     - name: verifySecondaryInstancesRunning
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: verifyInstancesRunning
         InputPayload:
           targetInstances: '{{getSecondaryInstanceState.originalInstanceStates}}'
         Script: |-
           def verifyInstancesRunning(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped':
                   print("The target instance " + instance + " is stopped. The instance will now be started.")
                   ec2.start_instances(
                       InstanceIds=[instance]
                       )
               elif instanceDict[instance] == 'stopping':
                   print("The target instance " + instance + " is stopping. Polling for instance to reach stopped state.")
                   while instanceDict[instance] != 'stopped':
                       poll = ec2.get_waiter('instance_stopped')
                       poll.wait(
                           InstanceIds=[instance]
                       )
                   ec2.start_instances(
                       InstanceIds=[instance]
                   )
               else:
                 pass
       nextStep: waitForSecondaryRunningInstances
     - name: waitForSecondaryRunningInstances
       action: 'aws:executeScript'
       timeoutSeconds: 300
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: waitForRunningInstances
         InputPayload:
           targetInstances: '{{getSecondaryInstanceState.originalInstanceStates}}'
         Script: |-
           def waitForRunningInstances(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
                 poll = ec2.get_waiter('instance_running')
                 poll.wait(
                     InstanceIds=[instance]
                 )
       nextStep: returnSecondaryTagKey
     - name: returnSecondaryTagKey
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           secondaryTag: '{{SecondaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['secondaryTag']
             tagKey = list(tag)[0]
             stringKey = "tag:" + tagKey
             return {'tagKey' : stringKey}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: secondaryPatchGroupKey
           Selector: $.Payload.tagKey
           Type: String
       nextStep: returnSecondaryTagValue
     - name: returnSecondaryTagValue
       action: 'aws:executeScript'
       timeoutSeconds: 120
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnTagValues
         InputPayload:
           secondaryTag: '{{SecondaryPatchGroupTag}}'
         Script: |-
           def returnTagValues(events,context):
             tag = events['secondaryTag']
             tagKey = list(tag)[0]
             tagValue = tag[tagKey]
             return {'tagValue' : tagValue}
       outputs:
         - Name: Payload
           Selector: $.Payload
           Type: StringMap
         - Name: secondaryPatchGroupValue
           Selector: $.Payload.tagValue
           Type: String
       nextStep: patchSecondaryInstances
     - name: patchSecondaryInstances
       action: 'aws:runCommand'
       onFailure: Abort
       timeoutSeconds: 7200
       inputs:
         DocumentName: AWS-RunPatchBaseline
         Parameters:
           SnapshotId: '{{SnapshotId}}'
           RebootOption: '{{RebootOption}}'
           Operation: '{{Operation}}'
         Targets:
           - Key: '{{returnSecondaryTagKey.secondaryPatchGroupKey}}'
             Values:
             - '{{returnSecondaryTagValue.secondaryPatchGroupValue}}'
         MaxConcurrency: 10%
         MaxErrors: 10%
       nextStep: returnSecondaryToOriginalState
     - name: returnSecondaryToOriginalState
       action: 'aws:executeScript'
       timeoutSeconds: 600
       onFailure: Abort
       inputs:
         Runtime: python3.11
         Handler: returnToOriginalState
         InputPayload:
           targetInstances: '{{getSecondaryInstanceState.originalInstanceStates}}'
         Script: |-
           def returnToOriginalState(events,context):
             import boto3
   
             #Initialize client
             ec2 = boto3.client('ec2')
             instanceDict = events['targetInstances']
             for instance in instanceDict:
               if instanceDict[instance] == 'stopped' or instanceDict[instance] == 'stopping':
                   ec2.stop_instances(
                       InstanceIds=[instance]
                       )
               else:
                 pass
   ```

------
#### [ JSON ]

   ```
   {
      "description":"An example of an Automation runbook that patches groups of Amazon EC2 instances in stages.",
      "schemaVersion":"0.3",
      "assumeRole":"{{AutomationAssumeRole}}",
      "parameters":{
         "AutomationAssumeRole":{
            "type":"String",
            "description":"(Required) The Amazon Resource Name (ARN) of the IAM role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to operate this runbook."
         },
         "PrimaryPatchGroupTag":{
            "type":"StringMap",
            "description":"(Required) The tag for the primary group of instances you want to patch. Specify a key-value pair. Example: {\"key\" : \"value\"}"
         },
         "SecondaryPatchGroupTag":{
            "type":"StringMap",
            "description":"(Required) The tag for the secondary group of instances you want to patch. Specify a key-value pair. Example: {\"key\" : \"value\"}"
         },
         "SnapshotId":{
            "type":"String",
            "description":"(Optional) The snapshot ID to use to retrieve a patch baseline snapshot.",
            "default":""
         },
         "RebootOption":{
            "type":"String",
            "description":"(Optional) Reboot behavior after a patch Install operation. If you choose NoReboot and patches are installed, the instance is marked as non-compliant until a subsequent reboot and scan.",
            "allowedValues":[
               "NoReboot",
               "RebootIfNeeded"
            ],
            "default":"RebootIfNeeded"
         },
         "Operation":{
            "type":"String",
            "description":"(Optional) The update or configuration to perform on the instance. The system checks if patches specified in the patch baseline are installed on the instance. The install operation installs patches missing from the baseline.",
            "allowedValues":[
               "Install",
               "Scan"
            ],
            "default":"Install"
         }
      },
      "mainSteps":[
         {
            "name":"getPrimaryInstanceState",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"getInstanceStates",
               "InputPayload":{
                  "primaryTag":"{{PrimaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"originalInstanceStates",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               }
            ],
            "nextStep":"verifyPrimaryInstancesRunning"
         },
         {
            "name":"verifyPrimaryInstancesRunning",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"verifyInstancesRunning",
               "InputPayload":{
                  "targetInstances":"{{getPrimaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"waitForPrimaryRunningInstances"
         },
         {
            "name":"waitForPrimaryRunningInstances",
            "action":"aws:executeScript",
            "timeoutSeconds":300,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"waitForRunningInstances",
               "InputPayload":{
                  "targetInstances":"{{getPrimaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"returnPrimaryTagKey"
         },
         {
            "name":"returnPrimaryTagKey",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "primaryTag":"{{PrimaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"primaryPatchGroupKey",
                  "Selector":"$.Payload.tagKey",
                  "Type":"String"
               }
            ],
            "nextStep":"returnPrimaryTagValue"
         },
         {
            "name":"returnPrimaryTagValue",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "primaryTag":"{{PrimaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"primaryPatchGroupValue",
                  "Selector":"$.Payload.tagValue",
                  "Type":"String"
               }
            ],
            "nextStep":"patchPrimaryInstances"
         },
         {
            "name":"patchPrimaryInstances",
            "action":"aws:runCommand",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"AWS-RunPatchBaseline",
               "Parameters":{
                  "SnapshotId":"{{SnapshotId}}",
                  "RebootOption":"{{RebootOption}}",
                  "Operation":"{{Operation}}"
               },
               "Targets":[
                  {
                     "Key":"{{returnPrimaryTagKey.primaryPatchGroupKey}}",
                     "Values":[
                        "{{returnPrimaryTagValue.primaryPatchGroupValue}}"
                     ]
                  }
               ],
               "MaxConcurrency":"10%",
               "MaxErrors":"10%"
            },
            "nextStep":"returnPrimaryToOriginalState"
         },
         {
            "name":"returnPrimaryToOriginalState",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnToOriginalState",
               "InputPayload":{
                  "targetInstances":"{{getPrimaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"getSecondaryInstanceState"
         },
         {
            "name":"getSecondaryInstanceState",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"getInstanceStates",
               "InputPayload":{
                  "secondaryTag":"{{SecondaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"originalInstanceStates",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               }
            ],
            "nextStep":"verifySecondaryInstancesRunning"
         },
         {
            "name":"verifySecondaryInstancesRunning",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"verifyInstancesRunning",
               "InputPayload":{
                  "targetInstances":"{{getSecondaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"waitForSecondaryRunningInstances"
         },
         {
            "name":"waitForSecondaryRunningInstances",
            "action":"aws:executeScript",
            "timeoutSeconds":300,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"waitForRunningInstances",
               "InputPayload":{
                  "targetInstances":"{{getSecondaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            },
            "nextStep":"returnSecondaryTagKey"
         },
         {
            "name":"returnSecondaryTagKey",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "secondaryTag":"{{SecondaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"secondaryPatchGroupKey",
                  "Selector":"$.Payload.tagKey",
                  "Type":"String"
               }
            ],
            "nextStep":"returnSecondaryTagValue"
         },
         {
            "name":"returnSecondaryTagValue",
            "action":"aws:executeScript",
            "timeoutSeconds":120,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnTagValues",
               "InputPayload":{
                  "secondaryTag":"{{SecondaryPatchGroupTag}}"
               },
               "Script":"..."
            },
            "outputs":[
               {
                  "Name":"Payload",
                  "Selector":"$.Payload",
                  "Type":"StringMap"
               },
               {
                  "Name":"secondaryPatchGroupValue",
                  "Selector":"$.Payload.tagValue",
                  "Type":"String"
               }
            ],
            "nextStep":"patchSecondaryInstances"
         },
         {
            "name":"patchSecondaryInstances",
            "action":"aws:runCommand",
            "onFailure":"Abort",
            "timeoutSeconds":7200,
            "inputs":{
               "DocumentName":"AWS-RunPatchBaseline",
               "Parameters":{
                  "SnapshotId":"{{SnapshotId}}",
                  "RebootOption":"{{RebootOption}}",
                  "Operation":"{{Operation}}"
               },
               "Targets":[
                  {
                     "Key":"{{returnSecondaryTagKey.secondaryPatchGroupKey}}",
                     "Values":[
                        "{{returnSecondaryTagValue.secondaryPatchGroupValue}}"
                     ]
                  }
               ],
               "MaxConcurrency":"10%",
               "MaxErrors":"10%"
            },
            "nextStep":"returnSecondaryToOriginalState"
         },
         {
            "name":"returnSecondaryToOriginalState",
            "action":"aws:executeScript",
            "timeoutSeconds":600,
            "onFailure":"Abort",
            "inputs":{
               "Runtime":"python3.11",
               "Handler":"returnToOriginalState",
               "InputPayload":{
                  "targetInstances":"{{getSecondaryInstanceState.originalInstanceStates}}"
               },
               "Script":"..."
            }
         }
      ]
   }
   ```

------

有关此示例中使用的自动化操作的更多信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。

# 其他运行手册示例
<a name="automation-document-examples"></a>

以下示例运行手册演示了如何使用 AWS Systems Manager 自动化操作来自动执行常见部署、故障排除和维护任务。

**注意**  
本部分提供的示例运行手册旨在演示如何创建自定义运行手册以支持您的特定操作需求。这些运行手册并非供您在生产环境中原样使用。但是，您可以自定义这些文档来满足自己的使用需求。

**Topics**
+ [部署 VPC 架构和 Microsoft Active Directory 域控制器](automation-document-architecture-deployment-example.md)
+ [从最新快照还原根卷](automation-document-instance-recovery-example.md)
+ [创建 AMI 和跨区域副本](automation-document-backup-maintenance-example.md)

# 部署 VPC 架构和 Microsoft Active Directory 域控制器
<a name="automation-document-architecture-deployment-example"></a>

要提高效率并实现常见任务的标准化，可以选择自动执行部署。如果您定期在多个账户和 AWS 区域 中部署相同的架构，此操作会很有帮助。自动进行架构部署还可以降低在手动部署架构时发生人为错误的可能性。AWS Systems Manager自动化操作可帮助您做到这一点。Automation 是 AWS Systems Manager 中的一项工具。

以下示例 AWS Systems Manager 运行手册执行这些操作：
+ 使用 Systems Manager Parameter Store 检索最新的 Windows Server 2016 Amazon Machine Image（AMI），在启动将配置作为域控制器的 EC2 实例时使用。Parameter Store 是 AWS Systems Manager 中的一项工具。
+ 使用 `aws:executeAwsApi` 自动化操作调用多个 AWS API 操作来创建 VPC 架构。域控制器实例在私有子网中启动，使用 NAT 网关连接到 Internet。这允许实例上的 SSM Agent 以访问必需的 Systems Manager 端点。
+ 使用 `aws:waitForAwsResourceProperty` 自动化操作确认上一个操作所启动的实例对于 `Online` 处于 AWS Systems Manager 状态。
+ 使用 `aws:runCommand` 自动化操作配置作为 Microsoft Active Directory 域控制器启动的实例。

------
#### [ YAML ]

```
    ---
    description: Custom Automation Deployment Example
    schemaVersion: '0.3'
    parameters:
      AutomationAssumeRole:
        type: String
        default: ''
        description: >-
          (Optional) The ARN of the role that allows Automation to perform the
          actions on your behalf. If no role is specified, Systems Manager
          Automation uses your IAM permissions to run this runbook.
    mainSteps:
      - name: getLatestWindowsAmi
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ssm
          Api: GetParameter
          Name: >-
            /aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base
        outputs:
          - Name: amiId
            Selector: $.Parameter.Value
            Type: String
        nextStep: createSSMInstanceRole
      - name: createSSMInstanceRole
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: iam
          Api: CreateRole
          AssumeRolePolicyDocument: >-
            {"Version": "2012-10-17",		 	 	 "Statement":[{"Effect":"Allow","Principal":{"Service":["ec2.amazonaws.com"]},"Action":["sts:AssumeRole"]}]}
          RoleName: sampleSSMInstanceRole
        nextStep: attachManagedSSMPolicy
      - name: attachManagedSSMPolicy
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: iam
          Api: AttachRolePolicy
          PolicyArn: 'arn:aws:iam::aws:policy/service-role/AmazonSSMManagedInstanceCore'
          RoleName: sampleSSMInstanceRole
        nextStep: createSSMInstanceProfile
      - name: createSSMInstanceProfile
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: iam
          Api: CreateInstanceProfile
          InstanceProfileName: sampleSSMInstanceRole
        outputs:
          - Name: instanceProfileArn
            Selector: $.InstanceProfile.Arn
            Type: String
        nextStep: addSSMInstanceRoleToProfile
      - name: addSSMInstanceRoleToProfile
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: iam
          Api: AddRoleToInstanceProfile
          InstanceProfileName: sampleSSMInstanceRole
          RoleName: sampleSSMInstanceRole
        nextStep: createVpc
      - name: createVpc
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateVpc
          CidrBlock: 10.0.100.0/22
        outputs:
          - Name: vpcId
            Selector: $.Vpc.VpcId
            Type: String
        nextStep: getMainRtb
      - name: getMainRtb
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: DescribeRouteTables
          Filters:
            - Name: vpc-id
              Values:
                - '{{ createVpc.vpcId }}'
        outputs:
          - Name: mainRtbId
            Selector: '$.RouteTables[0].RouteTableId'
            Type: String
        nextStep: verifyMainRtb
      - name: verifyMainRtb
        action: aws:assertAwsResourceProperty
        onFailure: Abort
        inputs:
          Service: ec2
          Api: DescribeRouteTables
          RouteTableIds:
            - '{{ getMainRtb.mainRtbId }}'
          PropertySelector: '$.RouteTables[0].Associations[0].Main'
          DesiredValues:
            - 'True'
        nextStep: createPubSubnet
      - name: createPubSubnet
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateSubnet
          CidrBlock: 10.0.103.0/24
          AvailabilityZone: us-west-2c
          VpcId: '{{ createVpc.vpcId }}'
        outputs:
          - Name: pubSubnetId
            Selector: $.Subnet.SubnetId
            Type: String
        nextStep: createPubRtb
      - name: createPubRtb
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateRouteTable
          VpcId: '{{ createVpc.vpcId }}'
        outputs:
          - Name: pubRtbId
            Selector: $.RouteTable.RouteTableId
            Type: String
        nextStep: createIgw
      - name: createIgw
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateInternetGateway
        outputs:
          - Name: igwId
            Selector: $.InternetGateway.InternetGatewayId
            Type: String
        nextStep: attachIgw
      - name: attachIgw
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: AttachInternetGateway
          InternetGatewayId: '{{ createIgw.igwId }}'
          VpcId: '{{ createVpc.vpcId }}'
        nextStep: allocateEip
      - name: allocateEip
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: AllocateAddress
          Domain: vpc
        outputs:
          - Name: eipAllocationId
            Selector: $.AllocationId
            Type: String
        nextStep: createNatGw
      - name: createNatGw
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateNatGateway
          AllocationId: '{{ allocateEip.eipAllocationId }}'
          SubnetId: '{{ createPubSubnet.pubSubnetId }}'
        outputs:
          - Name: natGwId
            Selector: $.NatGateway.NatGatewayId
            Type: String
        nextStep: verifyNatGwAvailable
      - name: verifyNatGwAvailable
        action: aws:waitForAwsResourceProperty
        timeoutSeconds: 150
        inputs:
          Service: ec2
          Api: DescribeNatGateways
          NatGatewayIds:
            - '{{ createNatGw.natGwId }}'
          PropertySelector: '$.NatGateways[0].State'
          DesiredValues:
            - available
        nextStep: createNatRoute
      - name: createNatRoute
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateRoute
          DestinationCidrBlock: 0.0.0.0/0
          NatGatewayId: '{{ createNatGw.natGwId }}'
          RouteTableId: '{{ getMainRtb.mainRtbId }}'
        nextStep: createPubRoute
      - name: createPubRoute
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateRoute
          DestinationCidrBlock: 0.0.0.0/0
          GatewayId: '{{ createIgw.igwId }}'
          RouteTableId: '{{ createPubRtb.pubRtbId }}'
        nextStep: setPubSubAssoc
      - name: setPubSubAssoc
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: AssociateRouteTable
          RouteTableId: '{{ createPubRtb.pubRtbId }}'
          SubnetId: '{{ createPubSubnet.pubSubnetId }}'
      - name: createDhcpOptions
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateDhcpOptions
          DhcpConfigurations:
            - Key: domain-name-servers
              Values:
                - '10.0.100.50,10.0.101.50'
            - Key: domain-name
              Values:
                - sample.com
        outputs:
          - Name: dhcpOptionsId
            Selector: $.DhcpOptions.DhcpOptionsId
            Type: String
        nextStep: createDCSubnet1
      - name: createDCSubnet1
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateSubnet
          CidrBlock: 10.0.100.0/24
          AvailabilityZone: us-west-2a
          VpcId: '{{ createVpc.vpcId }}'
        outputs:
          - Name: firstSubnetId
            Selector: $.Subnet.SubnetId
            Type: String
        nextStep: createDCSubnet2
      - name: createDCSubnet2
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateSubnet
          CidrBlock: 10.0.101.0/24
          AvailabilityZone: us-west-2b
          VpcId: '{{ createVpc.vpcId }}'
        outputs:
          - Name: secondSubnetId
            Selector: $.Subnet.SubnetId
            Type: String
        nextStep: createDCSecGroup
      - name: createDCSecGroup
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: CreateSecurityGroup
          GroupName: SampleDCSecGroup
          Description: Security Group for Sample Domain Controllers
          VpcId: '{{ createVpc.vpcId }}'
        outputs:
          - Name: dcSecGroupId
            Selector: $.GroupId
            Type: String
        nextStep: authIngressDCTraffic
      - name: authIngressDCTraffic
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: AuthorizeSecurityGroupIngress
          GroupId: '{{ createDCSecGroup.dcSecGroupId }}'
          IpPermissions:
            - FromPort: -1
              IpProtocol: '-1'
              IpRanges:
                - CidrIp: 0.0.0.0/0
                  Description: Allow all traffic between Domain Controllers
        nextStep: verifyInstanceProfile
      - name: verifyInstanceProfile
        action: aws:waitForAwsResourceProperty
        maxAttempts: 5
        onFailure: Abort
        inputs:
          Service: iam
          Api: ListInstanceProfilesForRole
          RoleName: sampleSSMInstanceRole
          PropertySelector: '$.InstanceProfiles[0].Arn'
          DesiredValues:
            - '{{ createSSMInstanceProfile.instanceProfileArn }}'
        nextStep: iamEventualConsistency
      - name: iamEventualConsistency
        action: aws:sleep
        inputs:
          Duration: PT2M
        nextStep: launchDC1
      - name: launchDC1
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: RunInstances
          BlockDeviceMappings:
            - DeviceName: /dev/sda1
              Ebs:
                DeleteOnTermination: true
                VolumeSize: 50
                VolumeType: gp2
            - DeviceName: xvdf
              Ebs:
                DeleteOnTermination: true
                VolumeSize: 100
                VolumeType: gp2
          IamInstanceProfile:
            Arn: '{{ createSSMInstanceProfile.instanceProfileArn }}'
          ImageId: '{{ getLatestWindowsAmi.amiId }}'
          InstanceType: t2.micro
          MaxCount: 1
          MinCount: 1
          PrivateIpAddress: 10.0.100.50
          SecurityGroupIds:
            - '{{ createDCSecGroup.dcSecGroupId }}'
          SubnetId: '{{ createDCSubnet1.firstSubnetId }}'
          TagSpecifications:
            - ResourceType: instance
              Tags:
                - Key: Name
                  Value: SampleDC1
        outputs:
          - Name: pdcInstanceId
            Selector: '$.Instances[0].InstanceId'
            Type: String
        nextStep: launchDC2
      - name: launchDC2
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: RunInstances
          BlockDeviceMappings:
            - DeviceName: /dev/sda1
              Ebs:
                DeleteOnTermination: true
                VolumeSize: 50
                VolumeType: gp2
            - DeviceName: xvdf
              Ebs:
                DeleteOnTermination: true
                VolumeSize: 100
                VolumeType: gp2
          IamInstanceProfile:
            Arn: '{{ createSSMInstanceProfile.instanceProfileArn }}'
          ImageId: '{{ getLatestWindowsAmi.amiId }}'
          InstanceType: t2.micro
          MaxCount: 1
          MinCount: 1
          PrivateIpAddress: 10.0.101.50
          SecurityGroupIds:
            - '{{ createDCSecGroup.dcSecGroupId }}'
          SubnetId: '{{ createDCSubnet2.secondSubnetId }}'
          TagSpecifications:
            - ResourceType: instance
              Tags:
                - Key: Name
                  Value: SampleDC2
        outputs:
          - Name: adcInstanceId
            Selector: '$.Instances[0].InstanceId'
            Type: String
        nextStep: verifyDCInstanceState
      - name: verifyDCInstanceState
        action: aws:waitForAwsResourceProperty
        inputs:
          Service: ec2
          Api: DescribeInstanceStatus
          IncludeAllInstances: true
          InstanceIds:
            - '{{ launchDC1.pdcInstanceId }}'
            - '{{ launchDC2.adcInstanceId }}'
          PropertySelector: '$.InstanceStatuses..InstanceState.Name'
          DesiredValues:
            - running
        nextStep: verifyInstancesOnlineSSM
      - name: verifyInstancesOnlineSSM
        action: aws:waitForAwsResourceProperty
        timeoutSeconds: 600
        inputs:
          Service: ssm
          Api: DescribeInstanceInformation
          InstanceInformationFilterList:
            - key: InstanceIds
              valueSet:
                - '{{ launchDC1.pdcInstanceId }}'
                - '{{ launchDC2.adcInstanceId }}'
          PropertySelector: '$.InstanceInformationList..PingStatus'
          DesiredValues:
            - Online
        nextStep: installADRoles
      - name: installADRoles
        action: aws:runCommand
        inputs:
          DocumentName: AWS-RunPowerShellScript
          InstanceIds:
            - '{{ launchDC1.pdcInstanceId }}'
            - '{{ launchDC2.adcInstanceId }}'
          Parameters:
            commands: |-
              try {
                  Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
              }
              catch {
                  Write-Error "Failed to install ADDS Role."
              }
        nextStep: setAdminPassword
      - name: setAdminPassword
        action: aws:runCommand
        inputs:
          DocumentName: AWS-RunPowerShellScript
          InstanceIds:
            - '{{ launchDC1.pdcInstanceId }}'
          Parameters:
            commands:
              - net user Administrator "sampleAdminPass123!"
        nextStep: createForest
      - name: createForest
        action: aws:runCommand
        inputs:
          DocumentName: AWS-RunPowerShellScript
          InstanceIds:
            - '{{ launchDC1.pdcInstanceId }}'
          Parameters:
            commands: |-
              $dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force
              try {
                  Install-ADDSForest -DomainName "sample.com" -DomainMode 6 -ForestMode 6 -InstallDNS -DatabasePath "D:\NTDS" -SysvolPath "D:\SYSVOL" -SafeModeAdministratorPassword $dsrmPass -Force
              }
              catch {
                  Write-Error $_
              }
              try {
                  Add-DnsServerForwarder -IPAddress "10.0.100.2"
              }
              catch {
                  Write-Error $_
              }
        nextStep: associateDhcpOptions
      - name: associateDhcpOptions
        action: aws:executeAwsApi
        onFailure: Abort
        inputs:
          Service: ec2
          Api: AssociateDhcpOptions
          DhcpOptionsId: '{{ createDhcpOptions.dhcpOptionsId }}'
          VpcId: '{{ createVpc.vpcId }}'
        nextStep: waitForADServices
      - name: waitForADServices
        action: aws:sleep
        inputs:
          Duration: PT1M
        nextStep: promoteADC
      - name: promoteADC
        action: aws:runCommand
        inputs:
          DocumentName: AWS-RunPowerShellScript
          InstanceIds:
            - '{{ launchDC2.adcInstanceId }}'
          Parameters:
            commands: |-
              ipconfig /renew
              $dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force
              $domAdminUser = "sample\Administrator"
              $domAdminPass = "sampleAdminPass123!" | ConvertTo-SecureString -asPlainText -Force
              $domAdminCred = New-Object System.Management.Automation.PSCredential($domAdminUser,$domAdminPass)
    
              try {
                  Install-ADDSDomainController -DomainName "sample.com" -InstallDNS -DatabasePath "D:\NTDS" -SysvolPath "D:\SYSVOL" -SafeModeAdministratorPassword $dsrmPass -Credential $domAdminCred -Force
              }
              catch {
                  Write-Error $_
              }
```

------
#### [ JSON ]

```
{
      "description": "Custom Automation Deployment Example",
      "schemaVersion": "0.3",
      "assumeRole": "{{ AutomationAssumeRole }}",
      "parameters": {
        "AutomationAssumeRole": {
          "type": "String",
          "description": "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to run this runbook.",
          "default": ""
        }
      },
      "mainSteps": [
        {
          "name": "getLatestWindowsAmi",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ssm",
            "Api": "GetParameter",
            "Name": "/aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base"
          },
          "outputs": [
            {
              "Name": "amiId",
              "Selector": "$.Parameter.Value",
              "Type": "String"
            }
          ],
          "nextStep": "createSSMInstanceRole"
        },
        {
          "name": "createSSMInstanceRole",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "iam",
            "Api": "CreateRole",
            "AssumeRolePolicyDocument": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}",
            "RoleName": "sampleSSMInstanceRole"
          },
          "nextStep": "attachManagedSSMPolicy"
        },
        {
          "name": "attachManagedSSMPolicy",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "iam",
            "Api": "AttachRolePolicy",
            "PolicyArn": "arn:aws:iam::aws:policy/service-role/AmazonSSMManagedInstanceCore",
            "RoleName": "sampleSSMInstanceRole"
          },
          "nextStep": "createSSMInstanceProfile"
        },
        {
          "name": "createSSMInstanceProfile",
          "action":"aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "iam",
            "Api": "CreateInstanceProfile",
            "InstanceProfileName": "sampleSSMInstanceRole"
          },
          "outputs": [
            {
              "Name": "instanceProfileArn",
              "Selector": "$.InstanceProfile.Arn",
              "Type": "String"
            }
          ],
          "nextStep": "addSSMInstanceRoleToProfile"
        },
        {
          "name": "addSSMInstanceRoleToProfile",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "iam",
            "Api": "AddRoleToInstanceProfile",
            "InstanceProfileName": "sampleSSMInstanceRole",
            "RoleName": "sampleSSMInstanceRole"
          },
          "nextStep": "createVpc"
        },
        {
          "name": "createVpc",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateVpc",
            "CidrBlock": "10.0.100.0/22"
          },
          "outputs": [
            {
              "Name": "vpcId",
              "Selector": "$.Vpc.VpcId",
              "Type": "String"
            }
          ],
          "nextStep": "getMainRtb"
        },
        {
          "name": "getMainRtb",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "DescribeRouteTables",
            "Filters": [
              {
                "Name": "vpc-id",
                "Values": ["{{ createVpc.vpcId }}"]
              }
            ]
          },
          "outputs": [
            {
              "Name": "mainRtbId",
              "Selector": "$.RouteTables[0].RouteTableId",
              "Type": "String"
            }
          ],
          "nextStep": "verifyMainRtb"
        },
        {
          "name": "verifyMainRtb",
          "action": "aws:assertAwsResourceProperty",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "DescribeRouteTables",
            "RouteTableIds": ["{{ getMainRtb.mainRtbId }}"],
            "PropertySelector": "$.RouteTables[0].Associations[0].Main",
            "DesiredValues": ["True"]
          },
          "nextStep": "createPubSubnet"
        },
        {
          "name": "createPubSubnet",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateSubnet",
            "CidrBlock": "10.0.103.0/24",
            "AvailabilityZone": "us-west-2c",
            "VpcId": "{{ createVpc.vpcId }}"
          },
          "outputs":[
            {
              "Name": "pubSubnetId",
              "Selector": "$.Subnet.SubnetId",
              "Type": "String"
            }
          ],
          "nextStep": "createPubRtb"
        },
        {
          "name": "createPubRtb",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateRouteTable",
            "VpcId": "{{ createVpc.vpcId }}"
          },
          "outputs": [
            {
              "Name": "pubRtbId",
              "Selector": "$.RouteTable.RouteTableId",
              "Type": "String"
            }
          ],
          "nextStep": "createIgw"
        },
        {
          "name": "createIgw",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateInternetGateway"
          },
          "outputs": [
            {
              "Name": "igwId",
              "Selector": "$.InternetGateway.InternetGatewayId",
              "Type": "String"
            }
          ],
          "nextStep": "attachIgw"
        },
        {
          "name": "attachIgw",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "AttachInternetGateway",
            "InternetGatewayId": "{{ createIgw.igwId }}",
            "VpcId": "{{ createVpc.vpcId }}"
          },
          "nextStep": "allocateEip"
        },
        {
          "name": "allocateEip",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "AllocateAddress",
            "Domain": "vpc"
          },
          "outputs": [
            {
              "Name": "eipAllocationId",
              "Selector": "$.AllocationId",
              "Type": "String"
            }
          ],
          "nextStep": "createNatGw"
        },
        {
          "name": "createNatGw",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateNatGateway",
            "AllocationId": "{{ allocateEip.eipAllocationId }}",
            "SubnetId": "{{ createPubSubnet.pubSubnetId }}"
          },
          "outputs":[
            {
              "Name": "natGwId",
              "Selector": "$.NatGateway.NatGatewayId",
              "Type": "String"
            }
          ],
          "nextStep": "verifyNatGwAvailable"
        },
        {
          "name": "verifyNatGwAvailable",
          "action": "aws:waitForAwsResourceProperty",
          "timeoutSeconds": 150,
          "inputs": {
            "Service": "ec2",
            "Api": "DescribeNatGateways",
            "NatGatewayIds": [
              "{{ createNatGw.natGwId }}"
            ],
            "PropertySelector": "$.NatGateways[0].State",
            "DesiredValues": [
              "available"
            ]
          },
          "nextStep": "createNatRoute"
        },
        {
          "name": "createNatRoute",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateRoute",
            "DestinationCidrBlock": "0.0.0.0/0",
            "NatGatewayId": "{{ createNatGw.natGwId }}",
            "RouteTableId": "{{ getMainRtb.mainRtbId }}"
          },
          "nextStep": "createPubRoute"
        },
        {
          "name": "createPubRoute",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateRoute",
            "DestinationCidrBlock": "0.0.0.0/0",
            "GatewayId": "{{ createIgw.igwId }}",
            "RouteTableId": "{{ createPubRtb.pubRtbId }}"
          },
          "nextStep": "setPubSubAssoc"
        },
        {
          "name": "setPubSubAssoc",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "AssociateRouteTable",
            "RouteTableId": "{{ createPubRtb.pubRtbId }}",
            "SubnetId": "{{ createPubSubnet.pubSubnetId }}"
          }
        },
        {
          "name": "createDhcpOptions",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateDhcpOptions",
            "DhcpConfigurations": [
              {
                "Key": "domain-name-servers",
                "Values": ["10.0.100.50,10.0.101.50"]
              },
              {
                "Key": "domain-name",
                "Values": ["sample.com"]
              }
            ]
          },
          "outputs": [
            {
              "Name": "dhcpOptionsId",
              "Selector": "$.DhcpOptions.DhcpOptionsId",
              "Type": "String"
            }
          ],
          "nextStep": "createDCSubnet1"
        },
        {
          "name": "createDCSubnet1",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateSubnet",
            "CidrBlock": "10.0.100.0/24",
            "AvailabilityZone": "us-west-2a",
            "VpcId": "{{ createVpc.vpcId }}"
          },
          "outputs": [
            {
              "Name": "firstSubnetId",
              "Selector": "$.Subnet.SubnetId",
              "Type": "String"
            }
          ],
          "nextStep": "createDCSubnet2"
        },
        {
          "name": "createDCSubnet2",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateSubnet",
            "CidrBlock": "10.0.101.0/24",
            "AvailabilityZone": "us-west-2b",
            "VpcId": "{{ createVpc.vpcId }}"
          },
          "outputs": [
            {
              "Name": "secondSubnetId",
              "Selector": "$.Subnet.SubnetId",
              "Type": "String"
            }
          ],
          "nextStep": "createDCSecGroup"
        },
        {
          "name": "createDCSecGroup",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "CreateSecurityGroup",
            "GroupName": "SampleDCSecGroup",
            "Description": "Security Group for Example Domain Controllers",
            "VpcId": "{{ createVpc.vpcId }}"
          },
          "outputs": [
            {
              "Name": "dcSecGroupId",
              "Selector": "$.GroupId",
              "Type": "String"
            }
          ],
          "nextStep": "authIngressDCTraffic"
        },
        {
          "name": "authIngressDCTraffic",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "AuthorizeSecurityGroupIngress",
            "GroupId": "{{ createDCSecGroup.dcSecGroupId }}",
            "IpPermissions": [
              {
                "FromPort": -1,
                "IpProtocol": "-1",
                "IpRanges": [
                  {
                    "CidrIp": "0.0.0.0/0",
                    "Description": "Allow all traffic between Domain Controllers"
                  }
                ]
              }
            ]
          },
          "nextStep": "verifyInstanceProfile"
        },
        {
          "name": "verifyInstanceProfile",
          "action": "aws:waitForAwsResourceProperty",
          "maxAttempts": 5,
          "onFailure": "Abort",
          "inputs": {
            "Service": "iam",
            "Api": "ListInstanceProfilesForRole",
            "RoleName": "sampleSSMInstanceRole",
            "PropertySelector": "$.InstanceProfiles[0].Arn",
            "DesiredValues": [
              "{{ createSSMInstanceProfile.instanceProfileArn }}"
            ]
          },
          "nextStep": "iamEventualConsistency"
        },
        {
          "name": "iamEventualConsistency",
          "action": "aws:sleep",
          "inputs": {
            "Duration": "PT2M"
          },
          "nextStep": "launchDC1"
        },
        {
          "name": "launchDC1",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "RunInstances",
            "BlockDeviceMappings": [
              {
                "DeviceName": "/dev/sda1",
                "Ebs": {
                  "DeleteOnTermination": true,
                  "VolumeSize": 50,
                  "VolumeType": "gp2"
                }
              },
              {
                "DeviceName": "xvdf",
                "Ebs": {
                  "DeleteOnTermination": true,
                  "VolumeSize": 100,
                  "VolumeType": "gp2"
                }
              }
            ],
            "IamInstanceProfile": {
              "Arn": "{{ createSSMInstanceProfile.instanceProfileArn }}"
            },
            "ImageId": "{{ getLatestWindowsAmi.amiId }}",
            "InstanceType": "t2.micro",
            "MaxCount": 1,
            "MinCount": 1,
            "PrivateIpAddress": "10.0.100.50",
            "SecurityGroupIds": [
              "{{ createDCSecGroup.dcSecGroupId }}"
            ],
            "SubnetId": "{{ createDCSubnet1.firstSubnetId }}",
            "TagSpecifications": [
              {
                "ResourceType": "instance",
                "Tags": [
                  {
                    "Key": "Name",
                    "Value": "SampleDC1"
                  }
                ]
              }
            ]
          },
          "outputs": [
            {
              "Name": "pdcInstanceId",
              "Selector": "$.Instances[0].InstanceId",
              "Type": "String"
            }
          ],
          "nextStep": "launchDC2"
        },
        {
          "name": "launchDC2",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "RunInstances",
            "BlockDeviceMappings": [
              {
                "DeviceName": "/dev/sda1",
                "Ebs": {
                  "DeleteOnTermination": true,
                  "VolumeSize": 50,
                  "VolumeType": "gp2"
                }
              },
              {
                "DeviceName": "xvdf",
                "Ebs": {
                  "DeleteOnTermination": true,
                  "VolumeSize": 100,
                  "VolumeType": "gp2"
                }
              }
            ],
            "IamInstanceProfile": {
              "Arn": "{{ createSSMInstanceProfile.instanceProfileArn }}"
            },
            "ImageId": "{{ getLatestWindowsAmi.amiId }}",
            "InstanceType": "t2.micro",
            "MaxCount": 1,
            "MinCount": 1,
            "PrivateIpAddress": "10.0.101.50",
            "SecurityGroupIds": [
              "{{ createDCSecGroup.dcSecGroupId }}"
            ],
            "SubnetId": "{{ createDCSubnet2.secondSubnetId }}",
            "TagSpecifications": [
              {
                "ResourceType": "instance",
                "Tags": [
                  {
                    "Key": "Name",
                    "Value": "SampleDC2"
                  }
                ]
              }
            ]
          },
          "outputs": [
            {
              "Name": "adcInstanceId",
              "Selector": "$.Instances[0].InstanceId",
              "Type": "String"
            }
          ],
          "nextStep": "verifyDCInstanceState"
        },
        {
          "name": "verifyDCInstanceState",
          "action": "aws:waitForAwsResourceProperty",
          "inputs": {
            "Service": "ec2",
            "Api": "DescribeInstanceStatus",
            "IncludeAllInstances": true,
            "InstanceIds": [
              "{{ launchDC1.pdcInstanceId }}",
              "{{ launchDC2.adcInstanceId }}"
            ],
            "PropertySelector": "$.InstanceStatuses[0].InstanceState.Name",
            "DesiredValues": [
              "running"
            ]
          },
          "nextStep": "verifyInstancesOnlineSSM"
        },
        {
          "name": "verifyInstancesOnlineSSM",
          "action": "aws:waitForAwsResourceProperty",
          "timeoutSeconds": 600,
          "inputs": {
            "Service": "ssm",
            "Api": "DescribeInstanceInformation",
            "InstanceInformationFilterList": [
              {
                "key": "InstanceIds",
                "valueSet": [
                  "{{ launchDC1.pdcInstanceId }}",
                  "{{ launchDC2.adcInstanceId }}"
                ]
              }
            ],
            "PropertySelector": "$.InstanceInformationList[0].PingStatus",
            "DesiredValues": [
              "Online"
            ]
          },
          "nextStep": "installADRoles"
        },
        {
          "name": "installADRoles",
          "action": "aws:runCommand",
          "inputs": {
            "DocumentName": "AWS-RunPowerShellScript",
            "InstanceIds": [
              "{{ launchDC1.pdcInstanceId }}",
              "{{ launchDC2.adcInstanceId }}"
            ],
            "Parameters": {
              "commands": [
                "try {",
                "  Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools",
                "}",
                "catch {",
                "  Write-Error \"Failed to install ADDS Role.\"",
                "}"
              ]
            }
          },
          "nextStep": "setAdminPassword"
        },
        {
          "name": "setAdminPassword",
          "action": "aws:runCommand",
          "inputs": {
            "DocumentName": "AWS-RunPowerShellScript",
            "InstanceIds": [
              "{{ launchDC1.pdcInstanceId }}"
            ],
            "Parameters": {
              "commands": [
                "net user Administrator \"sampleAdminPass123!\""
              ]
            }
          },
          "nextStep": "createForest"
        },
        {
          "name": "createForest",
          "action": "aws:runCommand",
          "inputs": {
            "DocumentName": "AWS-RunPowerShellScript",
            "InstanceIds": [
              "{{ launchDC1.pdcInstanceId }}"
            ],
            "Parameters": {
              "commands": [
                "$dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force",
                "try {",
                "   Install-ADDSForest -DomainName \"sample.com\" -DomainMode 6 -ForestMode 6 -InstallDNS -DatabasePath \"D:\\NTDS\" -SysvolPath \"D:\\SYSVOL\" -SafeModeAdministratorPassword $dsrmPass -Force",
                "}",
                "catch {",
                "   Write-Error $_",
                "}",
                "try {",
                "   Add-DnsServerForwarder -IPAddress \"10.0.100.2\"",
                "}",
                "catch {",
                "   Write-Error $_",
                "}"
              ]
            }
          },
          "nextStep": "associateDhcpOptions"
        },
        {
          "name": "associateDhcpOptions",
          "action": "aws:executeAwsApi",
          "onFailure": "Abort",
          "inputs": {
            "Service": "ec2",
            "Api": "AssociateDhcpOptions",
            "DhcpOptionsId": "{{ createDhcpOptions.dhcpOptionsId }}",
            "VpcId": "{{ createVpc.vpcId }}"
          },
          "nextStep": "waitForADServices"
        },
        {
          "name": "waitForADServices",
          "action": "aws:sleep",
          "inputs": {
            "Duration": "PT1M"
          },
          "nextStep": "promoteADC"
        },
        {
          "name": "promoteADC",
          "action": "aws:runCommand",
          "inputs": {
            "DocumentName": "AWS-RunPowerShellScript",
            "InstanceIds": [
              "{{ launchDC2.adcInstanceId }}"
            ],
            "Parameters": {
              "commands": [
                "ipconfig /renew",
                "$dsrmPass = 'sample123!' | ConvertTo-SecureString -asPlainText -Force",
                "$domAdminUser = \"sample\\Administrator\"",
                "$domAdminPass = \"sampleAdminPass123!\" | ConvertTo-SecureString -asPlainText -Force",
                "$domAdminCred = New-Object System.Management.Automation.PSCredential($domAdminUser,$domAdminPass)",
                "try {",
                "   Install-ADDSDomainController -DomainName \"sample.com\" -InstallDNS -DatabasePath \"D:\\NTDS\" -SysvolPath \"D:\\SYSVOL\" -SafeModeAdministratorPassword $dsrmPass -Credential $domAdminCred -Force",
                "}",
                "catch {",
                "   Write-Error $_",
                "}"
              ]
            }
          }
        }
      ]
    }
```

------

# 从最新快照还原根卷
<a name="automation-document-instance-recovery-example"></a>

根卷上的操作系统可能会出于各种原因而受损。例如，在执行修补操作后，实例可能会因内核或注册表损坏而无法成功启动。自动执行常见故障排除任务（例如，从修补操作前拍摄的最新快照还原根卷）可以减少停机时间并加快故障排除工作。AWS Systems Manager自动化操作可帮助您做到这一点。Automation 是 AWS Systems Manager 中的一项工具。

以下示例 AWS Systems Manager 运行手册执行这些操作：
+ 使用 `aws:executeAwsApi` 自动化操作从实例的根卷检索详细信息。
+ 使用 `aws:executeScript` 自动化操作检索根卷的最新快照。
+ 如果找到了根卷的快照，则使用 `aws:branch` 自动化操作继续执行自动化。

------
#### [ YAML ]

```
    ---
    description: Custom Automation Troubleshooting Example
    schemaVersion: '0.3'
    assumeRole: "{{ AutomationAssumeRole }}"
    parameters:
      AutomationAssumeRole:
        type: String
        description: "(Required) The ARN of the role that allows Automation to perform
          the actions on your behalf. If no role is specified, Systems Manager Automation
          uses your IAM permissions to use this runbook."
        default: ''
      InstanceId:
          type: String
          description: "(Required) The Instance Id whose root EBS volume you want to restore the latest Snapshot."
          default: ''
    mainSteps:
    - name: getInstanceDetails
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: DescribeInstances
        InstanceIds:
        - "{{ InstanceId }}"
      outputs:
        - Name: availabilityZone
          Selector: "$.Reservations[0].Instances[0].Placement.AvailabilityZone"
          Type: String
        - Name: rootDeviceName
          Selector: "$.Reservations[0].Instances[0].RootDeviceName"
          Type: String
      nextStep: getRootVolumeId
    - name: getRootVolumeId
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: DescribeVolumes
        Filters:
        -  Name: attachment.device
           Values: ["{{ getInstanceDetails.rootDeviceName }}"]
        -  Name: attachment.instance-id
           Values: ["{{ InstanceId }}"]
      outputs:
        - Name: rootVolumeId
          Selector: "$.Volumes[0].VolumeId"
          Type: String
      nextStep: getSnapshotsByStartTime
    - name: getSnapshotsByStartTime
      action: aws:executeScript
      timeoutSeconds: 45
      onFailure: Abort
      inputs:
        Runtime: python3.11
        Handler: getSnapshotsByStartTime
        InputPayload:
          rootVolumeId : "{{ getRootVolumeId.rootVolumeId }}"
        Script: |-
          def getSnapshotsByStartTime(events,context):
            import boto3
    
            #Initialize client
            ec2 = boto3.client('ec2')
            rootVolumeId = events['rootVolumeId']
            snapshotsQuery = ec2.describe_snapshots(
              Filters=[
                {
                  "Name": "volume-id",
                  "Values": [rootVolumeId]
                }
              ]
            )
            if not snapshotsQuery['Snapshots']:
              noSnapshotFoundString = "NoSnapshotFound"
              return { 'noSnapshotFound' : noSnapshotFoundString }
            else:
              jsonSnapshots = snapshotsQuery['Snapshots']
              sortedSnapshots = sorted(jsonSnapshots, key=lambda k: k['StartTime'], reverse=True)
              latestSortedSnapshotId = sortedSnapshots[0]['SnapshotId']
              return { 'latestSnapshotId' : latestSortedSnapshotId }
      outputs:
      - Name: Payload
        Selector: $.Payload
        Type: StringMap
      - Name: latestSnapshotId
        Selector: $.Payload.latestSnapshotId
        Type: String
      - Name: noSnapshotFound
        Selector: $.Payload.noSnapshotFound
        Type: String 
      nextStep: branchFromResults
    - name: branchFromResults
      action: aws:branch
      onFailure: Abort
      inputs:
        Choices:
        - NextStep: createNewRootVolumeFromSnapshot
          Not:
            Variable: "{{ getSnapshotsByStartTime.noSnapshotFound }}"
            StringEquals: "NoSnapshotFound"
      isEnd: true
    - name: createNewRootVolumeFromSnapshot
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: CreateVolume
        AvailabilityZone: "{{ getInstanceDetails.availabilityZone }}"
        SnapshotId: "{{ getSnapshotsByStartTime.latestSnapshotId }}"
      outputs:
        - Name: newRootVolumeId
          Selector: "$.VolumeId"
          Type: String
      nextStep: stopInstance
    - name: stopInstance
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: StopInstances
        InstanceIds:
        - "{{ InstanceId }}"
      nextStep: verifyVolumeAvailability
    - name: verifyVolumeAvailability
      action: aws:waitForAwsResourceProperty
      timeoutSeconds: 120
      inputs:
        Service: ec2
        Api: DescribeVolumes
        VolumeIds:
        - "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}"
        PropertySelector: "$.Volumes[0].State"
        DesiredValues:
        - "available"
      nextStep: verifyInstanceStopped
    - name: verifyInstanceStopped
      action: aws:waitForAwsResourceProperty
      timeoutSeconds: 120
      inputs:
        Service: ec2
        Api: DescribeInstances
        InstanceIds:
        - "{{ InstanceId }}"
        PropertySelector: "$.Reservations[0].Instances[0].State.Name"
        DesiredValues:
        - "stopped"
      nextStep: detachRootVolume
    - name: detachRootVolume
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: DetachVolume
        VolumeId: "{{ getRootVolumeId.rootVolumeId }}"
      nextStep: verifyRootVolumeDetached
    - name: verifyRootVolumeDetached
      action: aws:waitForAwsResourceProperty
      timeoutSeconds: 30
      inputs:
        Service: ec2
        Api: DescribeVolumes
        VolumeIds:
        - "{{ getRootVolumeId.rootVolumeId }}"
        PropertySelector: "$.Volumes[0].State"
        DesiredValues:
        - "available"
      nextStep: attachNewRootVolume
    - name: attachNewRootVolume
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: AttachVolume
        Device: "{{ getInstanceDetails.rootDeviceName }}"
        InstanceId: "{{ InstanceId }}"
        VolumeId: "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}"
      nextStep: verifyNewRootVolumeAttached
    - name: verifyNewRootVolumeAttached
      action: aws:waitForAwsResourceProperty
      timeoutSeconds: 30
      inputs:
        Service: ec2
        Api: DescribeVolumes
        VolumeIds:
        - "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}"
        PropertySelector: "$.Volumes[0].Attachments[0].State"
        DesiredValues:
        - "attached"
      nextStep: startInstance
    - name: startInstance
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: StartInstances
        InstanceIds:
        - "{{ InstanceId }}"
```

------
#### [ JSON ]

```
    {
       "description": "Custom Automation Troubleshooting Example",
       "schemaVersion": "0.3",
       "assumeRole": "{{ AutomationAssumeRole }}",
       "parameters": {
          "AutomationAssumeRole": {
             "type": "String",
             "description": "(Required) The ARN of the role that allows Automation to perform the actions on your behalf. If no role is specified, Systems Manager Automation uses your IAM permissions to run this runbook.",
             "default": ""
          },
          "InstanceId": {
             "type": "String",
             "description": "(Required) The Instance Id whose root EBS volume you want to restore the latest Snapshot.",
             "default": ""
          }
       },
       "mainSteps": [
          {
             "name": "getInstanceDetails",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "DescribeInstances",
                "InstanceIds": [
                   "{{ InstanceId }}"
                ]
             },
             "outputs": [
                {
                   "Name": "availabilityZone",
                   "Selector": "$.Reservations[0].Instances[0].Placement.AvailabilityZone",
                   "Type": "String"
                },
                {
                   "Name": "rootDeviceName",
                   "Selector": "$.Reservations[0].Instances[0].RootDeviceName",
                   "Type": "String"
                }
             ],
             "nextStep": "getRootVolumeId"
          },
          {
             "name": "getRootVolumeId",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "DescribeVolumes",
                "Filters": [
                   {
                      "Name": "attachment.device",
                      "Values": [
                         "{{ getInstanceDetails.rootDeviceName }}"
                      ]
                   },
                   {
                      "Name": "attachment.instance-id",
                      "Values": [
                         "{{ InstanceId }}"
                      ]
                   }
                ]
             },
             "outputs": [
                {
                   "Name": "rootVolumeId",
                   "Selector": "$.Volumes[0].VolumeId",
                   "Type": "String"
                }
             ],
             "nextStep": "getSnapshotsByStartTime"
          },
          {
             "name": "getSnapshotsByStartTime",
             "action": "aws:executeScript",
             "timeoutSeconds": 45,
             "onFailure": "Continue",
             "inputs": {
                "Runtime": "python3.11",
                "Handler": "getSnapshotsByStartTime",
                "InputPayload": {
                   "rootVolumeId": "{{ getRootVolumeId.rootVolumeId }}"
                },
                "Attachment": "getSnapshotsByStartTime.py"
             },
             "outputs": [
                {
                   "Name": "Payload",
                   "Selector": "$.Payload",
                   "Type": "StringMap"
                },
                {
                   "Name": "latestSnapshotId",
                   "Selector": "$.Payload.latestSnapshotId",
                   "Type": "String"
                },
                {
                   "Name": "noSnapshotFound",
                   "Selector": "$.Payload.noSnapshotFound",
                   "Type": "String"
                }
             ],
             "nextStep": "branchFromResults"
          },
          {
             "name": "branchFromResults",
             "action": "aws:branch",
             "onFailure": "Abort",
             "inputs": {
                "Choices": [
                   {
                      "NextStep": "createNewRootVolumeFromSnapshot",
                      "Not": {
                         "Variable": "{{ getSnapshotsByStartTime.noSnapshotFound }}",
                         "StringEquals": "NoSnapshotFound"
                      }
                   }
                ]
             },
             "isEnd": true
          },
          {
             "name": "createNewRootVolumeFromSnapshot",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "CreateVolume",
                "AvailabilityZone": "{{ getInstanceDetails.availabilityZone }}",
                "SnapshotId": "{{ getSnapshotsByStartTime.latestSnapshotId }}"
             },
             "outputs": [
                {
                   "Name": "newRootVolumeId",
                   "Selector": "$.VolumeId",
                   "Type": "String"
                }
             ],
             "nextStep": "stopInstance"
          },
          {
             "name": "stopInstance",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "StopInstances",
                "InstanceIds": [
                   "{{ InstanceId }}"
                ]
             },
             "nextStep": "verifyVolumeAvailability"
          },
          {
             "name": "verifyVolumeAvailability",
             "action": "aws:waitForAwsResourceProperty",
             "timeoutSeconds": 120,
             "inputs": {
                "Service": "ec2",
                "Api": "DescribeVolumes",
                "VolumeIds": [
                   "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}"
                ],
                "PropertySelector": "$.Volumes[0].State",
                "DesiredValues": [
                   "available"
                ]
             },
             "nextStep": "verifyInstanceStopped"
          },
          {
             "name": "verifyInstanceStopped",
             "action": "aws:waitForAwsResourceProperty",
             "timeoutSeconds": 120,
             "inputs": {
                "Service": "ec2",
                "Api": "DescribeInstances",
                "InstanceIds": [
                   "{{ InstanceId }}"
                ],
                "PropertySelector": "$.Reservations[0].Instances[0].State.Name",
                "DesiredValues": [
                   "stopped"
                ]
             },
             "nextStep": "detachRootVolume"
          },
          {
             "name": "detachRootVolume",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "DetachVolume",
                "VolumeId": "{{ getRootVolumeId.rootVolumeId }}"
             },
             "nextStep": "verifyRootVolumeDetached"
          },
          {
             "name": "verifyRootVolumeDetached",
             "action": "aws:waitForAwsResourceProperty",
             "timeoutSeconds": 30,
             "inputs": {
                "Service": "ec2",
                "Api": "DescribeVolumes",
                "VolumeIds": [
                   "{{ getRootVolumeId.rootVolumeId }}"
                ],
                "PropertySelector": "$.Volumes[0].State",
                "DesiredValues": [
                   "available"
                ]
             },
             "nextStep": "attachNewRootVolume"
          },
          {
             "name": "attachNewRootVolume",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "AttachVolume",
                "Device": "{{ getInstanceDetails.rootDeviceName }}",
                "InstanceId": "{{ InstanceId }}",
                "VolumeId": "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}"
             },
             "nextStep": "verifyNewRootVolumeAttached"
          },
          {
             "name": "verifyNewRootVolumeAttached",
             "action": "aws:waitForAwsResourceProperty",
             "timeoutSeconds": 30,
             "inputs": {
                "Service": "ec2",
                "Api": "DescribeVolumes",
                "VolumeIds": [
                   "{{ createNewRootVolumeFromSnapshot.newRootVolumeId }}"
                ],
                "PropertySelector": "$.Volumes[0].Attachments[0].State",
                "DesiredValues": [
                   "attached"
                ]
             },
             "nextStep": "startInstance"
          },
          {
             "name": "startInstance",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "StartInstances",
                "InstanceIds": [
                   "{{ InstanceId }}"
                ]
             }
          }
       ],
       "files": {
            "getSnapshotsByStartTime.py": {
                "checksums": {
                    "sha256": "sampleETagValue"
                }
            }
        }
    }
```

------

# 创建 AMI 和跨区域副本
<a name="automation-document-backup-maintenance-example"></a>

创建实例的 Amazon Machine Image (AMI) 是备份和恢复操作中的常用过程。也可以选择将 AMI 作为灾难恢复架构的一部分复制到另一个 AWS 区域。如果问题需要进行故障转移，则自动执行常见的维护任务可以减少停机时间。AWS Systems Manager自动化操作可帮助您做到这一点。Automation 是 AWS Systems Manager 中的一项工具。

以下示例 AWS Systems Manager 运行手册执行这些操作：
+ 使用 `aws:executeAwsApi` 自动化操作创建 AMI。
+ 使用 `aws:waitForAwsResourceProperty` 自动化操作确认 AMI 的可用性。
+ 使用 `aws:executeScript` 自动化操作将 AMI 复制到目标区域。

------
#### [ YAML ]

```
    ---
    description: Custom Automation Backup and Recovery Example
    schemaVersion: '0.3'
    assumeRole: "{{ AutomationAssumeRole }}"
    parameters:
      AutomationAssumeRole:
        type: String
        description: "(Required) The ARN of the role that allows Automation to perform
          the actions on your behalf. If no role is specified, Systems Manager Automation
          uses your IAM permissions to use this runbook."
        default: ''
      InstanceId:
        type: String
        description: "(Required) The ID of the EC2 instance."
        default: ''
    mainSteps:
    - name: createImage
      action: aws:executeAwsApi
      onFailure: Abort
      inputs:
        Service: ec2
        Api: CreateImage
        InstanceId: "{{ InstanceId }}"
        Name: "Automation Image for {{ InstanceId }}"
        NoReboot: false
      outputs:
        - Name: newImageId
          Selector: "$.ImageId"
          Type: String
      nextStep: verifyImageAvailability
    - name: verifyImageAvailability
      action: aws:waitForAwsResourceProperty
      timeoutSeconds: 600
      inputs:
        Service: ec2
        Api: DescribeImages
        ImageIds:
        - "{{ createImage.newImageId }}"
        PropertySelector: "$.Images[0].State"
        DesiredValues:
        - available
      nextStep: copyImage
    - name: copyImage
      action: aws:executeScript
      timeoutSeconds: 45
      onFailure: Abort
      inputs:
        Runtime: python3.11
        Handler: crossRegionImageCopy
        InputPayload:
          newImageId : "{{ createImage.newImageId }}"
        Script: |-
          def crossRegionImageCopy(events,context):
            import boto3
    
            #Initialize client
            ec2 = boto3.client('ec2', region_name='us-east-1')
            newImageId = events['newImageId']
    
            ec2.copy_image(
              Name='DR Copy for ' + newImageId,
              SourceImageId=newImageId,
              SourceRegion='us-west-2'
            )
```

------
#### [ JSON ]

```
    {
       "description": "Custom Automation Backup and Recovery Example",
       "schemaVersion": "0.3",
       "assumeRole": "{{ AutomationAssumeRole }}",
       "parameters": {
          "AutomationAssumeRole": {
             "type": "String",
             "description": "(Required) The ARN of the role that allows Automation to perform\nthe actions on your behalf. If no role is specified, Systems Manager Automation\nuses your IAM permissions to run this runbook.",
             "default": ""
          },
          "InstanceId": {
             "type": "String",
             "description": "(Required) The ID of the EC2 instance.",
             "default": ""
          }
       },
       "mainSteps": [
          {
             "name": "createImage",
             "action": "aws:executeAwsApi",
             "onFailure": "Abort",
             "inputs": {
                "Service": "ec2",
                "Api": "CreateImage",
                "InstanceId": "{{ InstanceId }}",
                "Name": "Automation Image for {{ InstanceId }}",
                "NoReboot": false
             },
             "outputs": [
                {
                   "Name": "newImageId",
                   "Selector": "$.ImageId",
                   "Type": "String"
                }
             ],
             "nextStep": "verifyImageAvailability"
          },
          {
             "name": "verifyImageAvailability",
             "action": "aws:waitForAwsResourceProperty",
             "timeoutSeconds": 600,
             "inputs": {
                "Service": "ec2",
                "Api": "DescribeImages",
                "ImageIds": [
                   "{{ createImage.newImageId }}"
                ],
                "PropertySelector": "$.Images[0].State",
                "DesiredValues": [
                   "available"
                ]
             },
             "nextStep": "copyImage"
          },
          {
             "name": "copyImage",
             "action": "aws:executeScript",
             "timeoutSeconds": 45,
             "onFailure": "Abort",
             "inputs": {
                "Runtime": "python3.11",
                "Handler": "crossRegionImageCopy",
                "InputPayload": {
                   "newImageId": "{{ createImage.newImageId }}"
                },
                "Attachment": "crossRegionImageCopy.py"
             }
          }
       ],
       "files": {
            "crossRegionImageCopy.py": {
                "checksums": {
                    "sha256": "sampleETagValue"
                }
            }
        }
    }
```

------

# 创建填充 AWS 资源的输入参数
<a name="populating-input-parameters"></a>

Automation 是 Systems Manager 中的一项工具，可在 AWS 管理控制台中填充与您为输入参数定义的资源类型相匹配的 AWS 资源。与资源类型匹配的 AWS 账户 中的资源将显示在下拉列表中供您选择。您可以为 Amazon Elastic Compute Cloud (Amazon EC2) 实例、Amazon Simple Storage Service (Amazon S3) 存储桶以及 AWS Identity and Access Management (IAM) 角色定义输入参数类型。支持的类型定义与用于查找匹配资源的正则表达式如下：
+ `AWS::EC2::Instance::Id` - `^m?i-([a-z0-9]{8}|[a-z0-9]{17})$`
+ `List<AWS::EC2::Instance::Id>` - `^m?i-([a-z0-9]{8}|[a-z0-9]{17})$`
+ `AWS::S3::Bucket::Name` - `^[0-9a-z][a-z0-9\\-\\.]{3,63}$`
+ `List<AWS::S3::Bucket::Name>` - `^[0-9a-z][a-z0-9\\-\\.]{3,63}$`
+ `AWS::IAM::Role::Arn` - `^arn:(aws|aws-cn|aws-us-gov|aws-iso|aws-iso-b):iam::[0-9]{12}:role/.*$`
+ `List<AWS::IAM::Role::Arn>` - `^arn:(aws|aws-cn|aws-us-gov|aws-iso|aws-iso-b):iam::[0-9]{12}:role/.*$`

以下是在运行手册内容中定义的输入参数类型的示例。

------
#### [ YAML ]

```
description: Enables encryption on an Amazon S3 bucket
schemaVersion: '0.3'
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
  BucketName:
    type: 'AWS::S3::Bucket::Name'
    description: (Required) The name of the Amazon S3 bucket you want to encrypt.
  SSEAlgorithm:
    type: String
    description: (Optional) The server-side encryption algorithm to use for the default encryption.
    default: AES256
  AutomationAssumeRole:
    type: 'AWS::IAM::Role::Arn'
    description: (Optional) The Amazon Resource Name (ARN) of the role that allows Automation to perform the actions on your behalf.
    default: ''
mainSteps:
  - name: enableBucketEncryption
    action: 'aws:executeAwsApi'
    inputs:
      Service: s3
      Api: PutBucketEncryption
      Bucket: '{{BucketName}}'
      ServerSideEncryptionConfiguration:
        Rules:
          - ApplyServerSideEncryptionByDefault:
              SSEAlgorithm: '{{SSEAlgorithm}}'
    isEnd: true
```

------
#### [ JSON ]

```
{
   "description": "Enables encryption on an Amazon S3 bucket",
   "schemaVersion": "0.3",
   "assumeRole": "{{ AutomationAssumeRole }}",
   "parameters": {
      "BucketName": {
         "type": "AWS::S3::Bucket::Name",
         "description": "(Required) The name of the Amazon S3 bucket you want to encrypt."
      },
      "SSEAlgorithm": {
         "type": "String",
         "description": "(Optional) The server-side encryption algorithm to use for the default encryption.",
         "default": "AES256"
      },
      "AutomationAssumeRole": {
         "type": "AWS::IAM::Role::Arn",
         "description": "(Optional) The Amazon Resource Name (ARN) of the role that allows Automation to perform the actions on your behalf.",
         "default": ""
      }
   },
   "mainSteps": [
      {
         "name": "enableBucketEncryption",
         "action": "aws:executeAwsApi",
         "inputs": {
            "Service": "s3",
            "Api": "PutBucketEncryption",
            "Bucket": "{{BucketName}}",
            "ServerSideEncryptionConfiguration": {
               "Rules": [
                  {
                     "ApplyServerSideEncryptionByDefault": {
                        "SSEAlgorithm": "{{SSEAlgorithm}}"
                     }
                  }
               ]
            }
         },
         "isEnd": true
      }
   ]
}
```

------

# 正在使用文档生成器创建运行手册
<a name="automation-document-builder"></a>

如果 AWS Systems Manager 公有运行手册不支持您希望在 AWS 资源上执行的操作，您可以创建自己的运行手册。要创建自定义运行手册，您可以手动创建包含相应自动化操作的本地 YAML 或 JSON 格式文件。或者，您也可以使用 Systems Manager Automation 控制台中的文档生成器来构建自定义运行手册。

通过使用文档生成器，您可以将自动化操作添加到自定义运行手册中，并提供所需的参数，而无需使用 JSON 或 YAML 语法。在添加步骤并创建运行手册后，系统将您添加的操作转换为 Systems Manager 可用于运行自动化的 YAML 格式。

自动化文档支持使用 Markdown（一种标记语言），它允许您为运行手册和其中的各个步骤添加 Wiki 样式的描述。有关使用 Markdown 的更多信息，请参阅[在 AWS 中使用 Markdown](https://docs.aws.amazon.com/general/latest/gr/aws-markdown.html)。

## 使用文档生成器创建运行手册
<a name="create-runbook"></a>

**开始前的准备工作**  
我们建议您阅读可在运行手册中使用的各种操作。有关更多信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。

**使用文档生成器创建运行手册**

1. 访问 [https://console.aws.amazon.com/systems-manager/](https://console.aws.amazon.com/systems-manager/)，打开 AWS Systems Manager 控制台。

1. 在导航窗格中，选择**文档**。

1. 选择**创建自动化**。

1. 对于**名称**，为运行手册输入一个描述性名称。

1. 对于**文档描述**，请提供运行手册的 markdown 样式描述。您可以提供使用运行手册的说明、编号的步骤或描述运行手册的任何其他类型的信息。请参阅默认文本以了解设置内容格式的信息。
**提示**  
在**隐藏预览**和**显示预览**之间切换，以便在编写时查看描述内容的效果。

1. （可选）对于 **Assume role (担任角色)**，请输入代表您执行操作的服务角色的名称或 ARN。如果未指定角色，自动化使用运行自动化的用户的访问权限。
**重要**  
对于不归 Amazon 所有并使用 `aws:executeScript` 操作的运行手册，必须指定一个角色。有关信息，请参阅[使用运行手册的权限](automation-document-script-considerations.md#script-permissions)。

1. （可选）对于**输出**，请输入执行该运行手册的自动化的任何输出以供其他进程使用。

   例如，如果文档创建新的 AMI，您可以指定 ["CreateImage.ImageId"]，然后使用该输出以在后续自动化中创建新的实例。

1. （可选）展开**输入参数**部分，然后执行以下操作。

   1. 对于**参数名称**，请输入要创建的运行手册参数的描述性名称。

   1. 对于**类型**，请选择参数的类型，例如 `String` 或 `MapList`。

   1. 对于**必需**，请执行以下操作之一：
      + 如果必须在运行时提供该运行手册参数的值，请选择**是**。
      + 如果该参数不是必需的，请选择**否**，然后（可选）在**默认值**中输入默认参数值。

   1. 对于**描述**，请输入运行手册参数的描述。
**注意**  
要添加更多运行手册参数，请选择**添加参数**。要删除运行手册参数，请选择 **X**（删除）按钮。

1. （可选）展开**目标类型**部分，然后选择一种目标类型以定义可以运行自动化的资源的类型。例如，要在 EC2 实例上使用运行手册，请选择 `/AWS::EC2::Instance`。
**注意**  
如果指定“`/`”值，则运行文档可以在所有类型的资源上运行。有关有效资源类型列表，请参阅 *AWS CloudFormation 用户指南* 中的 [AWS 资源类型参考](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)。

1. （可选）展开**文档标签**部分，然后输入一个或多个标签键值对以应用于运行手册。标签可以轻松标识、划分和搜索资源。

1. 在**步骤 1** 部分中，提供以下信息。
   + 对于**步骤名称**，请输入自动化的第一步的描述性名称。
   + 对于**操作类型**，请选择用于该步骤的操作类型。

     有关可用操作类型的列表和信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)。
   + 对于**描述**，请输入自动化步骤的描述。您可以使用 Markdown 设置文本格式。
   + 根据选定的**操作类型**，在**步骤输入**部分中输入操作类型所需的输入。例如，如果选择了 `aws:approve` 操作，您必须为 `Approvers` 属性指定一个值。

     有关步骤输入字段的信息，请参阅 [Systems Manager 自动化操作参考](automation-actions.md)中的选定操作类型的条目。例如：[`aws:executeStateMachine` - 运行 AWS Step Functions 状态机。](automation-action-executeStateMachine.md)。
   + （可选）对于**其他输入**，请提供运行手册所需的任何其他输入值。可用的输入类型取决于您为步骤选择的操作类型。（请注意，某些操作类型需要使用输入值。）
**注意**  
要添加更多输入，请选择 **Add optional input (添加可选的输入)**。要删除输入，请选择 **X**（删除）按钮。
   + （可选）对于**输出**，请输入执行该步骤的任何输出以供其他进程使用。
**注意**  
**输出**并非适用于所有操作类型。
   + （可选）展开**通用属性**部分，然后指定所有自动化操作的通用操作属性。例如，对于**超时秒数**，您可以提供一个值以指定步骤在停止之前可以运行多长时间（以秒为单位）。

     有关更多信息，请参阅 [所有操作共享的属性](automation-actions.md#automation-common)。
**注意**  
要添加更多步骤，请选择**添加步骤**，然后重复创建步骤的过程。要删除步骤，请选择**删除步骤**。

1. 选择**创建自动化**以保存运行手册。

## 创建运行脚本的运行手册
<a name="create-runbook-scripts"></a>

以下过程展示了如何在 AWS Systems Manager Automation 控制台中使用文档生成器创建运行脚本的自定义运行手册。

您创建的运行手册的第一步运行一个脚本以启动 Amazon Elastic Compute Cloud (Amazon EC2) 实例。第二步运行另一个脚本来监控要更改为 `ok` 的实例状态检查。然后，报告自动化的整体状态 `Success`。

**开始前的准备工作**  
确保您已完成以下步骤：
+ 确认您具有管理员权限，或为您授予了相应的权限以在 AWS Identity and Access Management (IAM) 中访问 Systems Manager。

  有关信息，请参阅[验证用户的运行手册访问权限](automation-setup.md#automation-setup-user-access)。
+ 确认在您的 AWS 账户 账户中具有自动化的 IAM 服务角色（也称为*担任角色*）。该角色是必需的，因为该演练使用 `aws:executeScript` 操作。

  有关创建此角色的信息，请参阅 [为自动化配置服务角色（担任角色）访问权限](automation-setup.md#automation-setup-configure-role)。

  有关用于运行 `aws:executeScript` 的 IAM 服务角色要求的信息，请参阅 [使用运行手册的权限](automation-document-script-considerations.md#script-permissions)。
+ 确认您具有启动 EC2 实例的权限。

  有关更多信息，请参阅《Amazon EC2 用户指南》**中的 [IAM 与 Amazon EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/UsingIAM.html#intro-to-iam)。

**创建使用文档生成器运行脚本的自定义运行手册**

1. 访问 [https://console.aws.amazon.com/systems-manager/](https://console.aws.amazon.com/systems-manager/)，打开 AWS Systems Manager 控制台。

1. 在导航窗格中，选择**文档**。

1. 选择**创建自动化**。

1. 对于**名称**，请为运行手册键入以下描述性名称：**LaunchInstanceAndCheckStatus**。

1. （可选）对于**文档描述)**，请使用 Markdown 将默认文本替换为该运行手册的描述。示例如下：

   ```
   ##Title: LaunchInstanceAndCheckState
       -----
       **Purpose**: This runbook first launches an EC2 instance using the AMI ID provided in the parameter ```imageId```. The second step of this runbook continuously checks the instance status check value for the launched instance until the status ```ok``` is returned.
       
       ##Parameters:
       -----
       Name | Type | Description | Default Value
       ------------- | ------------- | ------------- | -------------
       assumeRole | String | (Optional) The ARN of the role that allows Automation to perform the actions on your behalf. | -
       imageId  | String | (Optional) The AMI ID to use for launching the instance. The default value uses the latest Amazon Linux 2023 AMI ID available. | {{ ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64 }}
   ```

1. 对于**担任角色**，请使用 **arn:aws:iam::111122223333:role/AutomationServiceRole** 格式输入自动化的自动化 IAM 服务角色（担任角色）的 ARN。使用您的 AWS 账户 ID 替代 111122223333。

   您指定的角色用于提供启动自动化执行所需的权限。
**重要**  
对于不归 Amazon 所有并使用 `aws:executeScript` 操作的运行手册，必须指定一个角色。有关信息，请参阅[使用运行手册的权限](automation-document-script-considerations.md#script-permissions)。

1. 展开**输入参数**，然后执行以下操作。

   1. 对于**参数名称**，请输入 **imageId**。

   1. 对于**类型**，选择 **String**。

   1. 对于**必需**，请选择 `No`。

   1. 对于**默认值**，请输入以下内容。

      ```
      {{ ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-arm64 }}
      ```
**注意**  
该值使用最新的 Amazon Linux 2023 Amazon Machine Image（AMI）ID 启动 Amazon EC2 实例。如果要使用不同的 AMI，请将该值替换为您的 AMI ID。

   1. 对于**描述**，请输入以下内容。

      ```
      (Optional) The AMI ID to use for launching the instance. The default value uses the latest released Amazon Linux 2023 AMI ID.
      ```

1. 选择**添加参数)**以创建第二个参数 **tagValue**，然后输入以下内容。

   1. 对于**参数名称**，请输入 **tagValue**。

   1. 对于**类型**，选择 **String**。

   1. 对于**必需**，请选择 `No`。

   1. 对于**默认值**，请输入 **LaunchedBySsmAutomation**。这会将标签键对值 `Name:LaunchedBySsmAutomation` 添加到实例中。

   1. 对于**描述**，请输入以下内容。

      ```
      (Optional) The tag value to add to the instance. The default value is LaunchedBySsmAutomation.
      ```

1. 选择**添加参数** 以创建第三个参数 **instanceType**，然后输入以下信息。

   1. 对于**参数名称**，请输入 **instanceType**。

   1. 对于**类型**，选择 **String**。

   1. 对于**必需**，请选择 `No`。

   1. 对于**默认值**，请输入 **t2.micro**。

   1. 对于**参数描述**，请输入以下内容。

      ```
      (Optional) The instance type to use for the instance. The default value is t2.micro.
      ```

1. 展开**目标类型**，然后选择 **"/"**。

1. （可选）展开**文档标签**以将资源标签应用于运行手册。为**标签键** 输入 **Purpose**，并为**标签值**输入 **LaunchInstanceAndCheckState**。

1. 在**步骤 1** 部分中，完成以下步骤。

   1. 对于**步骤名称**，为自动化的第一步输入以下描述性步骤名称：**LaunchEc2Instance**。

   1. 对于**操作类型**，请选择**运行脚本)** (**aws:executeScript**)。

   1. 对于**描述**，请输入自动化步骤的描述，例如以下内容。

      ```
      **About This Step**
          
          This step first launches an EC2 instance using the ```aws:executeScript``` action and the provided script.
      ```

   1. 展开**输入**。

   1. 对于**运行时**，请选择运行提供的脚本所使用的运行时语言。

   1. 对于**处理程序**，输入 **launch\$1instance**。这是在以下脚本中声明的函数名称。
**注意**  
这对于 PowerShell 不是必需的。

   1. 对于**脚本**，请将默认内容替换为以下内容。请确保将脚本与相应的运行时值匹配。

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

      ```
      def launch_instance(events, context):
            import boto3
            ec2 = boto3.client('ec2')
          
            image_id = events['image_id']
            tag_value = events['tag_value']
            instance_type = events['instance_type']
          
            tag_config = {'ResourceType': 'instance', 'Tags': [{'Key':'Name', 'Value':tag_value}]}
          
            res = ec2.run_instances(ImageId=image_id, InstanceType=instance_type, MaxCount=1, MinCount=1, TagSpecifications=[tag_config])
          
            instance_id = res['Instances'][0]['InstanceId']
          
            print('[INFO] 1 EC2 instance is successfully launched', instance_id)
          
            return { 'InstanceId' : instance_id }
      ```

------
#### [ PowerShell ]

      ```
      Install-Module AWS.Tools.EC2 -Force
          Import-Module AWS.Tools.EC2
          
          $payload = $env:InputPayload | ConvertFrom-Json
          
          $imageid = $payload.image_id
          
          $tagvalue = $payload.tag_value
          
          $instanceType = $payload.instance_type
          
          $type = New-Object Amazon.EC2.InstanceType -ArgumentList $instanceType
          
          $resource = New-Object Amazon.EC2.ResourceType -ArgumentList 'instance'
          
          $tag = @{Key='Name';Value=$tagValue}
          
          $tagSpecs = New-Object Amazon.EC2.Model.TagSpecification
          
          $tagSpecs.ResourceType = $resource
          
          $tagSpecs.Tags.Add($tag)
          
          $res = New-EC2Instance -ImageId $imageId -MinCount 1 -MaxCount 1 -InstanceType $type -TagSpecification $tagSpecs
          
          return @{'InstanceId'=$res.Instances.InstanceId}
      ```

------

   1. 展开**其他输入**。

   1. 对于**输入名称**，请选择 **InputPayload**。对于**输入值**，请输入以下 YAML 数据。

      ```
      image_id: "{{ imageId }}"
          tag_value: "{{ tagValue }}"
          instance_type: "{{ instanceType }}"
      ```

1. 展开**输出**，然后执行以下操作：
   + 对于**名称**，请输入 **payload**。
   + 对于**选择器**，请输入 **\$1.Payload**。
   + 对于**类型**，选择 `StringMap`。

1. 选择**添加步骤**将第二步添加到运行手册中。第二步查询在步骤 1 中启动的实例的状态，并等到返回的状态为 `ok`。

1. 在 **Step 2 (步骤 2)** 部分中，执行以下操作。

   1. 对于**步骤名称**，请为自动化的第二步输入以下描述性名称：**WaitForInstanceStatusOk**。

   1. 对于**操作类型**，请选择**运行脚本** (**aws:executeScript**)。

   1. 对于**描述**，请输入自动化步骤的描述，例如以下内容。

      ```
      **About This Step**
          
          The script continuously polls the instance status check value for the instance launched in Step 1 until the ```ok``` status is returned.
      ```

   1. 对于**运行时**，选择用于执行提供的脚本的运行时语言。

   1. 对于**处理程序**，输入 **poll\$1instance**。这是在以下脚本中声明的函数名称。
**注意**  
这对于 PowerShell 不是必需的。

   1. 对于**脚本**，请将默认内容替换为以下内容。请确保将脚本与相应的运行时值匹配。

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

      ```
      def poll_instance(events, context):
            import boto3
            import time
          
            ec2 = boto3.client('ec2')
          
            instance_id = events['InstanceId']
          
            print('[INFO] Waiting for instance status check to report ok', instance_id)
          
            instance_status = "null"
          
            while True:
              res = ec2.describe_instance_status(InstanceIds=[instance_id])
          
              if len(res['InstanceStatuses']) == 0:
                print("Instance status information is not available yet")
                time.sleep(5)
                continue
          
              instance_status = res['InstanceStatuses'][0]['InstanceStatus']['Status']
          
              print('[INFO] Polling to get status of the instance', instance_status)
          
              if instance_status == 'ok':
                break
          
              time.sleep(10)
          
            return {'Status': instance_status, 'InstanceId': instance_id}
      ```

------
#### [ PowerShell ]

      ```
          Install-Module AWS.Tools.EC2 -Force
          
          $inputPayload = $env:InputPayload | ConvertFrom-Json
          
          $instanceId = $inputPayload.payload.InstanceId
          
          $status = Get-EC2InstanceStatus -InstanceId $instanceId
          
          while ($status.Status.Status -ne 'ok'){
             Write-Host 'Polling get status of the instance', $instanceId
          
             Start-Sleep -Seconds 5
          
             $status = Get-EC2InstanceStatus -InstanceId $instanceId
          }
          
          return @{Status = $status.Status.Status; InstanceId = $instanceId}
      ```

------

   1. 展开**其他输入**。

   1. 对于**输入名称**，请选择 **InputPayload**。对于**输入值**，请输入以下内容：

      ```
      {{ LaunchEc2Instance.payload }}
      ```

1. 选择**创建自动化**以保存运行手册。

# 在运行手册中使用脚本
<a name="automation-document-script-considerations"></a>

自动化运行手册支持将脚本作为自动化的一部分运行。Automation 是 AWS Systems Manager 中的一项工具。通过使用运行手册，您可以直接在 AWS 中运行脚本，而无需创建单独的计算环境来运行脚本。由于运行手册可以将脚本步骤与其他自动化步骤类型（例如批准）一起运行，因此您能够在紧急或不明确的情况下手动进行干预。您可以把来自运行手册中 `aws:executeScript` 操作的输出添加到 Amazon CloudWatch Logs。有关更多信息，请参阅 [使用 CloudWatch Logs 记录自动化操作输出](automation-action-logging.md)。

## 使用运行手册的权限
<a name="script-permissions"></a>

要使用运行手册，Systems Manager 必须使用 AWS Identity and Access Management (IAM) 角色的权限。自动化用于确定使用哪个角色的权限的方法取决于一些因素，以及步骤是否使用 `aws:executeScript` 操作。

对于不使用 `aws:executeScript` 的运行手册，自动化将使用以下两种权限来源之一：
+ 在运行手册中指定或作为参数传递的 IAM 服务角色或担任角色的权限。
+ 如果未指定 IAM 服务角色，则为启动自动化的用户的权限。

不过，如果运行手册中的步骤包含 `aws:executeScript` 操作，并且为该操作指定的 Python 或 PowerShell 脚本调用任何 AWS API 操作，则始终需要使用 IAM 服务角色（担任角色）。自动化按以下顺序检查该角色：
+ 在运行手册中指定或作为参数传递的 IAM 服务角色或担任角色的权限。
+ 如果找不到任何角色，自动化尝试在没有任何权限的情况下运行为 `aws:executeScript` 指定的 Python 或 PowerShell 脚本。如果脚本调用 AWS API 操作（例如，Amazon EC2 `CreateImage` 操作）或者尝试对 AWS 资源（例如 EC2 实例）执行操作，则包含脚本的步骤将失败，并且 Systems Manager 返回一条错误消息以报告失败。

## 将脚本添加到运行手册
<a name="adding-scripts"></a>

您可以通过内联方式将脚本作为运行手册中的步骤的一部分，从而将其添加到运行手册中。您还可以从本地计算机中上传脚本或指定脚本所在的 Amazon Simple Storage Service (Amazon S3) 存储桶，以将脚本附加到运行手册。在运行脚本的步骤完成后，脚本的输出将作为 JSON 对象提供，您可以将其作为运行手册中的后续步骤的输入。有关 `aws:executeScript` 操作以及如何使用脚本附件的更多信息，请参阅 [`aws:executeScript` - 运行脚本](automation-action-executeScript.md)。

## 运行手册的脚本限制
<a name="script-constraints"></a>

运行手册强制执行 5 个文件附加文件的限制。脚本可以采用 Python 脚本 (.py) 或 PowerShell Core 脚本 (.ps1) 的形式，也可以作为 .zip 文件中的内容附加。

# 在运行手册中使用条件语句
<a name="automation-branch-condition"></a>

默认情况下，在运行手册的 `mainSteps` 部分中定义的步骤将按先后顺序运行。在一个操作完成后，`mainSteps` 部分中指定的下一个操作将开始。此外，如果一个操作无法运行，则整个自动化将失败（默认情况下）。可以使用本节所述的 `aws:branch` 自动化操作和运行手册选项来创建执行*条件分支*的自动化。也就是说，您可以创建在评估不同选项后跳转到不同步骤或在某个步骤完成时动态响应更改的自动化。以下是可用于创建动态自动化的选项的列表：
+ **`aws:branch`**：此自动化操作让您能够创建一个动态自动化，该自动化可以在一个步骤中评估多个选项，然后根据评估结果跳转到运行手册中的不同步骤。
+ **`nextStep`**：此选项指定在成功完成一个步骤后，接下来处理自动化中的哪个步骤。
+ **`isEnd`**：此选项在特定步骤结束时停止自动化。该选项的默认值为 false。
+ **`isCritical`**：此选项将一个步骤指定为成功完成自动化的关键步骤。如果具有此分派的步骤失败，自动化会将自动化的最终状态报告为 `Failed`。该选项的默认值为 `true`。
+ **`onFailure`**：此选项指示自动化在失败时是应中止、继续还是转到其他步骤。该选项的默认值为 abort。

下一节介绍 `aws:branch` 自动化操作。有关 `nextStep`、`isEnd`、`isCritical` 和 `onFailure` 选项的更多信息，请参阅 [示例 `aws:branch` 运行手册](#branch-runbook-examples)。

## 使用 `aws:branch` 操作
<a name="branch-action-explained"></a>

`aws:branch` 操作提供适用于自动化的大部分动态条件分支选项。如前所述，此操作让自动化能够在一个步骤中评估多个条件，然后根据评估结果跳转到新的步骤。`aws:branch` 操作的功能类似于编程中的 `IF-ELIF-ELSE` 语句。

下面是一个 `aws:branch` 步骤的 YAML 示例：

```
- name: ChooseOSforCommands
  action: aws:branch
  inputs:
    Choices:
    - NextStep: runPowerShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Windows
    - NextStep: runShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Linux
    Default:
      PostProcessing
```

在为步骤指定 `aws:branch` 操作时，请指定自动化必须评估的 `Choices`。自动化可以根据在运行手册的 `Parameters` 部分中指定的参数值来评估 `Choices`。自动化也可以基于上一步的输出来评估 `Choices`。

自动化使用布尔表达式评估每个选择。如果评估确定第一个选择为 `true`，自动化跳转到为该选择指定的步骤。如果评估确定第一个选择为 `false`，自动化程评估下一个选择。如果您的步骤包含三个或更多的 `Choices`，自动化按顺序评估每个选择，直到某个选择的评估结果为 `true`。然后，自动化跳转到为结果为 `true` 的选择指定的步骤。

如果所有 `Choices` 都为 `true`，则工作流程检查该步骤是否包含 `Default` 值。`Default` 值定义当所有选择都为 `true` 时工作流程应跳转到的步骤。如果没有为该步骤指定 `Default` 值，自动化处理文档中的下一个步骤。

以下是 YAML 的 `aws:branch` 步骤，命名为 **chooseOSfromParameter**。此步骤包含两个 `Choices`：(`NextStep: runWindowsCommand`) 和 (`NextStep: runLinuxCommand`)。自动化评估这些 `Choices`，以确定应为适当的操作系统运行哪个命令。每个选择的 `Variable` 都使用 `{{OSName}}`，这是运行手册作者在运行手册的 `Parameters` 部分中定义的参数。

```
mainSteps:
- name: chooseOSfromParameter
  action: aws:branch
  inputs:
    Choices:
    - NextStep: runWindowsCommand
      Variable: "{{OSName}}"
      StringEquals: Windows
    - NextStep: runLinuxCommand
      Variable: "{{OSName}}"
      StringEquals: Linux
```

以下是 YAML 的 `aws:branch` 步骤，命名为 **chooseOSfromOutput**。此步骤包含两个 `Choices`：(`NextStep: runPowerShellCommand`) 和 (`NextStep: runShellCommand`)。自动化评估这些 `Choices`，以确定应为适当的操作系统运行哪个命令。每个选择的 `Variable` 都使用 `{{GetInstance.platform}}`，这是文档中上一步的输出。此示例还包含一个名为 `Default` 的选项。如果自动化评估两个的结果都为 `Choices`，而且两个选择均为 `true`，自动化跳转到名为 `PostProcessing` 的步骤。

```
mainSteps:
- name: chooseOSfromOutput
  action: aws:branch
  inputs:
    Choices:
    - NextStep: runPowerShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Windows
    - NextStep: runShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Linux
    Default:
      PostProcessing
```

### 在运行手册中创建 `aws:branch` 步骤
<a name="create-branch-action"></a>

在运行手册中创建 `aws:branch` 步骤时，请定义自动化应对其进行评估的 `Choices` 以确定自动化应跳转到哪个步骤。如前所述，`Choices` 使用布尔表达式进行评估。每个选择都必须定义以下选项：
+ **NextStep**：当指定的选择为 `true` 时，运行手册要处理的下一个步骤。
+ **变量**：指定在运行手册的 `Parameters` 部分中定义的参数的名称（在 `Variables` 部分中定义的参数），或指定上一步的输出对象。

  使用以下格式指定变量值。

  `Variable: "{{variable name}}"`

  使用以下格式指定参数值。

  `Variable: "{{parameter name}}"`

  使用以下格式指定输出对象变量：

  `Variable: "{{previousStepName.outputName}}"`
**注意**  
下一节[关于创建输出变量](#branch-action-output)更详细地介绍如何创建输出变量。
+ **Operation**：用于评估选择的标准，例如 `StringEquals: Linux`。`aws:branch` 操作支持以下运算：

**字符串运算**
  + 字符串等于
  + EqualsIgnoreCase
  + StartsWith
  + EndsWith
  + 包含

**数值运算**
  + NumericEquals
  + NumericGreater
  + NumericLesser
  + NumericGreaterOrEquals
  + NumericLesser
  + NumericLesserOrEquals

**布尔运算**
  + BooleanEquals
**重要**  
创建运行手册时，系统将验证运行手册中的每个操作。在尝试创建运行手册时，如果某个操作不受支持，系统会返回错误。
+ **Default**：指定当所有 `Choices` 都为 `true` 时自动化应跳转到的回退步骤。
**注意**  
如果不想指定 `Default` 值，则可以指定 `isEnd` 选项。如果所有 `Choices` 都为 `true` 并且未指定 `Default` 值，自动化在此步骤结束时停止。

使用以下模板可以帮助您在运行手册中构建 `aws:branch` 步骤：将每个*示例资源占位符*替换为您自己的信息。

------
#### [ YAML ]

```
mainSteps:
- name: step name
  action: aws:branch
  inputs:
    Choices:
    - NextStep: step to jump to if evaluation for this choice is true
      Variable: "{{parameter name or output from previous step}}"
      Operation type: Operation value
    - NextStep: step to jump to if evaluation for this choice is true
      Variable: "{{parameter name or output from previous step}}"
      Operation type: Operation value
    Default:
      step to jump to if all choices are false
```

------
#### [ JSON ]

```
{
   "mainSteps":[
      {
         "name":"a name for the step",
         "action":"aws:branch",
         "inputs":{
            "Choices":[
               {
                  "NextStep":"step to jump to if evaluation for this choice is true",
                  "Variable":"{{parameter name or output from previous step}}",
                  "Operation type":"Operation value"
               },
               {
                  "NextStep":"step to jump to if evaluation for this choice is true",
                  "Variable":"{{parameter name or output from previous step}}",
                  "Operation type":"Operation value"
               }
            ],
            "Default":"step to jump to if all choices are false"
         }
      }
   ]
}
```

------

#### 关于创建输出变量
<a name="branch-action-output"></a>

要创建引用上一步输出的 `aws:branch` 选择，您需要标识上一步的名称和输出字段的名称。然后，使用以下格式组合步骤和字段的名称：

`Variable: "{{previousStepName.outputName}}"`

例如，以下示例中的第一个步骤名为 `GetInstance`。然后，在 `outputs` 下，有一个名为 `platform` 的字段。在第二个步骤 (`ChooseOSforCommands`) 中，作者希望将平台字段的输出引用为变量。要创建变量，只需将步骤名称 (GetInstance) 与输出字段名称 (platform) 组合在一起即可创建 `Variable: "{{GetInstance.platform}}"`。

```
mainSteps:
- Name: GetInstance
  action: aws:executeAwsApi
  inputs:
    Service: ssm
    Api: DescribeInstanceInformation
    Filters:
    - Key: InstanceIds
      Values: ["{{ InstanceId }}"]
  outputs:
  - Name: myInstance
    Selector: "$.InstanceInformationList[0].InstanceId"
    Type: String
  - Name: platform
    Selector: "$.InstanceInformationList[0].PlatformType"
    Type: String
- name: ChooseOSforCommands
  action: aws:branch
  inputs:
    Choices:
    - NextStep: runPowerShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Windows
    - NextStep: runShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Linux
    Default:
      Sleep
```

这是一个示例，展示了从上一步和输出创建 *"Variable": "\$1\$1 describeInstance.Platform \$1\$1"* 的方法。

```
- name: describeInstance
  action: aws:executeAwsApi
  onFailure: Abort
  inputs:
    Service: ec2
    Api: DescribeInstances
    InstanceIds:
    - "{{ InstanceId }}"
  outputs:
  - Name: Platform
    Selector: "$.Reservations[0].Instances[0].Platform"
    Type: String
  nextStep: branchOnInstancePlatform
- name: branchOnInstancePlatform
  action: aws:branch
  inputs:
    Choices:
    - NextStep: runEC2RescueForWindows
      Variable: "{{ describeInstance.Platform }}"
      StringEquals: windows
    Default: runEC2RescueForLinux
```

### 示例 `aws:branch` 运行手册
<a name="branch-runbook-examples"></a>

下面是一些使用 `aws:branch` 的示例运行手册。

**示例 1：使用 `aws:branch` 和输出变量基于操作系统类型运行命令**

在此示例的第一步（`GetInstance`）中，运行手册作者使用 `aws:executeAwsApi` 操作来调用 `ssm` `DescribeInstanceInformation` API 操作。作者使用此操作确定实例使用的操作系统的类型。`aws:executeAwsApi` 操作输出实例 ID 和平台类型。

在第二个步骤 (`ChooseOSforCommands`) 中，作者使用了 `aws:branch` 操作和两个 `Choices`：(`NextStep: runPowerShellCommand`) 和 (`NextStep: runShellCommand`)。自动化使用上一步 (`Variable: "{{GetInstance.platform}}"`) 的输出评估实例的操作系统。自动化跳转到指定操作系统的步骤。

```
---
schemaVersion: '0.3'
assumeRole: "{{AutomationAssumeRole}}"
parameters:
  AutomationAssumeRole:
    default: ""
    type: String
mainSteps:
- name: GetInstance
  action: aws:executeAwsApi
  inputs:
    Service: ssm
    Api: DescribeInstanceInformation
  outputs:
  - Name: myInstance
    Selector: "$.InstanceInformationList[0].InstanceId"
    Type: String
  - Name: platform
    Selector: "$.InstanceInformationList[0].PlatformType"
    Type: String
- name: ChooseOSforCommands
  action: aws:branch
  inputs:
    Choices:
    - NextStep: runPowerShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Windows
    - NextStep: runShellCommand
      Variable: "{{GetInstance.platform}}"
      StringEquals: Linux
    Default:
      Sleep
- name: runShellCommand
  action: aws:runCommand
  inputs:
    DocumentName: AWS-RunShellScript
    InstanceIds:
    - "{{GetInstance.myInstance}}"
    Parameters:
      commands:
      - ls
  isEnd: true
- name: runPowerShellCommand
  action: aws:runCommand
  inputs:
    DocumentName: AWS-RunPowerShellScript
    InstanceIds:
    - "{{GetInstance.myInstance}}"
    Parameters:
      commands:
      - ls
  isEnd: true
- name: Sleep
  action: aws:sleep
  inputs:
    Duration: PT3S
```

**示例 2：使用 `aws:branch` 和参数变量基于操作系统类型运行命令**

运行手册作者在 `parameters` 部分的运行手册开头定义了几个参数选项。一个参数名为 `OperatingSystemName`。在第一个步骤 (`ChooseOS`) 中，作者使用了 `aws:branch` 操作和两个 `Choices`：(`NextStep: runWindowsCommand`) 和 (`NextStep: runLinuxCommand`)。这些 `Choices` 的变量引用在参数部分 (`Variable: "{{OperatingSystemName}}"`) 中指定的参数选项。当用户运行此自动化时，他们在运行时为 `OperatingSystemName` 指定值。自动化在 `Choices` 评估期间使用运行时参数。自动化基于为 `OperatingSystemName` 指定的运行时参数跳转到指定操作系统的步骤。

```
---
schemaVersion: '0.3'
assumeRole: "{{AutomationAssumeRole}}"
parameters:
  AutomationAssumeRole:
    default: ""
    type: String
  OperatingSystemName:
    type: String
  LinuxInstanceId:
    type: String
  WindowsInstanceId:
    type: String
mainSteps:
- name: ChooseOS
  action: aws:branch
  inputs:
    Choices:
    - NextStep: runWindowsCommand
      Variable: "{{OperatingSystemName}}"
      StringEquals: windows
    - NextStep: runLinuxCommand
      Variable: "{{OperatingSystemName}}"
      StringEquals: linux
    Default:
      Sleep
- name: runLinuxCommand
  action: aws:runCommand
  inputs:
    DocumentName: "AWS-RunShellScript"
    InstanceIds:
    - "{{LinuxInstanceId}}"
    Parameters:
      commands:
      - ls
  isEnd: true
- name: runWindowsCommand
  action: aws:runCommand
  inputs:
    DocumentName: "AWS-RunPowerShellScript"
    InstanceIds:
    - "{{WindowsInstanceId}}"
    Parameters:
      commands:
      - date
  isEnd: true
- name: Sleep
  action: aws:sleep
  inputs:
    Duration: PT3S
```

### 使用运算符创建复杂的分支自动化
<a name="branch-operators"></a>

您可以在 `aws:branch` 步骤中使用 `And`、`Or` 和 `Not` 运算符来创建复杂的分支自动化。

**“And”运算符**  
如果某个选择的多个变量需要为 `true`，可以使用 `And` 运算符。在下面的示例中，第一个选择评估实例是否 `running` 并且使用的是 `Windows` 操作系统。如果这*两个*变量的评估结果都为真，自动化跳转到 `runPowerShellCommand` 步骤。如果一个或多个变量为 `false`，自动化评估第二个选择的变量。

```
mainSteps:
- name: switch2
  action: aws:branch
  inputs:
    Choices:
    - And:
      - Variable: "{{GetInstance.pingStatus}}"
        StringEquals: running
      - Variable: "{{GetInstance.platform}}"
        StringEquals: Windows
      NextStep: runPowerShellCommand

    - And:
      - Variable: "{{GetInstance.pingStatus}}"
        StringEquals: running
      - Variable: "{{GetInstance.platform}}"
        StringEquals: Linux
      NextStep: runShellCommand
    Default:
      sleep3
```

**“Or”运算符**  
如果*某个*选择的多个变量需要为真，可以使用 `Or` 运算符。在下面的示例中，第一个选择评估参数字符串是不是 `Windows` 和 AWS Lambda 步骤的输出是不是真。如果*任意一个*变量的评估结果都为真，自动化跳转到 `RunPowerShellCommand` 步骤。如果两个变量都为假，自动化评估第二个选择的变量。

```
- Or:
  - Variable: "{{parameter1}}"
    StringEquals: Windows
  - Variable: "{{BooleanParam1}}"
    BooleanEquals: true
  NextStep: RunPowershellCommand
- Or:
  - Variable: "{{parameter2}}"
    StringEquals: Linux
  - Variable: "{{BooleanParam2}}"
    BooleanEquals: true
  NextStep: RunShellScript
```

**“Not”运算符**  
当变量*不*为真时，如果您想要跳转到定义的步骤，则使用 `Not` 运算符。在下面的示例中，第一个选择评估参数字符串是不是 `Not Linux`。如果变量的评估结果不为 Linux，自动化跳转到 `sleep2` 步骤。如果第一个选择的评估结果*为* Linux，自动化评估下一个选择。

```
mainSteps:
- name: switch
  action: aws:branch
  inputs:
    Choices:
    - NextStep: sleep2
      Not:
        Variable: "{{testParam}}"
        StringEquals: Linux
    - NextStep: sleep1
      Variable: "{{testParam}}"
      StringEquals: Windows
    Default:
      sleep3
```

## 如何使用条件选项的示例
<a name="conditional-examples"></a>

本部分包括有关如何使用运行手册中的动态选项的其他示例。本部分中的每个示例均扩展以下运行手册。该运行手册包含两个操作。第一个操作名为 `InstallMsiPackage`。它使用 `aws:runCommand` 操作将应用程序安装到 Windows Server 实例上。第二个操作名为 `TestInstall`。它使用 `aws:invokeLambdaFunction` 操作在成功安装应用程序后测试该应用程序。步骤 1 指定 `onFailure: Abort`。这意味着，如果应用程序未成功安装，自动化会在进入步骤 2 前停止。

**示例 1：包含两个线性操作的运行手册**

```
---
schemaVersion: '0.3'
description: Install MSI package and run validation.
assumeRole: "{{automationAssumeRole}}"
parameters:
  automationAssumeRole:
    type: String
    description: "(Required) Assume role."
  packageName:
    type: String
    description: "(Required) MSI package to be installed."
  instanceIds:
    type: String
    description: "(Required) Comma separated list of instances."
mainSteps:
- name: InstallMsiPackage
  action: aws:runCommand
  maxAttempts: 2
  onFailure: Abort
  inputs:
    InstanceIds:
    - "{{instanceIds}}"
    DocumentName: AWS-RunPowerShellScript
    Parameters:
      commands:
      - msiexec /i {{packageName}}
- name: TestInstall
  action: aws:invokeLambdaFunction
  maxAttempts: 1
  timeoutSeconds: 500
  inputs:
    FunctionName: TestLambdaFunction
...
```

**使用 `onFailure` 选项创建跳转到不同步骤的动态自动化**

下面的示例使用 `onFailure: step:step name`、`nextStep` 和 `isEnd` 选项创建一个动态自动化。在此示例中，如果 `InstallMsiPackage` 操作失败，自动化将跳转到名为 *PostFailure* (`onFailure: step:PostFailure`) 的操作以运行 AWS Lambda 函数，从而在安装失败时执行某个操作。如果安装成功，自动化将跳转到 TestInstall 操作 (`nextStep: TestInstall`)。`TestInstall` 和 `PostFailure` 步骤都使用 `isEnd` 选项 (`isEnd: true`)，以便自动化在任一步骤完成时结束。

**注意**  
可以选择在 `mainSteps` 部分的最后一步中使用 `isEnd` 选项。如果最后一个步骤未跳转到其他步骤，自动化会在最后一步中运行操作后停止。

**示例 2：跳转到不同步骤的动态自动化**

```
mainSteps
- name: InstallMsiPackage
  action: aws:runCommand
  onFailure: step:PostFailure
  maxAttempts: 2
  inputs:
    InstanceIds:
    - "{{instanceIds}}"
    DocumentName: AWS-RunPowerShellScript
    Parameters:
      commands:
      - msiexec /i {{packageName}}
  nextStep: TestInstall
- name: TestInstall
  action: aws:invokeLambdaFunction
  maxAttempts: 1
  timeoutSeconds: 500
  inputs:
    FunctionName: TestLambdaFunction
  isEnd: true
- name: PostFailure
  action: aws:invokeLambdaFunction
  maxAttempts: 1
  timeoutSeconds: 500
  inputs:
    FunctionName: PostFailureRecoveryLambdaFunction
  isEnd: true
...
```

**注意**  
在处理运行手册之前，系统将验证运行手册不会创建无限循环。如果检测到无限循环，自动化将返回一个错误和一个显示哪些步骤创建循环的循环跟踪。

**创建定义关键步骤的动态自动化**

您可以指定一个对于自动化的整体成功很重要的步骤。如果关键步骤失败，自动化会将自动化状态报告为 `Failed`，即使已成功运行一个或多个步骤也是如此。在以下示例中，用户在 *InstallMsiPackage* 步骤失败 (`onFailure: step:VerifyDependencies`) 时标识 *VerifyDependencies* 步骤。用户指定 `InstallMsiPackage` 步骤为非关键步骤 (`isCritical: false`)。在此示例中，如果应用程序安装失败，自动化处理 `VerifyDependencies` 步骤以确定是否因一个或多个依赖项缺失而导致应用程序安装失败。

**示例 3：定义自动化的关键步骤**

```
---
name: InstallMsiPackage
action: aws:runCommand
onFailure: step:VerifyDependencies
isCritical: false
maxAttempts: 2
inputs:
  InstanceIds:
  - "{{instanceIds}}"
  DocumentName: AWS-RunPowerShellScript
  Parameters:
    commands:
    - msiexec /i {{packageName}}
nextStep: TestPackage
...
```

# 使用操作输出作为输入
<a name="automation-action-outputs-inputs"></a>

多个自动化操作会返回预定义的输出。您可以使用格式 `{{stepName.outputName}}` 将这些输出作为输入传递到运行手册的后续步骤。您还可以在运行手册中为自动化操作定义自定义输出。让您可以运行脚本，或调用其他 AWS 服务 的 API 操作一次，以便您在后续操作中重复使用这些值作为输入。运行手册中的参数类型是静态的。这意味着参数类型在定义后无法更改。要定义步骤输出，请提供以下字段：
+ 名称：（必需）输出名称，用于在后续步骤中引用输出值。
+ 选择器：（必需）用于确定输出值的 JSONPath 表达式。
+ 类型：（可选）选择器字段返回的值的数据类型。有效的类型值为 `String`、`Integer`、`Boolean`、`StringList`、`StringMap`、`MapList`。默认值为 `String`。

如果输出的值与您指定的数据类型不匹配，自动化会尝试转换该数据类型。例如，如果返回的值是 `Integer`，但指定的 `Type` 是 `String`，则最终输出值为 `String` 值。支持以下类型转换：
+ `String` 值可以转换为 `StringList`、`Integer` 和 `Boolean`。
+ `Integer` 值可以转换为 `String` 和 `StringList`。
+ `Boolean` 值可以转换为 `String` 和 `StringList`。
+ 包含一个元素的 `StringList`、`IntegerList` 或 `BooleanList` 值可以转换为 `String`、`Integer` 或 `Boolean`。

将参数或输出与自动化操作一起使用时，不能在操作的输入中动态更改数据类型。

这份示例运行手册演示了如何定义操作输出，并将该值引用为后续操作的输入。运行手册执行以下操作：
+ 使用 `aws:executeAwsApi` 操作调用 Amazon EC2 DescribeImages API 操作来获取特定 Windows Server 2016 AMI 的名称。它将映像 ID 输出为 `ImageId`。
+ 使用 `aws:executeAwsApi` 操作调用 Amazon EC2 RunInstances API 操作来启动一个实例，它使用上一步中的 `ImageId`。它将实例 ID 输出为 `InstanceId`。
+ 使用 ` aws:waitForAwsResourceProperty` 操作轮询 Amazon EC2 DescribeInstanceStatus API 操作来等待实例进入 `running` 状态。此操作的超时时间为 60 秒。如果实例在轮询 60 秒后未能进入 `running` 状态，则此步骤超时。
+ 使用 `aws:assertAwsResourceProperty` 操作调用 Amazon EC2 `DescribeInstanceStatus` API 操作来断言实例处于 `running` 状态。如果实例状态不为 `running`，则此步骤失败。

```
---
description: Sample runbook using AWS API operations
schemaVersion: '0.3'
assumeRole: "{{ AutomationAssumeRole }}"
parameters:
  AutomationAssumeRole:
    type: String
    description: "(Optional) The ARN of the role that allows Automation to perform the actions on your behalf."
    default: ''
  ImageName:
    type: String
    description: "(Optional) Image Name to launch EC2 instance with."
    default: "Windows_Server-2022-English-Full-Base*"
mainSteps:
- name: getImageId
  action: aws:executeAwsApi
  inputs:
    Service: ec2
    Api: DescribeImages
    Filters:  
    - Name: "name"
      Values: 
      - "{{ ImageName }}"
  outputs:
  - Name: ImageId
    Selector: "$.Images[0].ImageId"
    Type: "String"
- name: launchOneInstance
  action: aws:executeAwsApi
  inputs:
    Service: ec2
    Api: RunInstances
    ImageId: "{{ getImageId.ImageId }}"
    MaxCount: 1
    MinCount: 1
  outputs:
  - Name: InstanceId
    Selector: "$.Instances[0].InstanceId"
    Type: "String"
- name: waitUntilInstanceStateRunning
  action: aws:waitForAwsResourceProperty
  timeoutSeconds: 60
  inputs:
    Service: ec2
    Api: DescribeInstanceStatus
    InstanceIds:
    - "{{ launchOneInstance.InstanceId }}"
    PropertySelector: "$.InstanceStatuses[0].InstanceState.Name"
    DesiredValues:
    - running
- name: assertInstanceStateRunning
  action: aws:assertAwsResourceProperty
  inputs:
    Service: ec2
    Api: DescribeInstanceStatus
    InstanceIds:
    - "{{ launchOneInstance.InstanceId }}"
    PropertySelector: "$.InstanceStatuses[0].InstanceState.Name"
    DesiredValues:
    - running
outputs:
- "launchOneInstance.InstanceId"
...
```

前面介绍的每个自动化操作都允许您通过指定服务命名空间、API 操作名称、输入参数和输出参数来调用特定的 API 操作。输入由您选择的 API 操作定义。您可以在以下[服务参考](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/index.html)页面的左侧导航栏中选择服务来查看 API 操作（也称为方法）。在要调用的服务的**客户端**部分中选择一种方法。例如，以下页面中列出了 Amazon Relational Database Service (Amazon RDS) 的所有 API 操作（方法）：[Amazon RDS 方法](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html)。

您可以在以下位置查看每个自动化操作的架构：
+ [`aws:assertAwsResourceProperty` - 断言 AWS 资源状态或事件状态](automation-action-assertAwsResourceProperty.md)
+ [`aws:executeAwsApi` - 调用并运行 AWS API 操作](automation-action-executeAwsApi.md)
+ [`aws:waitForAwsResourceProperty` - 等待 AWS 资源属性](automation-action-waitForAwsResourceProperty.md)

架构包括使用每个操作所需字段的描述。

**使用 Selector/PropertySelector 字段**  
每个自动化操作都要求您指定一个输出 `Selector`（用于 `aws:executeAwsApi`）或 `PropertySelector`（用于 `aws:assertAwsResourceProperty` 和 `aws:waitForAwsResourceProperty`)。这些字段用于处理 AWS API 操作的 JSON 响应。这些字段使用 JSONPath 语法。

以下是帮助说明这一概念的 `aws:executeAwsAPi` 操作的示例：

```
---
mainSteps:
- name: getImageId
  action: aws:executeAwsApi
  inputs:
    Service: ec2
    Api: DescribeImages
    Filters:  
      - Name: "name"
        Values: 
          - "{{ ImageName }}"
  outputs:
    - Name: ImageId
      Selector: "$.Images[0].ImageId"
      Type: "String"
...
```

在 `aws:executeAwsApi` 步骤 `getImageId` 中，自动化将调用 `DescribeImages` API 操作并接收来自 `ec2` 的响应。然后，自动化将 `Selector - "$.Images[0].ImageId"` 应用于 API 响应，并将所选值分配给输出 `ImageId` 变量。通过指定 `"{{ getImageId.ImageId }}"`，同一自动化中的其他步骤可以使用 `ImageId` 的值。

以下是帮助说明这一概念的 `aws:waitForAwsResourceProperty` 操作的示例：

```
---
- name: waitUntilInstanceStateRunning
  action: aws:waitForAwsResourceProperty
  # timeout is strongly encouraged for action - aws:waitForAwsResourceProperty
  timeoutSeconds: 60
  inputs:
    Service: ec2
    Api: DescribeInstanceStatus
    InstanceIds:
    - "{{ launchOneInstance.InstanceId }}"
    PropertySelector: "$.InstanceStatuses[0].InstanceState.Name"
    DesiredValues:
    - running
...
```

在 `aws:waitForAwsResourceProperty` 步骤 `waitUntilInstanceStateRunning` 中，自动化将调用 `DescribeInstanceStatus` API 操作并接收来自 `ec2` 的响应。然后，自动化将 `PropertySelector - "$.InstanceStatuses[0].InstanceState.Name"` 应用于响应，并检查指定的返回值是否与 `DesiredValues` 列表中的值匹配（在本例中为 `running`）。此步骤重复这一过程，直到响应返回 `running` 实例状态。

## 在运行手册中使用 JSONPath
<a name="automation-action-json-path"></a>

JSONPath 表达式是以“\$1”开头的字符串。用于选择 JSON 元素中的一个或多个组件。下面的列表包含有关 Systems Manager 自动化支持的 JSONPath 运算符的信息：
+ **点表示的子字段 (.)**：用于 JSON 对象。此运算符选择特定键的值。
+ **深层扫描 (..)**：用于 JSON 元素。此运算符逐级扫描 JSON 元素，并使用特定键选择值列表。此运算符的返回类型始终为 JSON 数组。在自动化步骤输出类型上下文中，运算符可以是 StringList 或 MapList。
+ **数组索引 ([ ])**：用于 JSON 数组。此运算符获取特定索引的值。
+ **筛选条件 ([?(*expression*)])**：与 JSON 数组一起使用。此运算符筛选条件 JSON 数组值与筛选条件表达式中定义的标准相匹配。筛选条件表达式只能使用以下运算符：==、\$1=、>、<、>= 或 <=。不支持将多个筛选条件表达式与 AND (&&) 或 OR (\$1\$1) 组合使用。此运算符的返回类型始终为 JSON 数组。

为了更好地理解 JSONPath 运算符，请查看以下 ec2 `DescribeInstances` API 操作的 JSON 响应。此响应下面提供了几个示例，它们说明通过向 `DescribeInstances` API 操作响应应用不同的 JSONPath 表达式获取的不同结果。

```
{
    "NextToken": "abcdefg",
    "Reservations": [
        {
            "OwnerId": "123456789012",
            "ReservationId": "r-abcd12345678910",
            "Instances": [
                {
                    "ImageId": "ami-12345678",
                    "BlockDeviceMappings": [
                        {
                            "Ebs": {
                                "DeleteOnTermination": true,
                                "Status": "attached",
                                "VolumeId": "vol-000000000000"
                            },
                            "DeviceName": "/dev/xvda"
                        }
                    ],
                    "State": {
                        "Code": 16,
                        "Name": "running"
                    }
                }
            ],
            "Groups": []
        },
        {
            "OwnerId": "123456789012",
            "ReservationId": "r-12345678910abcd",
            "Instances": [
                {
                    "ImageId": "ami-12345678",
                    "BlockDeviceMappings": [
                        {
                            "Ebs": {
                                "DeleteOnTermination": true,
                                "Status": "attached",
                                "VolumeId": "vol-111111111111"
                            },
                            "DeviceName": "/dev/xvda"
                        }
                    ],
                    "State": {
                        "Code": 80,
                        "Name": "stopped"
                    }
                }
            ],
            "Groups": []
        }
    ]
}
```

**JSONPath 示例 1：从 JSON 响应获取特定的 String**

```
JSONPath: 
$.Reservations[0].Instances[0].ImageId 

Returns:
"ami-12345678"

Type: String
```

**JSONPath 示例 2：从 JSON 响应获取特定的 Boolean**

```
JSONPath:
$.Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.DeleteOnTermination
        
Returns:
true

Type: Boolean
```

**JSONPath 示例 3：从 JSON 响应获取特定的 Integer**

```
JSONPath:
$.Reservations[0].Instances[0].State.Code
        
Returns:
16

Type: Integer
```

**JSONPath 示例 4：深层扫描 JSON 响应，然后以 StringList 的形式获取 VolumeId 的所有值** 

```
JSONPath:
$.Reservations..BlockDeviceMappings..VolumeId
        
Returns:
[
   "vol-000000000000",
   "vol-111111111111"
]

Type: StringList
```

**JSONPath 示例 5：以 StringMap 的形式获取特定的 BlockDeviceMappings 对象**

```
JSONPath:
$.Reservations[0].Instances[0].BlockDeviceMappings[0]
        
Returns:
{
   "Ebs" : {
      "DeleteOnTermination" : true,
      "Status" : "attached",
      "VolumeId" : "vol-000000000000"
   },
   "DeviceName" : "/dev/xvda"
}

Type: StringMap
```

**JSONPath 示例 6：深层扫描 JSON 响应，然后以 MapList 的形式获取所有 State 对象**

```
JSONPath:
$.Reservations..Instances..State 
    
Returns:
[
   {
      "Code" : 16,
      "Name" : "running"
   },
   {
      "Code" : 80,
      "Name" : "stopped"
   }
]

Type: MapList
```

**JSONPath 示例 7：筛选处于 `running` 状态的实例**

```
JSONPath:
$.Reservations..Instances[?(@.State.Name == 'running')]

Returns:
[
  {
    "ImageId": "ami-12345678",
    "BlockDeviceMappings": [
      {
        "Ebs": {
          "DeleteOnTermination": true,
          "Status": "attached",
          "VolumeId": "vol-000000000000"
        },
        "DeviceName": "/dev/xvda"
      }
    ],
    "State": {
      "Code": 16,
      "Name": "running"
    }
  }
]

Type: MapList
```

**JSONPath 示例 8：返回未处于 `running` 状态实例的 `ImageId`**

```
JSONPath:
$.Reservations..Instances[?(@.State.Name != 'running')].ImageId

Returns:
[
  "ami-12345678"
]

Type: StringList | String
```

# 为 Automation 创建 Webhook 集成
<a name="creating-webhook-integrations"></a>

要在自动化期间使用 Webhooks 发送消息，请创建集成。可以在自动化过程中使用您的运行手册中的 `aws:invokeWebhook` 操作调用集成。如果尚未创建 Webhook，请参阅 [为集成创建 Webhooks](#creating-webhooks)。要了解有关 `aws:invokeWebhook` 操作的更多信息，请参阅 [`aws:invokeWebhook` – 调用 Automation Webhook 集成](invoke-webhook.md)。

如以下程序所示，您可以使用 Systems Manager Automation 控制台或您的首选命令行工具来创建集成。

## 创建集成（控制台）
<a name="creating-integrations-console"></a>

**要为 Automation 创建集成（控制台）**

1. 访问 [https://console.aws.amazon.com/systems-manager/](https://console.aws.amazon.com/systems-manager/)，打开 AWS Systems Manager 控制台。

1. 在导航窗格中，选择 **自动化**。

1. 请选择 **Integrations**（集成）选项卡。

1. 请选择 **Add integration**（添加集成），然后选择 **Webhook**。

1. 请输入要为集成包括的所需值和任何可选值。

1. 请选择 **Add**（添加）来创建集成。

## 创建集成（命令行）
<a name="creating-integrations-commandline"></a>

要使用命令行工具创建集成，您必须为集成创建所需的 `SecureString` 参数。Automation 使用 Parameter Store（Systems Manager 中的一项工具）中的预留命名空间来存储有关集成的信息。如果您使用 AWS 管理控制台 创建集成，Automation 会为您处理此过程。在命名空间之后，您必须指定要创建的集成类型，然后指定集成的名称。目前，Automation 支持 `webhook` 类型集成。

`webhook` 类型集成支持的字段如下：
+ 说明
+ headers
+ payload
+ URL

**开始前的准备工作**  
安装并配置 AWS Command Line Interface (AWS CLI) 或 AWS Tools for PowerShell（如果尚未这样做）。有关信息，请参阅[安装或更新 AWS CLI 的最新版本](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)以及[安装 AWS Tools for PowerShell](https://docs.aws.amazon.com/powershell/latest/userguide/pstools-getting-set-up.html)。

**要为 Automation 创建集成（命令行）**
+ 运行下列命令以为集成创建所需的 `SecureString` 参数。将每个*示例资源占位符*替换为您自己的信息。`/d9d01087-4a3f-49e0-b0b4-d568d7826553/ssm/integrations/webhook/` 命名空间在 Parameter Store 中为集成预留。参数的名称必须使用此命名空间，后跟集成的名称。例如 `/d9d01087-4a3f-49e0-b0b4-d568d7826553/ssm/integrations/webhook/myWebhookIntegration`。

------
#### [ Linux & macOS ]

  ```
  aws ssm put-parameter \
      --name "/d9d01087-4a3f-49e0-b0b4-d568d7826553/ssm/integrations/webhook/myWebhookIntegration" \
      --type "SecureString" \
      --data-type "aws:ssm:integration" \
      --value '{"description": "My first webhook integration for Automation.", "url": "myWebHookURL"}'
  ```

------
#### [ Windows ]

  ```
  aws ssm put-parameter ^
      --name "/d9d01087-4a3f-49e0-b0b4-d568d7826553/ssm/integrations/webhook/myWebhookIntegration" ^
      --type "SecureString" ^
      --data-type "aws:ssm:integration" ^
      --value  "{\"description\":\"My first webhook integration for Automation.\",\"url\":\"myWebHookURL\"}"
  ```

------
#### [ PowerShell ]

  ```
  Write-SSMParameter `
      -Name "/d9d01087-4a3f-49e0-b0b4-d568d7826553/ssm/integrations/webhook/myWebhookIntegration" `
      -Type "SecureString"
      -DataType "aws:ssm:integration"
      -Value '{"description": "My first webhook integration for Automation.", "url": "myWebHookURL"}'
  ```

------

## 为集成创建 Webhooks
<a name="creating-webhooks"></a>

在使用您的提供商创建 Webhooks 时，请注意以下几点：
+ 协议必须是 HTTPS。
+ 支持自定义请求标头。
+ 可以指定默认的请求正文。
+ 当使用 `aws:invokeWebhook` 操作调用集成时，可以覆盖默认的请求正文。

# 处理运行手册中的超时
<a name="automation-handling-timeouts"></a>

`timeoutSeconds` 属性由所有自动化操作共享。您可以使用此属性指定操作的执行超时值。此外，您还可以更改操作超时如何影响自动化和整体执行状态。另外，您可以通过定义操作的 `onFailure` 和 `isCritical` 共享属性来完成此操作。

例如，根据您的使用案例，您可能希望自动化继续执行其他操作，并且在操作超时的情况下不影响自动化的整体状态。在此示例中，您可以使用 `timeoutSeconds` 属性指定操作超时之前等待的时间长度。然后，您可以指定存在超时的情况下自动化应转到的操作（即步骤）。使用 `onFailure` 属性的格式 `step:step name` 指定值，而不是指定默认值 `Abort`。默认情况下，如果操作超时，在自动化执行状态将为 `Timed Out`。要防止超时影响自动化执行状态，请为 `false` 属性指定 `isCritical`。

以下示例演示如何为此情况中描述的操作定义共享属性。

------
#### [ YAML ]

```
- name: verifyImageAvailability
  action: 'aws:waitForAwsResourceProperty'
  timeoutSeconds: 600
  isCritical: false
  onFailure: 'step:getCurrentImageState'
  inputs:
    Service: ec2
    Api: DescribeImages
    ImageIds:
      - '{{ createImage.newImageId }}'
    PropertySelector: '$.Images[0].State'
    DesiredValues:
      - available
  nextStep: copyImage
```

------
#### [ JSON ]

```
{
    "name": "verifyImageAvailability",
    "action": "aws:waitForAwsResourceProperty",
    "timeoutSeconds": 600,
    "isCritical": false,
    "onFailure": "step:getCurrentImageState",
    "inputs": {
        "Service": "ec2",
        "Api": "DescribeImages",
        "ImageIds": [
            "{{ createImage.newImageId }}"
        ],
        "PropertySelector": "$.Images[0].State",
        "DesiredValues": [
            "available"
        ]
    },
    "nextStep": "copyImage"
}
```

------

有关所有自动化操作共享的属性的更多信息，请参阅 [所有操作共享的属性](automation-actions.md#automation-common)。