我正在使用新的Terraform插件框架创建一个自定义Terraform提供程序。
我想把这样的对象保存到terraform状态:
"attributes": {
"name_servers": {
"ns1": {
"host": "host1",
"id": 100,
"ip": "",
"is_used": true
},
"ns2": {
"host": "host2",
"id": 101,
"ip": "",
"is_used": true,
}
},
"domain": "example.com"
},
它正在使用以下terraform资源:
...
type FooResourceModel struct {
Domain types.String `tfsdk:"domain"`
NameServers map[string]NameServersModel `tfsdk:"name_servers"`
}
type NameServersModel struct {
ID types.Int64 `tfsdk:"id"`
Host types.String `tfsdk:"host"`
IP types.String `tfsdk:"ip"`
IsUsed types.Bool `tfsdk:"is_used"`
}
func (r *FooNSResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"domain": schema.StringAttribute{
Required: true,
},
"name_servers": schema.MapNestedAttribute{
Required: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"id": schema.Int64Attribute{
Computed: true,
},
"host": schema.StringAttribute{
Optional: true,
Computed: true,
},
"ip": schema.StringAttribute{
Optional: true,
Computed: true,
},
"is_used": schema.BoolAttribute{
Computed: true,
},
},
},
},
},
}
}
func (r *FooNSResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data *FooNSResourceModel
diags := req.State.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
serviceName := data.ServiceName.ValueString()
// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}
nameServers, err := getNs(r, serviceName)
data.NameServers = nameServers
diags = resp.State.Set(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
func getNs(r *FooNSResource, domain string) (map[string]NameServersModel, error) {
nameServers := make(map[string]NameServersModel)
var ids []uint64
err := r.client.Get(domain, &ids)
if err != nil {
return nil, err
}
for key, id := range ids {
nsResponse := NameServerResponse{}
err := r.client.Get(id, &nsResponse)
if err != nil {
return nil, err
}
nameServers["ns"+strconv.Itoa(key+1)] = NameServersModel{
ID: types.Int64Value(int64(nsResponse.Id)),
Host: types.StringValue(nsResponse.GetHost()),
IP: types.StringValue(nsResponse.GetIP()),
IsUsed: types.BoolValue(nsResponse.IsUsed),
}
}
return nameServers, nil
}
func (r *FooNSResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
serviceName := req.ID
nameServers, err := getNs(r, serviceName)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("domain"), serviceName)...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("name_servers"), nameServers)...)
}
...
所以当我从API导入数据时,它会将其保存到terraform状态。
我的Terraform文件中有这个:
...
resource "my_custom_provider" "my-server" {
service_name = "example.com"
name_servers = {
"ns1" = {
host = "host1"
},
"ns2" = {
host = "host2"
},
}
}
...
当我运行地形计划时,它说一切正常:No changes. Your infrastructure matches the configuration.
但是当我添加ip
值时:
...
resource "my_custom_provider" "my-server" {
service_name = "example.com"
name_servers = {
"ns1" = {
host = "host1"
},
"ns2" = {
host = "host2"
ip = "127.0.0.1"
},
}
}
...
然后Terraform想要更新所有内容:
Terraform will perform the following actions:
# my_custom_provider.my-server will be updated in-place
~ resource "my_custom_provider" "my-server" {
~ name_servers = {
~ "ns1" = {
~ id = 100 -> (known after apply)
~ ip = "" -> (known after apply)
~ is_used = true -> (known after apply)
# (1 unchanged attribute hidden)
},
~ "ns2" = {
~ id = 101 -> (known after apply)
~ ip = "" -> "127.0.0.1"
~ is_used = true -> (known after apply)
# (1 unchanged attribute hidden)
},
}
# (1 unchanged attribute hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
name_servers
中未更改的属性是my host。
问题是:为什么Terraform认为一切都会改变?我如何防止它,所以只有ns 1或ns 2值host/ip会被更新?
1条答案
按热度按时间kmbjn2e31#
我找到了一个解决方案- PlanModifier:https://developer.hashicorp.com/terraform/plugin/framework/resources/plan-modification#common-use-case-attribute-plan-modifiers
使用
PlanModifier
和UseStateForUnknown()
函数:UseStateForUnknown():复制先前的状态值(如果不为空)。这对于减少(应用后已知)已知不随时间变化的计算属性的计划输出很有用。
Schema现在看起来像这样:
我为每个属性添加了计划修改器,现在terraform计划中的输出看起来应该是这样的。
我不知道这是否是最好的解决方案,但它的作品:)测试Terraform 1.4.2.