我正在尝试使用 Terraform 在 Azure 中创建 SQL 服务器,并创建一个 JSON 用于将登录信息上传到 Hashicorp 金库。 这是我的代码:
resource "random_string" "password" {
length = 20
upper = true
min_upper = 1
lower = true
min_lower = 1
number = true
min_numeric = 1
special = false
}
resource "azurerm_mssql_server" "server" {
for_each = var.sql_servers
name = each.key
location = each.value.location
resource_group_name = each.value.resource_group_name
}
variable "sql_servers" {
type = map(object({
location = string
resource_group_name = string
}))
variable "administrator_login" {
type = string
description = "Admin login for the sql servers"
default = "admin_user"
}
locals {
sql_server_login_info = [
for key, server in var.sql_servers : {
server_name = key
administrator_password = random_string.password.result
administrator_login = var.administrator_login
}
]
}
我希望输出看起来像这样:
{
server_name = server1
admin_user = admin_user
admin_password = password
}
{
server_name = server2
admin_user = admin_user
admin_password = password
}
{
server_name = server3
admin_user = admin_user
admin_password = password
}
我正在尝试使用以下块将其上传到 Hashicorp 金库:
resource "vault_kv_secret_v2" "sql_vault" {
depends_on = [azurerm_mssql_server.server]
mount = "dba-info"
name = "/some/path/in/hashicorp/vault"
cas = 1
data_json = jsonencode(local.sql_server_login_info)
}
但是,我收到以下错误:
Error: json: cannot unmarshal array into Go value of type map[string]interface {}
我做了一些研究,认为我需要在编码之前将 local.sql_server_login_info 转换为不同的类型。 但我不知道这是否正确,如果是,我不知道该怎么做。
谢谢。
您收到的错误看起来是由于
jsonencode()
需要一个映射,但您传递的是一个列表 (local.sql_server_login_info
),并且 Vault 期望 data_json
是一个键值映射.
您需要修改
local.sql_server_login_info
的结构,使其成为一个映射而不是一个列表,以服务器名称作为键。这将解决这个问题。
您应该创建一个地图,而不是一个列表,以便每个服务器的信息的关键是它的服务器名称。尝试使用以下方法:
locals {
sql_server_login_info = {
for key, server in var.sql_servers : key => {
administrator_password = random_string.password.result
administrator_login = var.administrator_login
}
}
}
然后将
sql_server_login_info
地图直接传递到 data_json
字段中,就像您已经做的那样:
resource "vault_kv_secret_v2" "sql_vault" {
depends_on = [azurerm_mssql_server.server]
mount = "dba-info"
name = "/some/path/in/hashicorp/vault"
cas = 1
data_json = jsonencode(local.sql_server_login_info)
}
此更改会将数据编码为映射,并以服务器名称作为键,这正是 Vault 所期望的。请注意,根据我的建议,您的输出将如下所示:
{
"server1": {
"admin_user": "admin_user",
"admin_password": "password1"
},
"server2": {
"admin_user": "admin_user",
"admin_password": "password2"
},
"server3": {
"admin_user": "admin_user",
"admin_password": "password3"
}
}
但这应该可以解决
jsonencode
错误的问题,并允许您成功将登录信息上传到 HashiCorp Vault。
这就是我要采取的方法。
注意 - 您想要的确切输出格式有点棘手,因为 JSON 不支持多个独立对象,而不将它们包装在数组或其他结构中。
解决方法是使用 Terraform 的
join()
和字符串插值。
您可以更新 locals 块以生成所需的字符串输出:
locals {
sql_server_login_info_string = join("\n", [
for key, server in var.sql_servers : <<EOT
{
server_name = ${key}
admin_user = ${var.administrator_login}
admin_password = ${random_string.password.result}
}
EOT
])
}
然后将格式化的字符串传递到Vault(或输出)。 如果您想将其存储在 Vault 中,可以将其编码为字符串:
resource "vault_kv_secret_v2" "sql_vault" {
depends_on = [azurerm_mssql_server.server]
mount = "dba-info"
name = "/some/path/in/hashicorp/vault"
cas = 1
data_json = jsonencode({
sql_info = local.sql_server_login_info_string
})
}
local.sql_server_login_info_string
看起来完全如您所愿,但作为单个连接字符串。
{
server_name = server1
admin_user = admin_user
admin_password = password1
}
{
server_name = server2
admin_user = admin_user
admin_password = password2
}
{
server_name = server3
admin_user = admin_user
admin_password = password3
}