

# 使用 CloudFormation 模块创建可跨模板包含的可重复使用的资源配置
<a name="modules"></a>

*模块*是一种供您以透明、易管理和可重复的方式打包资源配置以便跨堆栈模板实现包含的方法。模块可以将常见服务配置和最佳实践封装为模块化、可自定义的构建基块，供您包含在堆栈模板中。模块使您能够将包含最佳实践、专家领域知识和可接受的指南（例如安全性、合规性、治理和行业法规）在内的资源配置纳入模板中，而无需深入了解资源实施的复杂性。

例如，网络领域专家可以创建一个包含遵守安全准则的内置安全组和入口/出口规则的模块。然后，您可以将该模块纳入到模板中，以便在堆栈中预置安全的网络基础架构，而无需花费时间弄清楚 VPC、子网、安全组和网关的工作方式。此外，由于模块具有版本控制，因此如果安全指南随时间而发生变化，模块作者可以创建包含这些更改的新版本模块。

在模板中使用模块的特点包括：
+ **可预测性** – 模块必须遵守其在 CloudFormation 注册表中注册的架构，这样在您将模块纳入到模板中后，您就知道该模块可以解析到哪些资源。
+ **可重用性** – 您可以在多个模板和账户中使用同一模块。
+ **可追溯性** – CloudFormation 可以保留堆栈中的哪些资源是从模块预配置的知识，使您能够轻松了解资源变化的来源。
+ **可管理性**：注册模块后，您可以通过 CloudFormation 注册表对其进行管理，包括版本控制以及账户和区域可用性。

模块可以包含：
+ 要从模块配置的一个或多个资源以及任何关联的数据，例如输出数据或条件。
+ 使您能够在使用模块时指定自定义值的任何模块参数。

