You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Terraform使用templatefile分离SQL模板报错,求解决方案

问题:将Terraform内嵌SQL模板移至独立文件失败

现有Terraform代码运行正常,希望把main.tf中内嵌的SQL模板迁移到独立文件,尝试使用templatefile函数但未成功,问题可能出在metric_info变量的结构或者模板重构上。


原main.tf代码

locals {
  # Base variables 
  base_vars = yamldecode(file("env/${var.working_env}/vars.yaml")) # TF_VAR_working_env

  # Metrics subfolder names
  metrics = distinct([
    for dir_path in fileset("../metrics-table/", "*/*/") :
    basename(dirname(dir_path))
  ])

  # Metrics configuration 
  metrics_config = flatten([
    for subfolder in local.metrics :
    [
      for file in fileset("../metrics-table/${subfolder}", "metrics.yaml") :
      merge(yamldecode(file("../metrics-table/${subfolder}/${file}")), { subfolder = subfolder })
    ]
  ])

  # Metrics info
  metric_info = [
    {
      metrics = merge([
        for metric_config in local.metrics_config :
        {
          for metric_name, metric_data in metric_config.metrics :
          metric_name => {
            name          = metric_data.name
            metric_table  = metric_config.metric_table
            numerator     = metric_data.numerator
            denominator   = metric_data.denominator
          }
        }
        ]...)
    }
  ]

}

#  TEMPLATE ALL_METRICS.SQL

