

# 在堆栈之间移动资源
<a name="refactor-stacks"></a>

通过使用 `resource import` 功能，您可以在堆栈之间移动资源或*重构* 堆栈。您需要先在要移动的资源中添加 `Retain` 删除策略，以确保在从源堆栈中删除资源并将其导入到目标堆栈时保留该资源。

如果您不熟悉导入，建议您先查看 [将 AWS 资源导入 CloudFormation 堆栈](import-resources.md) 主题中的介绍信息。

**重要**  
并非所有资源都支持导入操作。在从堆栈中删除资源之前，请参阅[支持导入操作的资源](resource-import-supported-resources.md)。如果您从堆栈中删除不支持导入操作的资源，则无法将该资源导入其他堆栈中或将其重新放回源堆栈中。

## 使用 AWS 管理控制台重构堆栈
<a name="refactor-stacks-console"></a>

1. 在源模板中，为要移动的资源指定 `Retain` [DeletionPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-attribute-deletionpolicy.html)。

   在以下示例源模板中，`Games` 是该重构的目标。  
**Example JSON**  

   ```
   {
       "AWSTemplateFormatVersion": "2010-09-09",
       "Description": "Import test",
       "Resources": {
           "ServiceTable":{
              "Type":"AWS::DynamoDB::Table",
              "Properties":{
                 "TableName":"Service",
                 "AttributeDefinitions":[
                    {
                       "AttributeName":"key",
                       "AttributeType":"S"
                    }
                 ],
                 "KeySchema":[
                    {
                       "AttributeName":"key",
                       "KeyType":"HASH"
                    }
                 ],
                 "ProvisionedThroughput":{
                    "ReadCapacityUnits":5,
                    "WriteCapacityUnits":1
                 }
              }
           },
           "GamesTable": {
               "Type": "AWS::DynamoDB::Table",
               "DeletionPolicy": "Retain",
               "Properties": {
                   "TableName": "Games",
                   "AttributeDefinitions": [
                       {
                           "AttributeName": "key",
                           "AttributeType": "S"
                       }
                   ],
                   "KeySchema": [
                       {
                           "AttributeName": "key",
                           "KeyType": "HASH"
                       }
                   ],
                   "ProvisionedThroughput": {
                       "ReadCapacityUnits": 5,
                       "WriteCapacityUnits": 1
                   }
               }
           }
       }
   }
   ```

1. 打开 CloudFormation 控制台以执行堆栈更新，从而应用删除策略。

   1. 在**堆栈**页面上，在选择了堆栈的情况下，选择**更新**。

   1. 在 **Prepare template (准备模板)** 下面，选择 **Replace current template (替换当前模板)**。

   1. 在 **Specify template (指定模板)** 下面，在 `GamesTable` 上为更新的源模板提供 `DeletionPolicy` 属性，然后选择 **Next (下一步)**。
      + 选择 **Amazon S3 URL**，然后在文本框中指定更新的源模板的 URL。
      + 选择 **Upload a template file (上传模板文件)**，然后浏览更新的源模板文件。

   1. 在 **Specify stack details (指定堆栈详细信息)** 页面上，不需要进行任何更改。选择**下一步**。

   1. 在 **Configure stack options (配置堆栈选项)** 页面上，不需要进行任何更改。选择**下一步**。

   1. 在**查看 *SourceStackName*** 页面上，检查所做的更改。如果您的模板包含 IAM 资源，请选择 **I acknowledge that this template may create IAM resources (我确认此模板可创建 IAM 资源)** 以指定您要使用模板中的 IAM 资源。有关在模板中使用 IAM 资源的更多信息，请参阅 [使用 AWS Identity and Access Management 控制 CloudFormation 访问权限](control-access-with-iam.md)。然后，创建更改集以更新源堆栈，或者直接更新源堆栈。

1. 从源模板中删除资源、相关参数和输出，然后将它们添加到目标模板中。

   现在，源模板如下所示。  
