OpenTofu Azure 远端状态后端验收测试指南:环境准备、鉴权方式与测试运行全解析
【免费下载链接】opentofuOpenTofu lets you declaratively manage your cloud infrastructure.项目地址: https://gitcode.com/gh_mirrors/op/opentofu
OpenTofu 的 Azure 远端状态后端(backend "azurerm")将terraform.tfstate存放在 Azure Blob Storage 中,并通过存储账户的访问密钥(Access Key)、SAS Token、服务主体(Client Secret / Client Certificate)、托管身份(MSI)、AKS Workload Identity 与 Azure DevOps 服务连接等多种鉴权方式访问状态文件。本文以仓库中 internal/backend/remote-state/azure/README.md 为骨架,结合该目录下的源码与测试实现,完整讲解如何在真实 Azure 订阅上搭建测试基础设施、配置全部环境变量、运行单元测试与各类验收测试(Acceptance Tests),并深入剖析后端配置的底层鉴权选择逻辑。
一、测试体系概览:单元测试与验收测试
Azure 后端的状态管理与客户端实现测试集中在两个文件:
- backend_test.go:验证后端(Backend)的配置解析、状态管理、锁与强制解锁等能力;
- client_test.go:验证远端状态客户端(RemoteClient)的读写、元数据维护、锁等能力。
这两个文件同时包含单元测试与验收测试两类用例,判别标准只有一个:凡测试函数名以TestAcc...开头即为验收测试,其余皆为单元测试。
单元测试不需要任何 Azure 环境即可运行,例如:
TestBackend_impl:仅断言*Backend实现了backend.Backend接口(见 backend_test.go);TestBackendConfig与TestBackendConfig_Timeout:仅实例化客户端、解析配置,不发起任何真实请求、不产生费用(见 backend_test.go);TestBackendPagination:使用 mock 客户端模拟 10,000 个 blob 的分页遍历,验证getPaginatedResults的分页聚合逻辑(见 backend_test.go);TestStorageNames:验证checkAccountAndContainerNames对存储账户名与容器名的命名规则校验(见 backend_test.go)。
验收测试默认被跳过。跳过判定逻辑位于 helpers_test.go:
skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_AZURE_TEST") == ""即:只有同时满足TF_ACC或TF_AZURE_TEST任一非空,验收测试才会真正执行。
注意:所有测试均假定运行在Azure 公共云(Azure Public Cloud)上,未针对 Azure 中国区、Azure Government 或 Azure Stack 等特殊环境适配,在这些环境中运行会失败。
二、环境准备:基础环境变量与 Azure CLI 鉴权
运行任何验收测试之前,需要先导出如下环境变量:
export TF_AZURE_TEST=1 export TF_ACC=1此外,还必须提供 Azure 区域(location)、订阅 ID 与租户 ID:
export ARM_LOCATION=centralus export ARM_SUBSCRIPTION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' export ARM_TENANT_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'从源码角度看,ARM_SUBSCRIPTION_ID与ARM_TENANT_ID同时也是后端配置项subscription_id、tenant_id的环境变量默认值来源(见 backend.go);ARM_LOCATION则被测试的辅助函数testResourceNames读取,用于创建资源组与存储账户(见 helpers_test.go)。
基础设施引导阶段(创建资源组、存储账户与 Blob 容器)建议使用Azure CLI(az)完成登录鉴权:
az login如果无法使用 Azure CLI,也可以通过设置TF_AZURE_TEST_CLIENT_ID与TF_AZURE_TEST_CLIENT_SECRET以服务主体方式运行测试。
配置完成后即可运行以下 4 个基础的验收测试:
TestAccBackendAccessKeyBasicTestAccBackendSASTokenTestAccRemoteClientAccessKeyBasicTestAccRemoteClientSASToken
源码视角:测试如何引导基础设施
以TestAccBackendAccessKeyBasic为例(见 backend_test.go),其执行流程为:
- 调用
testAccAzureBackend(t)检查验收测试开关; - 用
acctest.RandString(4)生成随机后缀,构造资源命名(如acctestRG-backend-<时间戳>-<随机串>、acctestsa<随机串>); - 通过
auth.GetAuthMethod+Construct获得 Token 凭证; - 调用
createTestResources创建资源组、StorageV2 存储账户(StandardLRS)与 Blob 容器,并把存储账户访问密钥回写到res.storageAccountAccessKey; - 用
t.Cleanup注册destroyTestResources,无论测试成败都会删除资源组完成清理(见 helpers_test.go)。
SAS Token 测试则由getSASToken基于共享密钥生成:有效期从前 5 分钟至未来 24 小时,权限覆盖读、写、删除、列举、追加、创建、更新、处理,资源类型为sco(服务、容器、对象),强制 HTTPS(见 helpers_test.go)。
三、用 meta-test 工作空间搭建测试基础设施
目录 meta-test 中的 OpenTofu 工作空间可为验收测试创建所需的应用注册、证书、VM、AKS 集群等基础设施,使用前提是你在 Azure 订阅中具备管理员权限。详细说明见 meta-test/README.md。
首先使用 CLI 登录并指定订阅:
$ az login $ export ARM_SUBSCRIPTION_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"随后初始化并应用工作空间:
$ tofu init $ tofu apply # 需要以明文输出敏感变量时: $ tofu apply -show-sensitive应用完成后会输出类似如下的环境变量,将其复制到命令行即可供测试使用:
export TF_AZURE_TEST_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx export TF_AZURE_TEST_CLIENT_SECRET=some~secret~string同时工作空间还会生成certs.pfx文件,用于证书鉴权测试:
export TF_AZURE_TEST_CERT_PATH="meta-test/certs.pfx" export TF_AZURE_TEST_CERT_PASSWORD=SoMePaSsWoRdMSI、AKS Workload Identity、CMK 等重基础设施默认不创建,通过变量按需开启(变量定义见 meta-test/variables.tf):
# 创建 MSI 测试所需的 VM、托管身份与授权 $ tofu apply -show-sensitive -var 'use_msi=true' -var 'location=centralus' -var 'ssh_pub_key_path=~/.ssh/id_rsa.pub' # 创建 AKS Workload Identity 测试所需的集群与授权 $ tofu apply -show-sensitive -var 'use_aks_workload_identity=true' -var 'location=centralus' # 创建 CMK(客户托管密钥)测试所需的 Key Vault、加密密钥与加密作用域 $ tofu apply -show-sensitive -var 'use_cmk=true' -var 'location=centralus'location与ssh_pub_key_path均有默认值(centralus与~/.ssh/id_rsa.pub),可省略;- 拆除某类重基础设施而保留其余凭据时,只需不带对应变量重新执行
tofu apply; - 彻底清理所有测试基础设施:
$ tofu destroy四、按鉴权方式运行各类验收测试
除前文 4 个基础测试外,其余验收测试均要求以服务主体或客户端凭据完成鉴权。meta-test 工作空间会自动创建应用注册并托管凭据;若需手动创建,可在 Azure 门户中新建应用注册,并在其"证书和机密"(Certificates & secrets)部分维护客户端机密与证书。
4.1 Client Secret(客户端机密)测试
需要额外设置:
export TF_AZURE_TEST_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx export TF_AZURE_TEST_CLIENT_SECRET=some~secret~string配置完成后可运行:
TestAccBackendServicePrincipalClientSecretTestAccRemoteClientServicePrincipalClientSecret
从源码可见,这两个测试在缺少TF_AZURE_TEST_CLIENT_ID或TF_AZURE_TEST_CLIENT_SECRET时会跳过,并提示"请手动设置或使用 meta-test 目录中的 terraform plan";而ARM_TENANT_ID缺失时则直接报错(见 backend_test.go)。测试通过client_id、client_secret、use_cli=false等配置构造后端,并借助TestBackendStateForceUnlock与TestBackendStateLocksInWS验证多工作空间的锁行为。
4.2 Client Certificate(客户端证书)测试
需要额外设置:
export TF_AZURE_TEST_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx export TF_AZURE_TEST_CERT_PATH="meta-test/certs.pfx" export TF_AZURE_TEST_CERT_PASSWORD=sOmEpAsSwOrD如果应用了 meta-test 工作空间,证书会自动生成并挂载到权限合适的应用上。否则可用openssl手动生成:
# 生成密钥对 + 证书 ~> openssl req -subj '/CN=myclientcertificate/O=MyCompany, Inc./ST=CA/C=US' \ -new -newkey rsa:4096 -sha256 -days 3 -nodes -x509 -keyout client.key -out client.crt # 生成状态后端要求的 PFX 包(使用 PBE-SHA1-3DES 与 sha1 MAC 算法) ~> openssl pkcs12 -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -export -macalg sha1 -password "pass:" -out client.pfx -inkey client.key -in client.crt随后进入 Azure 门户,手动将公开的client.crt文件上传到应用注册的证书中。
对应验收测试为TestAccBackendServicePrincipalClientCertificate。源码中该测试在TF_AZURE_TEST_CLIENT_ID或TF_AZURE_TEST_CERT_PATH缺失时跳过,并会先读取验证证书文件内容(见 backend_test.go)。注意TF_AZURE_TEST_CERT_PASSWORD可以为空,测试以client_certificate_path与client_certificate_password两个配置项传入后端。
4.3 Managed Service Identity(MSI)测试
强烈建议使用 meta-test 工作空间搭建 VM 与相关授权。搭建完成后,在 README 同级目录编译测试二进制:
$ GOOS=linux GOARCH=amd64 go test -c .生成azure.test文件后传送到 VM:
$ scp azure.test azureadmin@xxx.xxx.xxx.xxx:/home/azureadminSSH 登录 VM:
$ ssh azureadmin@xxx.xxx.xxx.xxx在 VM 内配置环境变量:
export TF_AZURE_TEST=1 export TF_ACC=1 export ARM_LOCATION=centralus export ARM_SUBSCRIPTION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' export ARM_TENANT_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' export TF_AZURE_TEST_STORAGE_ACCOUNT_NAME=acctestsaxxxx export TF_AZURE_TEST_RESOURCE_GROUP_NAME=acctestRG-backend-1234567890-xxxx export TF_AZURE_TEST_CONTAINER_NAME=acctestcont最后运行 MSI 测试:
$ ./azure.test -test.v -test.run "TestAcc.*ManagedServiceIdentity"源码揭示了 MSI 测试的特殊之处:TestAccBackendManagedServiceIdentity不会自行创建资源组、存储账户或容器,而是要求全部通过上述三个TF_AZURE_TEST_*环境变量预先提供,缺失时测试直接跳过(见 backend_test.go)。测试以后端配置use_msi=true构造,用azidentity.NewManagedIdentityCredential获取凭证,并在结束后手动清理容器内 blob。
4.4 AKS Workload Identity 测试
同样强烈建议使用 meta-test 工作空间搭建 AKS 集群与授权。在 README 同级目录编译:
$ GOOS=linux GOARCH=amd64 go test -c .假设kubectl已配置为可访问default命名空间下名为shell-demo的 Pod,将测试二进制复制进去:
kubectl cp azure.test shell-demo:/进入 Pod:
kubectl exec --stdin --tty shell-demo -- /bin/sh在 Pod 内配置环境变量:
export TF_AZURE_TEST=1 export TF_ACC=1 export ARM_LOCATION=centralus export ARM_SUBSCRIPTION_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' export ARM_TENANT_ID='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' export TF_AZURE_TEST_STORAGE_ACCOUNT_NAME=acctestsaxxxx export TF_AZURE_TEST_RESOURCE_GROUP_NAME=acctestRG-backend-1234567890-xxxx export TF_AZURE_TEST_CONTAINER_NAME=acctestcont运行 AKS Workload Identity 测试:
$ ./azure.test -test.v -test.run "TestAcc.*AKSWorkloadIdentity"meta-test 工作空间会在aks_kubectl_instructions输出中给出获取集群凭据、创建带azure.workload.identity/use: "true"标签的 ServiceAccount 与 Pod 的完整 YAML(详见 meta-test/README.md),shell-demoPod 即测试运行的工作负载。对应的源码实现TestAccBackendAKSWorkloadIdentity使用use_aks_workload_identity=true配置后端,并以azidentity.NewWorkloadIdentityCredential获取凭证(见 backend_test.go)。
4.5 Azure DevOps(ADO Workload Identity)测试
该测试验证后端在 Azure DevOps Pipeline 中通过服务连接 + OIDC 联邦身份鉴权的能力。前置条件:一个 Microsoft Entra 租户及 Azure 订阅、在租户中创建 Azure DevOps 组织的权限、创建服务连接的权限,以及至少Cloud Application Administrator角色来创建测试所需的应用注册。
准备步骤:
- 访问 https://aex.dev.azure.com/ 并使用 Microsoft Entra 账号登录;
- 新建一个专用于测试的 Azure DevOps 组织;
- 若组织创建方式未自动关联目录,进入
Organization Settings -> Microsoft Entra,使用Connect Directory将 ADO 关联到你的 Entra 租户; - 为你的账号创建/添加 SSH 密钥;
- 为 ADO 组织申请 Pipeline Parallelism(审核可能需数天,也可在
Organization Settings -> Billing中关联 Azure 订阅后付费购买并行度——测试完成后务必关闭该计费项,否则即使闲置也会持续扣费); - 在
meta-test目录设置测试所需的变量:- 将
use_ado设为true以启用 Azure DevOps 相关测试(见 meta-test/variables.tf); - 导出环境变量
AZDO_ORG_SERVICE_URL,值为 ADO 组织 URL(如https://dev.azure.com/<myorg>);
- 将
- 在
meta-test目录运行tofu apply,配置 ADO 组织并创建测试所需的服务连接; - 按
tofu apply输出的后续指引继续操作。
完成上述准备后,使用 ADO 服务连接的 Azure 后端测试即可正常运行。对应的TestAccBackendADOWorkloadIdentity(见 backend_test.go)读取AZURESUBSCRIPTION_SERVICE_CONNECTION_ID、ARM_TENANT_ID、ARM_CLIENT_ID、SYSTEM_OIDCREQUESTURI、SYSTEM_ACCESSTOKEN等环境变量(这些正是 ADO Pipeline 中自动注入的变量),以后端配置use_azuread_auth=true、use_oidc=true、ado_service_connection_id=...构造,并通过azidentity.NewAzurePipelinesCredential获取凭证。
五、源码级原理:后端鉴权方式的选择顺序
理解了所有测试的鉴权路径后,可以顺带理清后端在真实运行时的鉴权决策链,这对理解测试为何要覆盖如此多组合至关重要。
在 backend.go 的configure中,鉴权按以下优先级依次判断:
- Access Key:若配置了
access_key(环境变量ARM_ACCESS_KEY),直接以存储账户访问密钥构造容器客户端; - SAS Token:否则若配置了
sas_token(环境变量ARM_SAS_TOKEN),以 SAS Token 构造容器客户端; - Azure AD(Entra ID)认证:若配置了
use_azuread_auth,直接用所选认证方法得到的 Token 凭证构造客户端; - 兜底路径:使用所选认证方法获取凭证后,进一步调用
AugmentConfig补全资源组与订阅信息,再换取存储账户共享密钥来构造客户端。
第 3、4 步所依赖的"所选认证方法"由auth.GetAuthMethod决定。在 auth/auth.go 中,认证方法按如下顺序逐一执行Validate,第一个校验通过者被选中:
- Client Certificate(
clientCertAuth) - Client Secret(
clientSecretCredentialAuth) - Azure DevOps / Pipelines(
adoAuth,注释明确指出它必须先于通用 OIDC,因为它是更特化的 OIDC 认证) - 通用 OIDC(
oidcAuth,支持 GitHub Actions、ACTIONS_ID_TOKEN_REQUEST_URL等) - Managed Identity(
managedIdentityAuth) - Workload Identity(
workloadIdentityAuth) - Azure CLI(
azureCLICredentialAuth)
全部校验失败时返回错误No valid azure auth methods found。这与 meta-test 工作空间依次覆盖 client secret、client cert、MSI、AKS Workload Identity、ADO 各类场景一一对应,也解释了为什么 README 要求按鉴权方式分组配置环境变量。
此外,无论采用哪种鉴权,configure都会先调用checkAccountAndContainerNames校验命名(见 backend.go):存储账户名须为 3–24 位小写字母与数字,容器名须为 3–63 位小写字母、数字与连字符,且不得以连字符开头/结尾、不得出现连续连字符——这也是TestStorageNames单元测试逐条验证的规则。
六、针对完整 tofu 二进制的测试技巧
有些场景需要针对完整的tofu二进制做端到端验证(例如历史上 issue #3586 所遇到的情况)。在 opentofu 仓库根目录执行:
$ GOOS=linux GOARCH=amd64 make build将产出的二进制scp到服务器后,即可在服务器上运行./tofu init、./tofu apply等命令,验证 azurerm 后端在真实二进制下的表现。
七、测试运行要点小结
- 单元测试:无需任何配置即可运行;
go test或go test ./internal/backend/remote-state/azure/...均可。 - 验收测试开关:
TF_AZURE_TEST=1与TF_ACC=1二者设其一即生效,对应 helpers_test.go 的跳过逻辑。 - 公共云限定:测试仅面向 Azure Public Cloud,在 Azure China / Government / Stack 中会失败。
- 基础设施引导:优先使用 Azure CLI 鉴权;
meta-test工作空间(main.tf)可一键创建应用注册、证书、VM、AKS 集群与 CMK 资源,并以tofu apply -show-sensitive输出全部测试所需环境变量。 - 命名规则:
TF_AZURE_TEST_CLIENT_ID/TF_AZURE_TEST_CLIENT_SECRET刻意采用不同于默认ARM_CLIENT_ID/ARM_CLIENT_SECRET的命名,避免与后端默认客户端凭据冲突、遮蔽特定鉴权方式的测试(见 helpers_test.go 注释)。 - 测试清理:除 MSI / AKS / ADO 这类复用预置基础设施的测试外,其余测试均通过
t.Cleanup自动删除所创建的资源组;meta-test 基础设施则通过tofu destroy统一清理。
通过本文的变量清单、命令序列与源码佐证,你可以从零开始在一个 Azure 订阅上完成 azurerm 后端全部六类鉴权方式的验收测试,并在排查问题时快速定位到对应的配置项与底层实现文件。
【免费下载链接】opentofuOpenTofu lets you declaratively manage your cloud infrastructure.项目地址: https://gitcode.com/gh_mirrors/op/opentofu
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考