data "template_file" "all_metrics_sql" {
  template = <<EOT
# Parte 1 do arquivo SQL
-- BEGIN Parte 1 do arquivo SQL
WITH
%{ for metric_info in local.metric_info }
  %{ for metric_name, metric_data in metric_info.metrics }
    ${metric_name} AS (
      SELECT
        date,
        '${metric_name}' AS metric_name,
        experiment,
        alternative,
        context_id,
        control,
        CASE
          WHEN version IS NULL THEN 'v1'
          ELSE version
        END AS version,
        device_group,
        platform,
        SUM(${metric_data.denominator}) AS denominator,
        SUM(${metric_data.numerator}) AS numerator,
        SAFE_DIVIDE(SUM(${metric_data.numerator}), SUM(${metric_data.denominator})) AS metric_value
      FROM `${local.base_vars.project}.ab_experiment_metrics.${metric_data.metric_table}`
      WHERE outlier = FALSE
      GROUP BY
        date,
        experiment,
        alternative,
        context_id,
        control,
        version,
        device_group,
        platform,
        metric_name
    ),
      
  %{ endfor }
%{ endfor }

-- END Parte 1 do arquivo SQL

# Parte 2 do arquivo SQL
-- BEGIN Parte 2 do arquivo SQL
all_metrics AS (
    SELECT * FROM conversion_rate
    %{ for metric_info in local.metric_info }
        %{ for metric_name, _ in metric_info.metrics }
            UNION ALL
            SELECT * FROM ${metric_name}
        %{ endfor }
    %{ endfor }
),
all_metrics_final as
(
    SELECT
        date,
        metric_name,
        all_metrics.experiment,
        alternative,
        control,
        version,
        device_group,
        platform,
        denominator,
        SUM(denominator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS denominator_cumsum,
        numerator,
        SUM(numerator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS numerator_cumsum,
        metric_value
    FROM all_metrics
)

SELECT *
FROM all_metrics_final
-- END Parte 2 do arquivo SQL
EOT
}

# CREATE ALL_METRICS.SQL

resource "local_file" "all_metrics_sql" {
  filename = "all_metrics.sql"
  content  = data.template_file.all_metrics_sql.rendered
}



#   OUTPUTS
output "metrics_config" {
  value = local.metrics_config
}

output "metric_info" {
  value = local.metric_info
}

output "all_metrics_sql_content" {
  value = data.template_file.all_metrics_sql.rendered
}

输出变更

Changes to Outputs:
  + metric_info    = [
      + {
          + metrics = {
              + time_to_first_play_per_login    = {
                  + denominator  = "experiment_login_count"
                  + metric_table = "time_to_first_play"
                  + name         = "time_to_first_play_per_login"
                  + numerator    = "total_time_to_first_play"
                }
              + time_to_first_play_per_session  = {
                  + denominator  = "experiment_session_count"
                  + metric_table = "time_to_first_play"
                  + name         = "time_to_first_play_per_session"
                  + numerator    = "total_time_to_first_play"
                }
              + time_to_first_play_per_user     = {
                  + denominator  = "experiment_user_count"
                  + metric_table = "time_to_first_play"
                  + name         = "time_to_first_play_per_user"
                  + numerator    = "total_time_to_first_play"
                }
              + time_to_second_play_per_creator = {
                  + denominator  = "experiment_user_count"
                  + metric_table = "time_to_second_play"
                  + name         = "time_to_second_play_per_user"
                  + numerator    = "total_time_to_second_play"
                }
              + time_to_second_play_per_login   = {
                  + denominator  = "experiment_user_count"
                  + metric_table = "time_to_second_play"
                  + name         = "time_to_second_play_per_user"
                  + numerator    = "total_time_to_second_play"
                }
              + time_to_second_play_per_name    = {
                  + denominator  = "experiment_session_count"
                  + metric_table = "time_to_second_play"
                  + name         = "time_to_second_play_per_session"
                  + numerator    = "total_time_to_second_play"
                }
            }
        },
    ]
  + metrics_config = [
      + {
          + metric_table = "time_to_first_play"
          + metrics      = {
              + time_to_first_play_per_login   = {
                  + active         = 1
                  + category       = "Métrica de vídeo"
                  + denominator    = "experiment_login_count"
                  + description    = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo"
                  + metric_title   = "Tempo para o Primeiro Play por Usuário"
                  + metric_type_id = 2
                  + name           = "time_to_first_play_per_login"
                  + numerator      = "total_time_to_first_play"
                }
              + time_to_first_play_per_session = {
                  + active         = 1
                  + category       = "Métrica de vídeo"
                  + denominator    = "experiment_session_count"
                  + description    = "Tempo médio que cada sessão leva para dar o primeiro play em algum vídeo"
                  + metric_title   = "Tempo para o Primeiro Play por Sessão"
                  + metric_type_id = 2
                  + name           = "time_to_first_play_per_session"
                  + numerator      = "total_time_to_first_play"
                }
              + time_to_first_play_per_user    = {
                  + active         = 1
                  + category       = "Métrica de vídeo"
                  + denominator    = "experiment_user_count"
                  + description    = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo"
                  + metric_title   = "Tempo para o Primeiro Play por Usuário"
                  + metric_type_id = 2
                  + name           = "time_to_first_play_per_user"
                  + numerator      = "total_time_to_first_play"
                }
            }
          + subfolder    = "time_to_first_play"
        },
      + {
          + metric_table = "time_to_second_play"
          + metrics      = {
              + time_to_second_play_per_creator = {
                  + active         = 1
                  + category       = "Métrica de vídeo"
                  + denominator    = "experiment_user_count"
                  + description    = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo"
                  + metric_title   = "Tempo para o Primeiro Play por Usuário"
                  + metric_type_id = 2
                  + name           = "time_to_second_play_per_user"
                  + numerator      = "total_time_to_second_play"
                }
              + time_to_second_play_per_login   = {
                  + active         = 1
                  + category       = "Métrica de vídeo"
                  + denominator    = "experiment_user_count"
                  + description    = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo"
                  + metric_title   = "Tempo para o Primeiro Play por Usuário"
                  + metric_type_id = 2
                  + name           = "time_to_second_play_per_user"
                  + numerator      = "total_time_to_second_play"
                }
              + time_to_second_play_per_name    = {
                  + active         = 1
                  + category       = "Métrica de vídeo"
                  + denominator    = "experiment_session_count"
                  + description    = "Tempo médio que cada sessão leva para dar o primeiro play em algum vídeo"
                  + metric_title   = "Tempo para o Primeiro Play por Sessão"
                  + metric_type_id = 2
                  + name           = "time_to_second_play_per_session"
                  + numerator      = "total_time_to_second_play"
                }
            }
          + subfolder    = "time_to_second_play"
        },
    ]

尝试的错误代码

data "template_file" "all_metrics_sql" {
  template = templatefile("${path.module}/template.tftpl", {
    metric_info = local.metric_info.metrics
  })
}

报错信息

╷
│ Error: Not enough function arguments
│ 
│   on main.tf line 69, in data "template_file" "all_metrics_sql":
│   69:   template = templatefile("arquivo_template.tpl")
│ 
│ Function "templatefile" expects 2 argument(s). Missing value for
│ "vars".
╵
╷
│ Error: Unsupported attribute
│ 
│   on main.tf line 72, in data "template_file" "all_metrics_sql":
│   72:     metric_info = local.metric_info.metrics
│     ├────────────────
│     │ local.metric_info is tuple with 1 element
│ 
│ This value does not have any attributes.

解决方案

1. 修正变量引用错误

local.metric_info是包含单个元素的元组,必须通过索引[0]访问内部元素后,再获取metrics属性,即local.metric_info[0].metrics。

2. 重构模板文件与代码

步骤1:创建独立模板文件template.tftpl

将原内嵌的SQL模板内容复制到该文件,同时调整循环逻辑(直接遍历传入的metrics字典,无需嵌套外层循环):

# Parte 1 do arquivo SQL
-- BEGIN Parte 1 do arquivo SQL
WITH
%{ for metric_name, metric_data in metrics }
    ${metric_name} AS (
      SELECT
        date,
        '${metric_name}' AS metric_name,
        experiment,
        alternative,
        context_id,
        control,
        CASE
          WHEN version IS NULL THEN 'v1'
          ELSE version
        END AS version,
        device_group,
        platform,
        SUM(${metric_data.denominator}) AS denominator,
        SUM(${metric_data.numerator}) AS numerator,
        SAFE_DIVIDE(SUM(${metric_data.numerator}), SUM(${metric_data.denominator})) AS metric_value
      FROM `${base_vars.project}.ab_experiment_metrics.${metric_data.metric_table}`
      WHERE outlier = FALSE
      GROUP BY
        date,
        experiment,
        alternative,
        context_id,
        control,
        version,
        device_group,
        platform,
        metric_name
    ),
%{ endfor }

-- END Parte 1 do arquivo SQL

# Parte 2 do arquivo SQL
-- BEGIN Parte 2 do arquivo SQL
all_metrics AS (
    SELECT * FROM conversion_rate
    %{ for metric_name, _ in metrics }
            UNION ALL
            SELECT * FROM ${metric_name}
    %{ endfor }
),
all_metrics_final as
(
    SELECT
        date,
        metric_name,
        all_metrics.experiment,
        alternative,
        control,
        version,
        device_group,
        platform,
        denominator,
        SUM(denominator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS denominator_cumsum,
        numerator,
        SUM(numerator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS numerator_cumsum,
        metric_value
    FROM all_metrics
)

SELECT *
FROM all_metrics_final
-- END Parte 2 do arquivo SQL

步骤2:修改main.tf中的SQL生成逻辑

直接使用templatefile函数生成内容,无需依赖template_file数据源(Terraform推荐直接使用函数替代旧版数据源):

# 替换原template_file数据源与local_file资源
resource "local_file" "all_metrics_sql" {
  filename = "all_metrics.sql"
  content  = templatefile("${path.module}/template.tftpl", {
    base_vars = local.base_vars
    metrics   = local.metric_info[0].metrics
  })
}

内容的提问来源于stack exchange,提问作者lubi_nascimento

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 20:24:54