有关开发模块的信息，请参阅《CloudFormation CLI User Guide》**中的 [Developing modules](https://docs.aws.amazon.com/cloudformation-cli/latest/userguide/modules.html)。

**Topics**
+ [使用模块时的注意事项](#module-considerations)
+ [了解模块版本控制](module-versioning.md)
+ [使用 CloudFormation 私有注册表中的模块](modules-using.md)
+ [使用参数指定模块值](module-using-params.md)
+ [CloudFormation 模板中的引用模块资源](module-ref-resources.md)

## 使用模块时的注意事项
<a name="module-considerations"></a>
+ 使用模块不收取任何额外费用。您只需为模块解析到堆栈中的资源付费。
+ CloudFormation 配额（例如堆栈中允许的最大资源数或模板正文的最大大小）适用于处理过的模板，无论该模板中包含的资源是否来自模块。有关更多信息，请参阅 [了解 CloudFormation 配额](cloudformation-limits.md)。
+ 您在堆栈级别指定的标签将分配给派生自模块的各个资源。
+ CloudFormation 处理模板时，在模块级别指定的帮助程序脚本不会传播到模块中包含的各个资源。
+ 模块中指定的输出将在模板级别传播到输出。

  将为每个输出分配一个逻辑 ID，该 ID 是模块逻辑名称和模块中定义的输出名称的拼接。有关更多信息，请参阅 [获取从已部署的 CloudFormation 堆栈导出的输出](using-cfn-stack-exports.md)。
+ 模块中指定的参数不会传播到模板级别的参数。

  但是，您可以创建引用模块级参数的模板级参数。有关更多信息，请参阅 [使用参数指定模块值](module-using-params.md)。

# 了解模块版本控制
<a name="module-versioning"></a>

CloudFormation 注册表充当存储库，您可以在其中注册和管理模块，以供在您的 AWS 账户和区域内使用。您可以在您的账户和区域内注册来自各种来源的模块，包括 AWS、第三方发布者和您自己的自定义扩展。有关更多信息，请参阅 [通过 CloudFormation 注册表管理扩展](registry.md)。

模块可以有不同的版本，因此您可以指定要使用的模块版本。当您需要更新或修改模块而不破坏依赖该模块的现有堆栈时，此版本控制功能特别有用。

使用模块的多个版本时，请注意以下事项：
+ 在堆栈操作期间，CloudFormation 会使用当前在执行堆栈操作的 AWS 账户和区域中注册为默认版本的模块版本。包括嵌套在其他模块中的模块。

  因此，请注意，如果您在不同账户或区域中将同一模块的不同版本注册为默认版本，则使用同一个模板可能会导致不同的结果。
+ 在堆栈操作期间，CloudFormation 会使用当前在执行堆栈操作的 AWS 账户和区域中注册为默认版本的资源版本。这包括通过纳入模块生成的资源。
+ 更改模块的默认版本不会启动任何堆栈更新操作。但是，在您下次使用包含该模块的任何模板执行堆栈操作（例如堆栈更新）时，CloudFormation 将在操作中使用新的默认版本。

  但在指定了**使用以前的模板**选项的情况下执行堆栈更新时例外，如下所述。
+ 对于堆栈更新操作，如果您指定**使用以前的模板**选项，则 CloudFormation 会使用先前处理过的模板进行堆栈更新，不会针对您可能对模块作出的任何更改重新处理模块。
+ 为了保证结果统一，如果要在堆栈模板中纳入模块以用于堆栈集，则应确保在计划部署堆栈实例的所有账户和区域中将模块的同一版本设置为默认版本。这包括嵌套在其他模块中的模块。有关更多信息，请参阅 [使用 StackSets 跨账户和区域管理堆栈](what-is-cfnstacksets.md)。

## 激活第三方公共模块的要求
<a name="requirements-for-modules"></a>

要在您的账户和区域中成功激活第三方公有模块，模块中包含的每个第三方公有扩展（资源或模块）必须满足以下条件：
+ **扩展激活**：必须在您要使用扩展的账户和区域中激活该扩展。有关更多信息，请参阅 [使用 CloudFormation 注册表中的第三方公有扩展](registry-public.md)。
+ **别名注册**：如果模块中的扩展使用类型名称别名，则该扩展必须使用相同的类型名称别名在您的账户和区域中注册。有关更多信息，请参阅 [使用别名以引用扩展](registry-public.md#registry-public-enable-alias)。
+ **版本兼容性**：当前激活的扩展版本必须是模块中指定的该扩展支持的主要版本之一。

如果您没有激活正确的第三方公有扩展和扩展版本，CloudFormation 将使操作失败，并显示一条错误消息，其中列出在成功激活该模块之前需要激活的扩展和版本。

# 使用 CloudFormation 私有注册表中的模块
<a name="modules-using"></a>

本主题介绍如何在 CloudFormation 模板中使用模块。将模块视为可以添加到模板中的预制资源包。

要使用模块，步骤如下：
+ **注册模块**：您在 CloudFormation 注册表中将模块注册为私有扩展。请确保模块已在您工作的 AWS 账户和区域注册。有关更多信息，请参阅 [CoudFormation 注册表概念](registry-concepts.md)。
+ **将其包含在模板中**：将模块添加到 CloudFormation 模板的 [Resources](resources-section-structure.md) 部分，就像处理其他资源一样。您还需要提供模块所需的所有属性。
+ **创建或更新堆栈**：当您启动堆栈操作时，CloudFormation 会生成一个已处理的模板，该模板将所有包含的模块解析为适当的资源。
+ **预览更改**：在进行更改之前，您可以使用更改集来查看将添加或更改哪些资源。有关更多信息，请参阅 [使用更改集更新 CloudFormation 堆栈](using-cfn-updating-stacks-changesets.md)。

参阅以下示例：您有一个包含资源和模块的模板。模板包含一个单独的资源 `ResourceA` 和一个模块 `ModuleParent`。该模块包含两个资源（`ResourceB` 和 `ResourceC`）以及一个嵌套模块 `ModuleChild`。`ModuleChild` 包含单个资源 `ResourceD`。如果您使用此模板创建堆栈，CloudFormation 会处理模板并将模块解析为适当的资源。生成的堆栈有四个资源：`ResourceA`、`ResourceB`、`ResourceC` 和 `ResourceD`。

![\[在堆栈操作期间，CloudFormation 会将堆栈模板中包含的两个模块解析为适当的四个资源。\]](http://docs.aws.amazon.com/zh_cn/AWSCloudFormation/latest/UserGuide/images/modules-resource-inclusion.png)


CloudFormation 会跟踪堆栈中的哪些资源是从模块创建而成的。您可以在给定堆栈的 **Events**（事件）、**Resources**（资源）和 **Drifts**（偏差）选项卡上查看此信息，更改集预览中也包含此信息。

模块与模板中的资源可以区别开来，因为它们遵循以下四部分命名约定，而不是资源所用的典型的三部分命名约定：

```
organization::service::use-case::MODULE
```

# 使用参数指定模块值
<a name="module-using-params"></a>

在 CloudFormation 中，您可以通过在堆栈创建或更新期间提供输入值，从而使用模板参数来自定义堆栈。这些参数允许您根据需要更改堆栈的某些方面。有关定义模板参数的更多信息，请参阅 [CloudFormation 模板 Parameters 语法](parameters-section-structure.md)。

同样，模块也可以有参数。这些模块参数使您能够从使用自定义值的模板（或其他模块）向模块输入自定义值。然后，模块可以使用这些自定义值来设置其所包含资源的属性值。

您还可以定义设置模块属性的模板参数，以便您可以输入在堆栈操作时传递给模块的值。

如果模块包含具有自己的模块参数的嵌套模块，则您可以：
+ 直接在父模块中为嵌套模块的参数指定值。
+ 在父模块中定义相应的模块参数，使嵌套模块的参数能够由包含父模块的模板（或模块）来设置。

## 使用模板参数指定模块参数值
<a name="module-using-params-example-1"></a>

以下示例展示了如何定义将值传递给模块的模板参数。

包含 `My::S3::SampleBucket::MODULE` 的此模板定义了模板参数 `BucketName`，该参数使用户能够在堆栈操作期间指定 S3 存储桶名称。

```
# Template containing My::S3::SampleBucket::MODULE
Parameters:
  BucketName:
    Description: Name for your sample bucket
    Type: String
Resources:
  MyBucket:
    Type: 'My::S3::SampleBucket::MODULE'
    Properties:
      BucketName: !Ref BucketName
```

## 在父模块中指定子模块中资源的属性
<a name="module-using-params-example-2"></a>

以下示例展示了如何在嵌套在另一个模块中的模块中指定参数值。

第一个模块 `My::S3::SampleBucketPrivate::MODULE` 将是子模块。该模块定义了两个参数：`BucketName` 和 `AccessControl`。为这些参数指定的值用于指定模块所包含的 `AWS::S3::Bucket` 资源的 `BucketName` 和 `AccessControl` 属性。以下为 `My::S3::SampleBucketPrivate::MODULE` 的模板片段。

```
# My::S3::SampleBucketPrivate::MODULE
AWSTemplateFormatVersion: 2010-09-09
Description: A sample S3 Bucket with Versioning and DeletionPolicy.
Parameters:
  BucketName:
    Description: Name for the bucket
    Type: String
  AccessControl:
    Description: AccessControl for the bucket
    Type: String
Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      AccessControl: !Ref AccessControl
      DeletionPolicy: Retain
      VersioningConfiguration:
        Status: Enabled
```

接下来，上一个模块嵌套在父模块 `My::S3::SampleBucket::MODULE` 中。父模块 `My::S3::SampleBucket::MODULE` 通过以下方式设置子模块参数：
+ 其将 `My::S3::SampleBucketPrivate::MODULE` 的 `AccessControl` 参数设置为 `Private`。
+ 对于 `BucketName`，其定义了一个模块参数，该参数将允许存储桶名称在包含 `My::S3::SampleBucket::MODULE` 的模板（或模块）中进行指定。

```
# My::S3::SampleBucket::MODULE
AWSTemplateFormatVersion: 2010-09-09
Description: A sample S3 Bucket. With Private AccessControl.
Parameters:
  BucketName:
    Description: Name for your sample bucket
    Type: String
Resources:
  MyBucket:
    Type: 'My::S3::SampleBucketPrivate::MODULE'
    Properties:
      BucketName: !Ref BucketName
      AccessControl: Private
```

## 为模块参数指定约束
<a name="modules-using-parameters-constraints"></a>

模块参数不支持约束强制执行。要对模块参数执行约束检查，请创建具有所需约束的模板参数，然后在模块参数中引用该模板参数。有关定义模板参数的更多信息，请参阅 [CloudFormation 模板 Parameters 语法](parameters-section-structure.md)。

# CloudFormation 模板中的引用模块资源
<a name="module-ref-resources"></a>

在 CloudFormation 模板中，您经常需要根据一个资源的名称或属性来设置另一个资源的属性。有关更多信息，请参阅 [引用资源](resources-section-structure.md#using-cross-resource-references)。

要引用 CloudFormation 模板中某个模块内包含的资源，必须将以下两个逻辑名称组合在一起：
+ 您在模板中包含模块时为模块本身指定的逻辑名称。
+ 该模块内特定资源的逻辑名称。

您可以将这两个逻辑名称组合在一起，两者之间可以使用也可以不使用句点 (.)。例如，如果模块的逻辑名称为 `MyModule`，而资源的逻辑名称为 `MyBucket`，则可以将该资源称为 `MyModule.MyBucket` 或 `MyModuleMyBucket`。

要查找模块内资源的逻辑名称，您可以查阅该模块的架构（该架构可在 CloudFormation 注册表中找到），也可以使用 [https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeType.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeType.html) 操作。架构列出了属于该模块的所有资源及其逻辑名称。

获得完整的逻辑名称后，您可以使用 `GetAtt` 和 `Ref` 之类的 CloudFormation 函数来访问模块资源上的属性值。

例如，您有一个 `My::S3::SampleBucket::MODULE` 模块，其中包含逻辑名称为 `S3Bucket` 的 `AWS::S3::Bucket` 资源。要使用 `Ref` 函数引用此存储桶的名称，请将模板中模块的名称（`MyBucket`）与模块中资源的逻辑名称（`S3Bucket`）组合使用。完整的逻辑名称为 `MyBucket.S3Bucket` 或 `MyBucketS3Bucket`。

**示例 模板**  
以下示例模板使用 `My::S3::SampleBucket::MODULE` 模块创建 S3 存储桶。它还会创建一个 Amazon SQS 队列，并将其名称设置为与模块中的存储桶名称相同。此外，该模板还输出所创建的 S3 存储桶的 Amazon 资源名称（ARN）。

```
# Template that uses My::S3::SampleBucket::MODULE
Parameters:
  BucketName:
    Description: Name for your sample bucket
    Type: String
Resources:
  MyBucket:
    Type: My::S3::SampleBucket::MODULE
    Properties:
      BucketName: !Ref BucketName
  exampleQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: !Ref MyBucket.S3Bucket
Outputs:
  BucketArn:
    Value: !GetAtt MyBucket.S3Bucket.Arn
```