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

如何在Terraform插件框架中读取types.Dynamic类型的JSON数据

问题:Terraform插件框架中解析API返回的JSON字符串到types.Dynamic字段失败

我正在使用Terraform插件框架(v1.8.0)开发Ansible Forms的自定义Terraform提供者。API返回的extravars字段是包含多类型值映射的JSON字符串,尝试用types.Dynamic类型读取该字段,但现有代码无法正常工作。


现有代码实现

数据源模型

type JobDataSourceModel struct {
    CxProfileName types.String  `tfsdk:"cx_profile_name"`
    ...
    Extravars     types.Dynamic `tfsdk:"extravars"`
    ...
}

Schema()方法实现

func (d *JobDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
    resp.Schema = schema.Schema{
        Attributes: map[string]schema.Attribute{
            ...
            "extravars": schema.DynamicAttribute{
                MarkdownDescription: "",
                Computed:            true,
            },
            ...
        },
    }
}

Read()方法实现

func (d *JobDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
    var data JobDataSourceModel
    ...
    data.Extravars = types.DynamicValue(restInfo.Data.Extravars)
    ...
}

API返回数据示例

{
    "status": "success",
    "message": "job ran successfully",
    "data": {
        "id": 86,
        "form": "Demo Form Ansible No input",
        "target": "Demo Form Ansible No input",
        "status": "success",
        "start": "2024-05-02T15:35:16.000Z",
        "end": "2024-05-02T15:35:18.000Z",
        "user": "admin",
        "user_type": "local",
        "job_type": "ansible",
        "extravars": "{\"name\":\"vm1\",\"region\":\"myregion\",\"opco\":\"myopco\",\"svm_name\":\"mysvm_name\",\"exposure\":\"myexposure\",\"env\":\"myenv\",\"dataclass\":\"mydataclass\",\"share_name\":\"myshare_name\",\"accountid\":\"myaccountid\",\"size\":\"mysize\",\"protection_required\":\"myprotection_required\",\"ansibleforms_user\":{\"username\":\"admin\",\"id\":1,\"type\":\"local\",\"groups\":[\"local/admins\"],\"roles\":[\"public\",\"admin\"]},\"__playbook__\":\"dummy.yaml\",\"__inventory__\":\"\",\"__credentials__\":{\"vcenterCredentials\":\"VCENTER\"}}",
        "credentials": "{\"vcenterCredentials\":\"VCENTER\"}",
        "notifications": "{}",
        "approval": null,
        "step": null,
        "parent_id": null,
        "subjobs": null,
        "no_of_records": 45,
        "counter": 8,
        "output": "<span class='has-text-warning'>[WARNING]: No inventory was parsed, only implicit localhost is available</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n<span class='has-text-warning'>[WARNING]: provided hosts list is empty, only localhost is available. Note that</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n<span class='has-text-warning'>the implicit localhost does not match 'all'</span>\r\n<span class='has-text-warning'>[WARNING]: Found variable using reserved name: name</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n\n<span class='has-text-weight-bold'>PLAY [This is a hello-world example] *******************************************</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n\r\n<span class='has-text-weight-bold'>TASK [Gathering Facts] *********************************************************</span>\r\n<span class='has-text-success'>ok: [localhost]</span> <span class='tag is-info is-light'>2024-05-02 15:35:18</span>\r\n\r\n<span class='has-text-weight-bold'>TASK [Output 'Welcome'.] *******************************************************</span>\r\n<span class='has-text-success'>ok: [localhost] => </span> <span class='tag is-info is-light'>2024-05-02 15:35:18</span>\r\n<span class='has-text-success'>  msg: Hi there and welcome to ansible</span>\r\n\r\n<span class='has-text-weight-bold'>PLAY RECAP *********************************************************************</span>\r\n<span class=''>localhost                  : <span class='tag is-success'>ok=2</span>    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0</span>\r\n<span class='has-text-success'>ok: [Playbook finished] with status (0)</span> <span class='tag is-info is-light'>2024-05-02 15:35:18</span>"
    }
}

问题原因与修复方法

问题出在直接将JSON字符串赋值给types.Dynamic类型:types.DynamicValue需要接收Terraform框架的tfjson.Value类型,而非原始JSON字符串。需先将JSON字符串解析为Go的通用类型,再转换为框架兼容的动态值。

修改后的Read()方法代码:

func (d *JobDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
    var data JobDataSourceModel
    ...
    
    // 解析extravars JSON字符串为通用类型
    var extravars interface{}
    err := json.Unmarshal([]byte(restInfo.Data.Extravars), &extravars)
    if err != nil {
        resp.Diagnostics.AddError(
            "解析Extravars JSON失败",
            "无法反序列化extravars字符串: "+err.Error(),
        )
        return
    }
    
    // 将解析后的值转换为Terraform动态值
    dynamicVal, err := types.DynamicValueFrom(ctx, schema.DynamicType{}, extravars)
    if err != nil {
        resp.Diagnostics.AddError(
            "创建动态值失败",
            "无法将解析后的extravars转换为动态值: "+err.Error(),
        )
        return
    }
    
    data.Extravars = dynamicVal
    ...
}

关键说明

  • 使用json.Unmarshal解析JSON字符串为interface{},保留原有的嵌套结构与多类型值
  • 通过types.DynamicValueFrom完成Go类型到Terraform框架动态值的转换,确保类型兼容
  • 添加错误处理,将解析/转换过程中的异常反馈到Terraform诊断系统,便于排查问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:20:09