**Example JSON**  

   ```
   {
       "AWSTemplateFormatVersion": "2010-09-09",
       "Description": "Import test",
       "Resources": {
           "ServiceTable":{
              "Type":"AWS::DynamoDB::Table",
              "Properties":{
                 "TableName":"Service",
                 "AttributeDefinitions":[
                    {
                       "AttributeName":"key",
                       "AttributeType":"S"
                    }
                 ],
                 "KeySchema":[
                    {
                       "AttributeName":"key",
                       "KeyType":"HASH"
                    }
                 ],
                 "ProvisionedThroughput":{
                    "ReadCapacityUnits":5,
                    "WriteCapacityUnits":1
                 }
              }
           }
       }
   }
   ```

   以下示例目标模板当前具有 `PlayersTable` 资源，现在还包含 `GamesTable`。  
**Example JSON**  

   ```
   {
       "AWSTemplateFormatVersion": "2010-09-09",
       "Description": "Import test",
       "Resources": {
           "PlayersTable": {
               "Type": "AWS::DynamoDB::Table",
               "Properties": {
                   "TableName": "Players",
                   "AttributeDefinitions": [
                       {
                           "AttributeName": "key",
                           "AttributeType": "S"
                       }
                   ],
                   "KeySchema": [
                       {
                           "AttributeName": "key",
                           "KeyType": "HASH"
                       }
                   ],
                   "ProvisionedThroughput": {
                       "ReadCapacityUnits": 5,
                       "WriteCapacityUnits": 1
                   }
               }
           },
           "GamesTable": {
               "Type": "AWS::DynamoDB::Table",
               "DeletionPolicy": "Retain",
               "Properties": {
                   "TableName": "Games",
                   "AttributeDefinitions": [
                       {
                           "AttributeName": "key",
                           "AttributeType": "S"
                       }
                   ],
                   "KeySchema": [
                       {
                           "AttributeName": "key",
                           "KeyType": "HASH"
                       }
                   ],
                   "ProvisionedThroughput": {
                       "ReadCapacityUnits": 5,
                       "WriteCapacityUnits": 1
                   }
               }
           }
       }
   }
   ```

1. 重复步骤 2 - 3 以再次更新源堆栈，此次是从堆栈中删除目标资源。

1. 执行导入操作以将 `GamesTable` 添加到目标堆栈中。

   1. 在 **Stacks (堆栈)** 页面上，在选择了父堆栈的情况下，选择 **Stack actions (堆栈操作)**，然后选择 **Import resources into stack (将资源导入到堆栈)**。  
