如何将手动更改导入Terraform远程状态

时间:2017-05-13 06:46:18

标签: amazon-web-services terraform

我是terraform的新手 - 我已经在s3中创建了远程tfstate,现在我的AWS基础架构中也进行了一些手动更改。我需要将这些手动更改导入到tfstate中。

我对某些资源使用了import命令,但是对于某些资源,例如IAM策略等,没有这样的导入命令。

此外,一些资源(如DB)会随着添加的新参数而更改,我也需要导入它们。当我尝试导入这些更改时,它说:

Error importing: 1 error(s) occurred:

* Can't import aws_security_group.Q8SgProdAdminSshInt, would collide
  with an existing resource.

Please remove or rename this resource before continuing.

任何帮助将不胜感激。感谢。

2 个答案:

答案 0 :(得分:21)

在直接回答这个问题之前,我认为一些背景会有所帮助:

在幕后,Terraform维护一个状态文件,其中包含从配置中的资源到基础提供程序API中的对象的映射。使用Terraform创建新对象时,创建的对象的id将自动保存在状态中,以便将来的命令可以找到引用的对象以进行读取,更新和删除操作。

然后,

terraform import是在状态文件中创建条目的另一种方法。而不是创建新对象并记录其id,而是用户在命令行上提供id。 Terraform 使用该id读取对象,并将结果添加到状态文件中,之后在状态上与Terraform自己创建的资源无法区分。

所有这些都说明了,让我们一个一个地解决你的问题。

导入不支持terraform import

的资源

由于每个资源都需要少量验证和数据获取代码才能导入,因此目前不支持导入所有资源。

鉴于我们对terraform import上述内容的了解,理论上 ,可以跳过Terraform对提供的ID的验证,而是手动添加国家的资源。 这是一项高级操作,必须小心谨慎,以免破坏状态

首先,将状态检索到您将用于本地工作的本地文件中:

terraform state pull >manual-import.tfstate

这将创建一个文件manual-import.tfstate,您可以在文本编辑器中打开该文件。它使用JSON语法,因此虽然它的内部结构没有记录为稳定格式,但只要我们保持与预期结构一致,我们就可以仔细编辑它。

最简单的方法是找到与您要导入和复制并编辑它的模块位于同一模块中的现有资源。我们假设我们有一个resources对象:

"resources": {
    "null_resource.foo": {
        "type": "null_resource",
        "depends_on": [],
        "primary": {
            "id": "5897853859325638329",
            "attributes": {
                "id": "5897853859325638329"
            },
            "meta": {},
            "tainted": false
        },
        "deposed": [],
        "provider": ""
    }
},

resources对象中的每个属性都对应于配置中的资源。属性名称是资源的类型和名称。在这种情况下,资源类型为null_resource,属性名称为foo。在您的情况下,您可能会在此处看到类似aws_instance.server的内容。

对于许多资源(但不是全部!),id属性是需要填充的主要内容。因此,我们可以为假设的IAM策略复制此结构:

"resources": {
    "null_resource.foo": {
        "type": "null_resource",
        "depends_on": [],
        "primary": {
            "id": "5897853859325638329",
            "attributes": {
                "id": "5897853859325638329"
            },
            "meta": {},
            "tainted": false
        },
        "deposed": [],
        "provider": ""
    },
    "aws_iam_policy.example": {
        "type": "aws_iam_policy",
        "depends_on": [],
        "primary": {
            "id": "?????",
            "attributes": {
                "id": "?????"
            },
            "meta": {},
            "tainted": false
        },
        "deposed": [],
        "provider": ""
    }
},

此步骤的挑战是弄清楚此资源需要哪种ID。知道这一点的唯一可靠方法是read the code,它告诉我该资源期望id是策略的完整ARN。

有了这些知识,我们将上例中的两个?????序列替换为我们要导入的策略的ARN。

手动更改状态后,需要更新文件顶层的serial号码。 Terraform预计任何新的更改都会有更高的序列号,因此我们可以增加此数字。

完成更新后,我们必须将更新后的状态文件上传回Terraform:

terraform state push manual-import.tfstate

最后,我们可以要求Terraform刷新状态以确保其有效:

terraform refresh

同样,这是一个非常危险的过程,因为状态文件是Terraform与底层系统关系的记录,如果此文件的内容丢失,则很难恢复。简单地更换资源通常比完成所有这些工作更容易,除非它已经在您的基础架构中担任关键角色,并且没有可用的优雅迁移策略。

与现有资源发生冲突的进口

您的问题中给出的错误消息是关于导入"碰撞"使用现有资源:

Error importing: 1 error(s) occurred:

* Can't import aws_security_group.Q8SgProdAdminSshInt, would collide with an existing resource.

Please remove or rename this resource before continuing.

此消息的含义是,当Terraform尝试将新资源写入状态文件时,它会找到名称为aws_security_group.Q8SgProdAdminSshInt的资源条目。这表明它已经被导入或者Terraform已经创建了一个新的安全组。

您可以检查状态中现有资源的属性:

terraform state show aws_security_group.Q8SgProdAdminSshInt

将返回的数据与您尝试导入的安全组进行比较。如果id匹配,则无需执行任何操作,因为资源已经导入。

如果ids 匹配,那么您需要确定哪两个对象是您要保留的对象。如果您想保留Terraform已有的那个,您可以手动删除您尝试导入的那个。

如果您想保留您尝试导入的那个,可以从Terraform状态中删除不需要的那个,以便让导入成功:

terraform state rm aws_security_group.Q8SgProdAdminSshInt

请注意,这只会使Terraform"忘记"资源;它仍然存在于EC2中,需要通过控制台,命令行工具或API手动删除。请务必在删除之前记下id,以确保您可以找到它以便进行清理。

答案 1 :(得分:0)

对于资源,您必须使用导入功能或手动添加像块一样的terraform状态。

或者如果您提到dB配置的配置有任何变化..如果dB资源由terraform远程状态管理.. terraform refresh将帮助您..