拥有不同订阅下的 Terraform 天蓝色状态文件

Posted

技术标签:

【中文标题】拥有不同订阅下的 Terraform 天蓝色状态文件【英文标题】:Having the Terraform azure state file under different subscription 【发布时间】:2019-12-08 22:10:20 【问题描述】:

我在 Azure 中有两个订阅。我们称它们为 sub-dev 和 sub-prod。在 sub-dev 下,我有开发资源(在资源组 rg-dev 中),在 sub-prod 资源下用于生产(在资源组 rg-prod 中)。

现在,我希望 dev 和 prod 都只有一个状态文件。我可以在使用 Terraform 工作区(开发和产品)时执行此操作。在子开发 (rg-dev) 下有一个名为 tfsate 的存储帐户。它有一个容器等。Azure 后端配置如下:

terraform 
  backend "azurerm" 
    resource_group_name  = "rg-dev"
    storage_account_name = "tfstate"
    container_name       = "tfcontainer"
    key                  = "terraform.tfstate" 
  

如果我想申请 dev 环境,我必须将 Az Cli 切换到 sub-dev。同样,对于生产,我将不得不使用 sub-prod。我使用 az cli 切换默认订阅:

az account set -s sub-prod

问题是该州的存储帐户在 sub-dev 下,而不是 sub-prod。当默认订阅设置为子产品时,尝试terraform init(或应用)时会出现访问错误。

Error: Failed to get existing workspaces: Error retrieving keys for Storage Account "tfstate": storage.AccountsClient#ListKeys: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client 'user@example.com' with object id '<redacted>' does not have authorization to perform action 'Microsoft.Storage/storageAccounts/listKeys/action' over scope '/subscriptions/sub-prod/resourceGroups/rg-dev/providers/Microsoft.Storage/storageAccounts/tfstate' or the scope is invalid. If access was recently granted, please refresh your credentials."

我尝试了几件事:

我加了subscription_id = "sub-dev" 我为 tfstate 存储帐户生成了一个 SAS 令牌并添加了 sas_token 配置值(已删除 resource_group_name

但徒劳并得到同样的错误。

我尝试az logout,但 terraform 要求我先登录。我是否必须以某种方式调整 Azure 端的权限(这很难,因为 Azure 环境是由第 3 方配置的)或者 Terraform 是否支持这种让您的状态文件处于不同订阅设置下的方式?

【问题讨论】:

您不希望只有一个状态文件用于 dev 和 prod。如果你以不同的方式应用它们(例如terraform apply devterraform apply production 或类似的),那么你绝对需要两个不同的状态文件,否​​则部署第二个将覆盖第一个,破坏第一个中的所有内容。而且您也不希望同时应用开发和生产。 我正在使用工作区(开发和产品),所以我可以使用单状态文件。要使用不同的状态文件,我应该在后端定义中添加一些条件值吗? 我不建议将工作空间用于静态环境。它们增加了事物的复杂性,使您更难从代码/文件结构的一瞥中看到您已部署的内容,因此您错过了 IaC 的一大优势。 那么,您是否建议拥有两个具有相同 tf 文件(参数化资源组名称)和不同后端配置的不同目录(dev 和 prod)? 是的。我会使用模块或符号链接来保持干燥,只通过不同的 tfvars 文件和提供程序配置文件更改您需要的内容。关于如何在 SO 上构建它,还有许多其他问题和答案。 【参考方案1】:

无论好坏(我没有对其他组织 terraform 的方法进行太多实验),我们都按照您所描述的方式使用 terraform。远程后端中的状态文件,在我的资源的不同订阅中。创建工作区是为了处理部署环境。

我们的状态文件是这样指定的:

terraform 
  required_version = ">= 0.12.6"
  
  backend "azurerm" 
    subscription_id      = "<subscription GUID storage account is in>"
    resource_group_name  = "terraform-rg"
    storage_account_name = "myterraform"
    container_name       = "tfstate"
    key                  = "root.terraform.tfstate"
  

我们将 terraform 存储帐户保存在对我们的部署完全不同的订阅中,但这不是必需的。

像这样配置您的状态文件时,它使用与 CLI 交互的人员的上下文通过 az CLI 向远程后端进行身份验证。此人需要对存储帐户具有“读取者和数据访问权限”角色才能在运行时动态检索存储帐户密钥。

配置上述状态文件后,执行 Terraform 将是

az login
az account set -s "<name of subscription where you want to create resources>"
terraform init
terraform plan
terraform apply

【讨论】:

您能否更新您的答案以说明您在登录时使用的是哪个订阅(terraform 存储帐户订阅/资源订阅)? 我必须改用az account set --subscription "&lt;&lt;sub&gt;&gt;" 嗨@haodeon。这里的情况相同。但是,我正在使用服务主体运行 terraform 脚本,该服务主体仅对我要创建资源的订阅具有权限。 terraform init 正在接收 StatusCode 403,即使我正确添加了存储帐户的 access_key。 这似乎对我不起作用。我已将 subscription_id 属性添加到 azurerm 后端块,但它仍然会引发相同的错误。就像它忽略了 subscription_id 属性。我正在使用 terraform 0.13 和 azurerm 提供程序版本 2.44.0 @RenatoSilva 要使用对存储帐户没有 RBAC 权限的服务主体,需要存储访问密钥。我不确定为什么在你的情况下它不起作用。我自己测试过,效果很好,我通过环境变量使用它。【参考方案2】:

还有另一种方法可以做到这一点。您可以在其他订阅上使用与存储帐户关联的访问密钥并将其导出为环境变量。 重击:

export ARM_ACCESS_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)

Powershell:

$env:ARM_ACCESS_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv)

【讨论】:

以上是关于拥有不同订阅下的 Terraform 天蓝色状态文件的主要内容,如果未能解决你的问题,请参考以下文章

Terraform - 部署到多个 Azure 订阅

带有 CSV 文件的天蓝色 Terraform 参数

Terraform:删除身份块不会删除从资源天蓝色逻辑应用程序分配的身份

使用 terraform 脚本的 pubsub 主题和订阅之间的依赖关系

如何从不同的 AAD 租户获取管理组和订阅?

Terraform 模块 azure 事件订阅可选字段