

# 防止更新堆栈资源
<a name="protect-stack-resources"></a>

在创建堆栈时，允许对所有资源执行所有更新操作。默认情况下，具有堆栈更新权限的任何人均可更新堆栈中的所有资源。在更新期间，一些资源可能需要中断或可能已完全替换，这会生成新的物理 ID 或全新的存储。使用堆栈策略可以防止堆栈资源在堆栈更新过程中被意外更新或删除。堆栈策略是一个 JSON 文档，该文档定义可对指定资源执行的更新操作。

设置堆栈策略后，默认情况下将保护堆栈中的所有资源。要允许对特定资源进行更新，您可在堆栈策略中为这些资源指定明确的 `Allow` 语句。您只能为每个堆栈定义一个堆栈策略，但在一个策略中可以保护多个资源。堆栈策略适用于所有尝试更新堆栈的 CloudFormation 用户。您不能将不同的堆栈策略与不同的用户关联。

堆栈策略仅在堆栈更新过程中适用。与 AWS Identity and Access Management（IAM）策略不同，它不提供访问控制。仅将堆栈策略用作故障保护功能来防止意外更新特定堆栈资源。要控制对 AWS 资源或操作的访问，请使用 IAM。

**Topics**
+ [示例堆栈策略](#stack-policy-intro-example)
+ [定义堆栈策略](#stack-policy-reference)
+ [设置堆栈策略](#protect-stack-resources-protecting)
+ [更新受保护资源](#protect-stack-resources-updating)
+ [修改堆栈策略](#protect-stack-resources-modifying)
+ [更多示例堆栈策略](#stack-policy-samples)

## 示例堆栈策略
<a name="stack-policy-intro-example"></a>

下面的示例堆栈策略阻止更新 `ProductionDatabase` 资源：

```
{
  "Statement" : [
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*"
    },
    {
      "Effect" : "Deny",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "LogicalResourceId/ProductionDatabase"
    }
  ]
}
```

当您设置堆栈策略时，将默认保护所有资源。为了允许对所有资源进行更新，我们添加了一个 `Allow` 语句来允许对所有资源执行的所有操作。虽然 `Allow` 语句指定所有资源，但显式 `Deny` 语句将为具有 `ProductionDatabase` 逻辑 ID 的资源覆盖前者。此 `Deny` 语句阻止对 `ProductionDatabase` 资源进行的所有更新操作，例如替换或删除。

需要 `Principal` 元素，但仅支持通配符 (`*`)，这意味着语句适用于所有[委托人](https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#principal)。

**注意**  
在堆栈更新期间，CloudFormation 会自动更新依赖其他更新的资源的资源。例如，CloudFormation 会更新引用所更新的资源的资源。CloudFormation 不会对自动更新的资源进行任何物理更改（例如，资源的 ID），但如果这些资源关联了堆栈策略，则您必须具有更新这些资源的权限。

## 定义堆栈策略
<a name="stack-policy-reference"></a>

在创建堆栈时，未设置堆栈策略，因此允许对所有资源执行所有更新操作。要阻止对堆栈资源执行更新操作，可定义一个堆栈策略，然后对堆栈设置该策略。堆栈策略是一个 JSON 文档，用于定义 CloudFormation 用户可以执行的 CloudFormation 堆栈更新操作以及这些操作应用到的资源。在创建堆栈时，可通过指定一个包含堆栈策略的文本文件或键入该策略来设置堆栈策略。在堆栈上设置堆栈策略时，默认情况下会拒绝未显式允许的任何更新。

您可定义一个带 5 个元素的堆栈策略：`Effect`、`Action`、`Principal`、`Resource` 和 `Condition`。下面的伪代码显示了堆栈策略语法。

```
{
  "Statement" : [
    {
      "Effect" : "Deny_or_Allow",
      "Action" : "update_actions",
      "Principal" : "*",
      "Resource" : "LogicalResourceId/resource_logical_ID",
      "Condition" : {
        "StringEquals_or_StringLike" : {
          "ResourceType" : [resource_type, ...]
        }
      }
    }
  ]
}
```

`Effect`  
确定是拒绝还是允许对指定资源执行指定的操作。您只能指定 `Deny` 或 `Allow`，例如：  

```
"Effect" : "Deny"
```
如果堆栈策略包含重叠语句 (同时允许和拒绝对资源进行更新)，则 `Deny` 语句始终将覆盖 `Allow` 语句。要确保某一资源受到保护，请对该资源使用 `Deny` 语句。

Action  
指定拒绝或允许的更新操作：    
更新:修改  
指定在对资源应用更改期间不会中断或有某些中断的更新操作。所有资源都保持其物理 ID。  
更新:替换  
指定会重新创建资源的更新操作。CloudFormation 使用指定的更新创建新资源，然后删除旧资源。由于资源是重新创建的，因此新资源的物理 ID 可能不相同。  
更新:删除  
指定删除资源的更新操作。从堆栈模板中完全删除资源的更新都需要此操作。  
更新:\$1  
指定所有更新操作。星号是通配符，代表所有更新操作。
以下示例说明如何只指定替换和删除操作：  

```
"Action" : ["Update:Replace", "Update:Delete"]
```
要允许除某个更新操作之外的所有更新操作，请使用 `NotAction`。例如，要允许除 `Update:Delete` 之外的所有更新操作，请使用 `NotAction`，如本示例中所示：  

```
{
  "Statement" : [
    {
      "Effect" : "Allow",
      "NotAction" : "Update:Delete",
      "Principal": "*",
      "Resource" : "*"
    }
  ]
}
```

Principal  
`Principal` 元素指定策略应用于的实体。需要此元素，但仅支持通配符 (`*`)，这意味着策略应用于所有[主体](https://docs.aws.amazon.com/glossary/latest/reference/glos-chap.html#principal)。

资源  
指定将应用策略的资源的逻辑 ID。要指定资源类型，请使用 `Condition` 元素。  
要指定一个资源，请使用其逻辑 ID。例如：  

```
"Resource" : ["LogicalResourceId/myEC2instance"]
```
您可以对逻辑 ID 使用通配符。例如，如果您对所有相关资源使用一个通用逻辑 ID 前缀，则可使用通配符指定所有资源：  

```
"Resource" : ["LogicalResourceId/CriticalResource*"]
```
您还可以对资源使用 `Not` 元素。例如，要允许对所有资源执行除某个更新之外的所有更新，请使用 `NotResource` 元素保护该资源：  

```
{
  "Statement" : [
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "NotResource" : "LogicalResourceId/ProductionDatabase"
    }
  ]
}
```
设置堆栈策略时，会拒绝未显式允许的任何更新。通过允许更新 `ProductionDatabase` 资源之外的所有资源，会拒绝更新 `ProductionDatabase` 资源。

Conditions  
指定应用策略的资源类型。要指定特定资源的逻辑 ID，请使用 `Resource` 元素。  
您可以指定资源类型（如所有 EC2 和 RDS 数据库实例），如以下示例所示：  

```
{
  "Statement" : [
  {
    "Effect" : "Deny",
    "Principal" : "*",
    "Action" : "Update:*",
    "Resource" : "*",
    "Condition" : {
      "StringEquals" : {
        "ResourceType" : ["AWS::EC2::Instance", "AWS::RDS::DBInstance"]
      }
    }
  },
  {
    "Effect" : "Allow",
    "Principal" : "*",
    "Action" : "Update:*",
    "Resource" : "*"
  }
  ]
}
```
`Allow` 语句授予对所有资源的更新权限，而 `Deny` 语句拒绝对 EC2 和 RDS 数据库实例的更新。`Deny` 语句始终覆盖允许操作。  
您可以对资源类型使用通配符。例如，您可以使用通配符拒绝所有 Amazon EC2 资源（如实例、安全组和子网）的更新权限，如以下示例所示：  

```
"Condition" : {
  "StringLike" : {
    "ResourceType" : ["AWS::EC2::*"]
  }
}
```
使用通配符时，必须使用 `StringLike` 条件。

## 设置堆栈策略
<a name="protect-stack-resources-protecting"></a>

您可以使用控制台或 AWS CLI 在创建堆栈时应用堆栈策略。您也可以使用 AWS CLI 将堆栈策略应用于现有堆栈。应用堆栈策略后，无法将其从堆栈中删除，但您可以使用 AWS CLI 修改该策略。

堆栈策略适用于所有尝试更新堆栈的 CloudFormation 用户。您不能将不同的堆栈策略与不同的用户关联。

有关如何编写堆栈策略的信息，请参阅[定义堆栈策略](#stack-policy-reference)。

**在创建堆栈时设置堆栈策略（控制台）**

1. 通过以下网址打开 CloudFormation 控制台：[https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/)。

1. 在屏幕顶部的导航栏中，选择 AWS 区域以在其中创建堆栈。

1. 在 **CloudFormation Stacks (CloudFormation 堆栈)** 页上，选择 **Create stack (创建堆栈)**。

1. 在“创建堆栈”向导的 **Configure stack options (配置堆栈选项)** 页面上，展开**高级**部分，然后选择 **Stack policy (堆栈策略)**。

1. 指定堆栈策略：
   + 要在控制台中直接编写策略，请选择 **Enter stack policy (输入堆栈策略)**，然后在文本字段中直接键入堆栈策略。
   + 要使用在单独的文件中定义的策略，请依次选择 **Upload a file (上传文件)**、**Choose file (选择文件)**，以选择包含堆栈策略的文件。

**在创建堆栈时设置堆栈策略（AWS CLI）**
+ 将 [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-stack.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/create-stack.html) 命令与 `--stack-policy-body` 选项结合使用可键入修改的策略，或将此命令与 `--stack-policy-url` 选项结合使用可指定包含策略的文件。

**在现有堆栈上设置堆栈策略（仅限 AWS CLI）**
+ 将 [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-stack-policy.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-stack-policy.html) 命令与 `--stack-policy-body` 选项结合使用可键入修改的策略，或将此命令与 `--stack-policy-url` 选项结合使用可指定包含策略的文件。
**注意**  
要将策略添加到现有堆栈中，您必须有权执行 CloudFormation [https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetStackPolicy.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetStackPolicy.html) 操作。

## 更新受保护资源
<a name="protect-stack-resources-updating"></a>

要更新受保护的资源，可创建一个覆盖堆栈策略并允许对这些资源进行更新的临时策略。在更新堆栈时指定覆盖策略。覆盖策略不会永久更改堆栈策略。

要更新受保护的资源，您必须有权使用 CloudFormation [https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetStackPolicy.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetStackPolicy.html) 操作。有关设置 CloudFormation 权限的信息，请参阅[使用 AWS Identity and Access Management 控制 CloudFormation 访问权限](control-access-with-iam.md)。

**注意**  
在堆栈更新期间，CloudFormation 会自动更新依赖其他更新的资源的资源。例如，CloudFormation 会更新引用所更新的资源的资源。CloudFormation 不会对自动更新的资源进行任何物理更改（例如，资源的 ID），但如果这些资源关联了堆栈策略，则您必须具有更新这些资源的权限。

**更新受保护的资源（控制台）**

1. 通过以下网址打开 CloudFormation 控制台：[https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/)。

1. 选择要更新的堆栈，然后依次选择 **Stack actions (堆栈操作)** 和 **Update Stack (更新堆栈)**。

1. 如果您*尚未*修改堆栈模板，请选择 **Use current template (使用当前模板)**，然后单击 **Next (下一步)**。如果您已修改模板，请选择 **Replace current template (替换当前模板)**，然后指定在 **Specify template (指定模板)** 中指定更新模板的位置：
   + 对于在计算机本地存储的模板，选择 **Upload a template file (上传模板文件)**。选择 **Choose File** 以导航到文件并选中它，然后单击 **Next**。
   + 对于存储在 Amazon S3 存储桶中的模板，请选择 **Amazon S3 URL**。输入或粘贴模板的 URL，然后单击 **Next (下一步)**。

     如果模板位于启用了版本控制的存储桶中，则可以通过在 URL 后附加 `?versionId=version-id` 来指定模板的具体版本。有关更多信息，请参阅《Amazon Simple Storage Service 用户指南》中的[使用启用版本控制的存储桶中的对象](https://docs.aws.amazon.com/AmazonS3/latest/userguide/manage-objects-versioned-bucket.html)**。

1. 如果您的模板包含参数，则在 **Specify stack details (指定堆栈详细信息)** 页上，输入或修改参数值，然后选择 **Next (下一步)**。

   CloudFormation 使用当前在堆栈中设置的值填充每个参数，使用 `NoEcho` 属性声明的参数除外。可以选择**使用现有值**来对这些参数使用当前值。

   有关使用 `NoEcho` 来遮蔽敏感信息以及使用动态参数管理密钥的更多信息，请参阅 [请勿将凭证嵌入您的模板](security-best-practices.md#creds) 最佳实践。

1. 指定覆盖堆栈策略。

   1. 在 **Configure stack options (配置堆栈选项)** 页上的**高级选项**部分中，选择 **Stack policy (堆栈策略)**。

   1. 选择 **Upload a file (上传文件)**。

   1. 单击 **Choose file (选择文件)**，然后导航到包含覆盖堆栈策略的文件或键入策略。

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

   覆盖策略必须为您要更新的受保护资源指定 `Allow` 语句。例如，要更新所有受保护资源，可以指定允许所有更新的临时覆盖策略：

   ```
   {
     "Statement" : [
       {
         "Effect" : "Allow",
         "Action" : "Update:*",
         "Principal": "*",
         "Resource" : "*"
       }
     ]
   }
   ```
**注意**  
CloudFormation 仅在此更新期间应用覆盖策略。覆盖策略不会永久更改堆栈策略。要修改堆栈策略，请参阅[修改堆栈策略](#protect-stack-resources-modifying)。

1. 审查提交的堆栈信息和更改。

   检查您是否已提交正确的信息，例如正确的参数值或模板 URL。如果模板包含 IAM 资源，请选择 **I acknowledge that this template may create IAM resources (我确认该模板可能会创建 IAM 资源)** 以指定您要在模板中使用 IAM 资源。有关更多信息，请参阅 [确认 CloudFormation 模板中的 IAM 资源](control-access-with-iam.md#using-iam-capabilities)。

   在**预览您的更改**部分中，检查 CloudFormation 是否会进行所需的所有更改。例如，检查 CloudFormation 是否添加、移除和修改了您要添加、移除或修改的资源。CloudFormation 通过为堆栈创建更改集来生成此预览。有关更多信息，请参阅 [使用更改集更新 CloudFormation 堆栈](using-cfn-updating-stacks-changesets.md)。

1. 如果您对所做更改感到满意，请单击 **Update (更新)**。
**注意**  
此时，您还可以选择查看更改集以更全面地查看建议的更新。要执行此操作，请单击 **View change set**（查看更改集）而不是 **Update**（更新）。CloudFormation 会显示根据您的更新生成的更改集。准备好执行堆栈更新后，请单击 **Execute (执行)**。

   CloudFormation 会显示您的堆栈的 **Stack details (堆栈详细信息)** 页面。堆栈现在的状态为 `UPDATE_IN_PROGRESS`。CloudFormation 成功完成堆栈更新后，会将堆栈状态设置为 `UPDATE_COMPLETE`。

   如果堆栈更新失败，CloudFormation 会自动回滚更改，并将状态设置为 `UPDATE_ROLLBACK_COMPLETE`。

**更新受保护资源（AWS CLI）**
+ 将 [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/update-stack.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/update-stack.html) 命令与 `--stack-policy-during-update-body` 选项结合使用可键入修改的策略，或将此命令与 `--stack-policy-during-update-url` 选项结合使用可指定包含策略的文件。
**注意**  
CloudFormation 仅在此更新期间应用覆盖策略。覆盖策略不会永久更改堆栈策略。要修改堆栈策略，请参阅[修改堆栈策略](#protect-stack-resources-modifying)。

## 修改堆栈策略
<a name="protect-stack-resources-modifying"></a>

要保护其他资源或从资源中删除保护，请修改堆栈策略。例如，当您将要保护的数据库添加到堆栈时，会将该数据库的 `Deny` 语句添加到堆栈策略。要修改策略，您必须有权使用 [https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetStackPolicy.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_SetStackPolicy.html) 操作。

使用 AWS CLI 修改堆栈策略。

**修改堆栈策略（AWS CLI）**
+ 将 [https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-stack-policy.html](https://docs.aws.amazon.com/cli/latest/reference/cloudformation/set-stack-policy.html) 命令与 `--stack-policy-body` 选项结合使用可键入修改的策略，或将此命令与 `--stack-policy-url` 选项结合使用可指定包含策略的文件。

您无法删除堆栈策略。要从所有资源删除全部保护，您可修改策略以明确允许对所有资源执行全部操作。以下策略允许对所有资源进行全部更新：

```
{
  "Statement" : [
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*"
    }
  ]
}
```

## 更多示例堆栈策略
<a name="stack-policy-samples"></a>

以下示例策略说明如何阻止对所有堆栈资源和特定资源进行更新，并阻止特定类型的更新。

### 阻止对所有堆栈资源的更新
<a name="w2aac43c15c21b5"></a>

要阻止对所有堆栈资源的更新，以下策略为所有资源的所有更新操作指定 `Deny` 语句。

```
{
  "Statement" : [
    {
      "Effect" : "Deny",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*"
    }
  ]
}
```

### 阻止对单个资源的更新
<a name="w2aac43c15c21b7"></a>

以下策略拒绝对带 `MyDatabase` 逻辑 ID 的数据库执行的所有更新操作。它使用 `Allow` 语句允许对所有其他堆栈资源进行全部更新操作。`Allow` 语句不应用于 `MyDatabase` 资源，因为 `Deny` 语句始终覆盖允许操作。

```
{
  "Statement" : [
    {
      "Effect" : "Deny",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "LogicalResourceId/MyDatabase"
    },
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*"
    }
  ]
}
```

您可以使用默认拒绝来获得与上一示例相同的结果。设置堆栈策略时，CloudFormation 会拒绝未显式允许的任何更新。以下策略允许对除 `ProductionDatabase` 资源 (默认情况下，拒绝更新此资源) 之外的所有资源进行的更新。

```
{
  "Statement" : [
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "NotResource" : "LogicalResourceId/ProductionDatabase"
    }
  ]
}
```

**重要**  
使用默认拒绝存在风险。如果您策略中的其他位置具有 `Allow` 语句 (例如，使用通配符的 `Allow` 语句)，则可能意外授予 (原本不打算授予) 对资源的更新权限。由于显式拒绝将覆盖任何允许操作，因此可以使用 `Deny` 语句确保保护资源。

### 阻止对资源类型的所有实例进行更新
<a name="w2aac43c15c21b9"></a>

以下策略拒绝针对 RDS 数据库实例资源类型的所有更新操作。它使用 `Allow` 语句允许对所有其他堆栈资源进行全部更新操作。`Allow` 语句不应用于 RDS 数据库实例资源，因为 `Deny` 语句始终覆盖允许操作。

```
{
  "Statement" : [
    {
      "Effect" : "Deny",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*",
      "Condition" : {
        "StringEquals" : {
          "ResourceType" : ["AWS::RDS::DBInstance"]
        }
      }
    },
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*"
    }
  ]
}
```

### 阻止针对实例的替换更新
<a name="w2aac43c15c21c11"></a>

以下策略拒绝会导致替换具有 `MyInstance` 逻辑 ID 的实例的更新。它使用 `Allow` 语句允许对所有其他堆栈资源进行全部更新操作。`Allow` 语句不应用于 `MyInstance` 资源，因为 `Deny` 语句始终覆盖允许操作。

```
{
  "Statement" : [
    {
      "Effect" : "Deny",
      "Action" : "Update:Replace",
      "Principal": "*",
      "Resource" : "LogicalResourceId/MyInstance"
    },
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*"
    }
  ]
}
```

### 阻止对嵌套堆栈进行更新
<a name="w2aac43c15c21c13"></a>

以下策略将拒绝针对 CloudFormation 堆栈资源类型（嵌套堆栈）的所有更新操作。它使用 `Allow` 语句允许对所有其他堆栈资源进行全部更新操作。`Allow` 语句不会应用于 CloudFormation 堆栈资源，因为 `Deny` 语句始终会覆盖 allow 操作。

```
{
  "Statement" : [
    {
      "Effect" : "Deny",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*",
      "Condition" : {
        "StringEquals" : {
          "ResourceType" : ["AWS::CloudFormation::Stack"]
        }
      }
    },
    {
      "Effect" : "Allow",
      "Action" : "Update:*",
      "Principal": "*",
      "Resource" : "*"
    }
  ]
}
```