![\[控制台中的 Import resources into stack (将资源导入到堆栈) 选项。\]](http://docs.aws.amazon.com/zh_cn/AWSCloudFormation/latest/UserGuide/images/stack-actions-import.png)

   1. 访问 **Import overview (导入概述)** 页面，以查看在该操作期间需要提供的内容列表。然后选择**下一步**。

   1. 在 **Specify template**（指定模板）页面上，完成以下操作之一，然后选择 **Next**（下一步）。
      + 选择 **Amazon S3 URL**，然后在文本框中指定一个 URL。
      + 选择 **Upload a template file (上传模板文件)**，然后浏览要上传的文件。

   1. 在 **Identify resources (标识资源)** 页面上，标识您要移动的资源（在本示例中为 `GamesTable`）。有关更多信息，请参阅 [资源标识符](import-resources-manually.md#resource-import-identifiers-unique-ids)。

      1. 在 **Identifier property (标识符属性)** 下面，选择资源标识符的类型。例如，可以使用 `TableName` 属性标识 `AWS::DynamoDB::Table` 资源。

      1. 在 **Identifier value (标识符值)** 下面，键入实际属性值。例如 `GamesTables`。

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

   1. 在 **Specify stack details (指定堆栈详细信息)** 页面上，修改任何参数，然后选择 **Next (下一步)**。这会自动创建一个更改集。
**重要**  
如果您修改的现有参数启动创建、更新或删除操作，导入操作将失败。

   1. 在**查看 *TargetStackName*** 页面上，确认正在导入正确的资源，然后选择**导入资源**。这会自动启动在上一步中创建的更改集。此时，任何[堆栈级标签](cfn-console-create-stack.md#configure-stack-options)将应用于导入的资源。

   1. 将显示父堆栈的 **Stack details**（堆栈详细信息）页面的 **Events（事件）**窗格。  
![\[控制台中的 Events (事件) 选项卡。\]](http://docs.aws.amazon.com/zh_cn/AWSCloudFormation/latest/UserGuide/images/import-events.png)
**注意**  
在该导入操作完成后，无需对父堆栈运行偏差检测，因为 `AWS::CloudFormation::Stack` 资源已由 CloudFormation 进行管理。

## 使用 AWS CLI重构堆栈
<a name="refactor-stacks-cli"></a>

1. 在源模板中，为要移动的资源指定 `Retain` [DeletionPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-attribute-deletionpolicy.html)。

   在以下示例源模板中，`GamesTable` 是该重构的目标。  
**Example JSON**  

   ```
   {
       "AWSTemplateFormatVersion": "2010-09-09",
       "Description": "Import test",
       "Resources": {
           "ServiceTable":{
              "Type":"AWS::DynamoDB::Table",
              "Properties":{
                 "TableName":"Service",
                 "AttributeDefinitions":[
                    {
                       "AttributeName":"key",
                       "AttributeType":"S"
                    }
                 ],
                 "KeySchema":[
                    {
                       "AttributeName":"key",
                       "KeyType":"HASH"
                    }
                 ],
                 "ProvisionedThroughput":{
                    "ReadCapacityUnits":5,
                    "WriteCapacityUnits":1
                 }
              }
           },
           "GamesTable": {
               "Type": "AWS::DynamoDB::Table",
               "DeletionPolicy": "Retain",
               "Properties": {
                   "TableName": "Games",
                   "AttributeDefinitions": [
                       {
                           "AttributeName": "key",
                           "AttributeType": "S"
                       }
                   ],
                   "KeySchema": [
                       {
                           "AttributeName": "key",
                           "KeyType": "HASH"
                       }
                   ],
                   "ProvisionedThroughput": {
                       "ReadCapacityUnits": 5,
                       "WriteCapacityUnits": 1
                   }
               }
           }
       }
   }
   ```

1. 更新源堆栈以将删除策略应用于资源。

   ```
   aws cloudformation update-stack --stack-name SourceStackName
   ```

1. 从源模板中删除资源、相关参数和输出，然后将它们添加到目标模板中。

   现在，源模板如下所示。  
**Example JSON**  

   ```
   {
       "AWSTemplateFormatVersion": "2010-09-09",
       "Description": "Import test",
       "Resources": {
           "ServiceTable":{
              "Type":"AWS::DynamoDB::Table",
              "Properties":{
                 "TableName":"Service",
                 "AttributeDefinitions":[
                    {
                       "AttributeName":"key",
                       "AttributeType":"S"
                    }
                 ],
                 "KeySchema":[
                    {
                       "AttributeName":"key",
                       "KeyType":"HASH"
                    }
                 ],
                 "ProvisionedThroughput":{
                    "ReadCapacityUnits":5,
                    "WriteCapacityUnits":1
                 }
              }
           }
       }
   }
   ```

   以下示例目标模板当前具有 `PlayersTable` 资源，现在还包含 `GamesTable`。  
**Example JSON**  

   ```
   {
       "AWSTemplateFormatVersion": "2010-09-09",
       "Description": "Import test",
       "Resources": {
           "PlayersTable": {
               "Type": "AWS::DynamoDB::Table",
               "Properties": {
                   "TableName": "Players",
                   "AttributeDefinitions": [
                       {
                           "AttributeName": "key",
                           "AttributeType": "S"
                       }
                   ],
                   "KeySchema": [
                       {
                           "AttributeName": "key",
                           "KeyType": "HASH"
                       }
                   ],
                   "ProvisionedThroughput": {
                       "ReadCapacityUnits": 5,
                       "WriteCapacityUnits": 1
                   }
               }
           },
           "GamesTable": {
               "Type": "AWS::DynamoDB::Table",
               "DeletionPolicy": "Retain",
               "Properties": {
                   "TableName": "Games",
                   "AttributeDefinitions": [
                       {
                           "AttributeName": "key",
                           "AttributeType": "S"
                       }
                   ],
                   "KeySchema": [
                       {
                           "AttributeName": "key",
                           "KeyType": "HASH"
                       }
                   ],
                   "ProvisionedThroughput": {
                       "ReadCapacityUnits": 5,
                       "WriteCapacityUnits": 1
                   }
               }
           }
       }
   }
   ```

1. 更新源堆栈以从堆栈中删除 `GamesTable` 资源及其相关参数和输出。

   ```
   aws cloudformation update-stack --stack-name SourceStackName
   ```

1. 按照以下 JSON 字符串格式，编写要导入的实际资源及其唯一标识符的列表。有关更多信息，请参阅 [资源标识符](import-resources-manually.md#resource-import-identifiers-unique-ids)。

   ```
   [{"ResourceType":"AWS::DynamoDB::Table","LogicalResourceId":"GamesTable","ResourceIdentifier":{"TableName":"Games"}}]
   ```

   或者，您可以在配置文件中指定 JSON 格式参数。

   例如，要导入 `GamesTable`，可以创建一个包含以下配置的 *ResourcesToImport.txt* 文件。

   ```
   [
     {
         "ResourceType":"AWS::DynamoDB::Table",
         "LogicalResourceId":"GamesTable",
         "ResourceIdentifier": {
           "TableName":"Games"
         }
     }
   ]
   ```

1. 要创建更改集，请使用以下 **create-change-set** 命令并替换占位符文本。对于 `--change-set-type` 选项，请指定 **IMPORT** 的值。对于 `--resources-to-import` 选项，将示例 JSON 字符串替换为您刚刚创建的实际 JSON 字符串。

   ```
   aws cloudformation create-change-set \
       --stack-name TargetStackName --change-set-name ImportChangeSet \
       --change-set-type IMPORT \
       --template-body file://TemplateToImport.json \
       --resources-to-import "'[{"ResourceType":"AWS::DynamoDB::Table","LogicalResourceId":"GamesTable","ResourceIdentifier":{"TableName":"Games"}}]'"
   ```
**注意**  
`--resources-to-import` 不支持内联 YAML。JSON 字符串中对转义引号的要求因终端而异。有关更多信息，请参阅《AWS Command Line Interface User Guide**》中的 [Using quotation marks inside strings](https://docs.aws.amazon.com/cli/latest/userguide/cli-usage-parameters-quoting-strings.html#cli-usage-parameters-quoting-strings-containing)。

   或者，您可以使用文件 URL 作为 `--resources-to-import` 选项的输入内容，如以下示例所示。

   ```
   --resources-to-import file://ResourcesToImport.txt
   ```

1. 查看更改集，以确保将正确的资源导入到目标堆栈中。

   ```
   aws cloudformation describe-change-set \
       --change-set-name ImportChangeSet
   ```

1. 要启动更改集并导入资源，请使用以下 **execute-change-set** 命令并替换占位符文本。此时，任何堆栈级标签将应用于导入的资源。在成功完成该操作后 `(IMPORT_COMPLETE)`，将成功导入资源。

   ```
   aws cloudformation execute-change-set \
       --change-set-name ImportChangeSet --stack-name TargetStackName
   ```
**注意**  
在该导入操作完成后，无需对目标堆栈运行偏差检测，因为资源已由 CloudFormation 进行管理。