diff --git a/.changelog/12245.txt b/.changelog/12245.txt
new file mode 100644
index 00000000000..850aa4fe40f
--- /dev/null
+++ b/.changelog/12245.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+filestore: added `performance_config` field to `google_filestore_instance`
+```
\ No newline at end of file
diff --git a/google/services/filestore/resource_filestore_instance.go b/google/services/filestore/resource_filestore_instance.go
index 5abbfb5e1f1..de94f07c0e3 100644
--- a/google/services/filestore/resource_filestore_instance.go
+++ b/google/services/filestore/resource_filestore_instance.go
@@ -256,6 +256,57 @@ Please refer to the field 'effective_labels' for all of the labels present on th
Description: `The name of the location of the instance. This can be a region for ENTERPRISE tier instances.`,
ExactlyOneOf: []string{},
},
+ "performance_config": {
+ Type: schema.TypeList,
+ Optional: true,
+ Description: `Performance configuration for the instance. If not provided,
+the default performance settings will be used.`,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "fixed_iops": {
+ Type: schema.TypeList,
+ Optional: true,
+ Description: `The instance will have a fixed provisioned IOPS value,
+which will remain constant regardless of instance
+capacity.`,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "max_iops": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Description: `The number of IOPS to provision for the instance.
+max_iops must be in multiple of 1000.`,
+ },
+ },
+ },
+ ConflictsWith: []string{},
+ },
+ "iops_per_tb": {
+ Type: schema.TypeList,
+ Optional: true,
+ Description: `The instance provisioned IOPS will change dynamically
+based on the capacity of the instance.`,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "max_iops_per_tb": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Description: `The instance max IOPS will be calculated by multiplying
+the capacity of the instance (TB) by max_iops_per_tb,
+and rounding to the nearest 1000. The instance max IOPS
+will be changed dynamically based on the instance
+capacity.`,
+ },
+ },
+ },
+ ConflictsWith: []string{},
+ },
+ },
+ },
+ },
"protocol": {
Type: schema.TypeString,
Optional: true,
@@ -367,6 +418,12 @@ func resourceFilestoreInstanceCreate(d *schema.ResourceData, meta interface{}) e
} else if v, ok := d.GetOkExists("deletion_protection_reason"); !tpgresource.IsEmptyValue(reflect.ValueOf(deletionProtectionReasonProp)) && (ok || !reflect.DeepEqual(v, deletionProtectionReasonProp)) {
obj["deletionProtectionReason"] = deletionProtectionReasonProp
}
+ performanceConfigProp, err := expandFilestoreInstancePerformanceConfig(d.Get("performance_config"), d, config)
+ if err != nil {
+ return err
+ } else if v, ok := d.GetOkExists("performance_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(performanceConfigProp)) && (ok || !reflect.DeepEqual(v, performanceConfigProp)) {
+ obj["performanceConfig"] = performanceConfigProp
+ }
labelsProp, err := expandFilestoreInstanceEffectiveLabels(d.Get("effective_labels"), d, config)
if err != nil {
return err
@@ -534,6 +591,9 @@ func resourceFilestoreInstanceRead(d *schema.ResourceData, meta interface{}) err
if err := d.Set("deletion_protection_reason", flattenFilestoreInstanceDeletionProtectionReason(res["deletionProtectionReason"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
+ if err := d.Set("performance_config", flattenFilestoreInstancePerformanceConfig(res["performanceConfig"], d, config)); err != nil {
+ return fmt.Errorf("Error reading Instance: %s", err)
+ }
if err := d.Set("terraform_labels", flattenFilestoreInstanceTerraformLabels(res["labels"], d, config)); err != nil {
return fmt.Errorf("Error reading Instance: %s", err)
}
@@ -584,6 +644,12 @@ func resourceFilestoreInstanceUpdate(d *schema.ResourceData, meta interface{}) e
} else if v, ok := d.GetOkExists("deletion_protection_reason"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, deletionProtectionReasonProp)) {
obj["deletionProtectionReason"] = deletionProtectionReasonProp
}
+ performanceConfigProp, err := expandFilestoreInstancePerformanceConfig(d.Get("performance_config"), d, config)
+ if err != nil {
+ return err
+ } else if v, ok := d.GetOkExists("performance_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, performanceConfigProp)) {
+ obj["performanceConfig"] = performanceConfigProp
+ }
labelsProp, err := expandFilestoreInstanceEffectiveLabels(d.Get("effective_labels"), d, config)
if err != nil {
return err
@@ -616,6 +682,10 @@ func resourceFilestoreInstanceUpdate(d *schema.ResourceData, meta interface{}) e
updateMask = append(updateMask, "deletionProtectionReason")
}
+ if d.HasChange("performance_config") {
+ updateMask = append(updateMask, "performanceConfig")
+ }
+
if d.HasChange("effective_labels") {
updateMask = append(updateMask, "labels")
}
@@ -951,6 +1021,81 @@ func flattenFilestoreInstanceDeletionProtectionReason(v interface{}, d *schema.R
return v
}
+func flattenFilestoreInstancePerformanceConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
+ if v == nil {
+ return nil
+ }
+ original := v.(map[string]interface{})
+ if len(original) == 0 {
+ return nil
+ }
+ transformed := make(map[string]interface{})
+ transformed["iops_per_tb"] =
+ flattenFilestoreInstancePerformanceConfigIopsPerTb(original["iopsPerTb"], d, config)
+ transformed["fixed_iops"] =
+ flattenFilestoreInstancePerformanceConfigFixedIops(original["fixedIops"], d, config)
+ return []interface{}{transformed}
+}
+func flattenFilestoreInstancePerformanceConfigIopsPerTb(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
+ if v == nil {
+ return nil
+ }
+ original := v.(map[string]interface{})
+ if len(original) == 0 {
+ return nil
+ }
+ transformed := make(map[string]interface{})
+ transformed["max_iops_per_tb"] =
+ flattenFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(original["maxIopsPerTb"], d, config)
+ return []interface{}{transformed}
+}
+func flattenFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
+ // Handles the string fixed64 format
+ if strVal, ok := v.(string); ok {
+ if intVal, err := tpgresource.StringToFixed64(strVal); err == nil {
+ return intVal
+ }
+ }
+
+ // number values are represented as float64
+ if floatVal, ok := v.(float64); ok {
+ intVal := int(floatVal)
+ return intVal
+ }
+
+ return v // let terraform core handle it otherwise
+}
+
+func flattenFilestoreInstancePerformanceConfigFixedIops(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
+ if v == nil {
+ return nil
+ }
+ original := v.(map[string]interface{})
+ if len(original) == 0 {
+ return nil
+ }
+ transformed := make(map[string]interface{})
+ transformed["max_iops"] =
+ flattenFilestoreInstancePerformanceConfigFixedIopsMaxIops(original["maxIops"], d, config)
+ return []interface{}{transformed}
+}
+func flattenFilestoreInstancePerformanceConfigFixedIopsMaxIops(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
+ // Handles the string fixed64 format
+ if strVal, ok := v.(string); ok {
+ if intVal, err := tpgresource.StringToFixed64(strVal); err == nil {
+ return intVal
+ }
+ }
+
+ // number values are represented as float64
+ if floatVal, ok := v.(float64); ok {
+ intVal := int(floatVal)
+ return intVal
+ }
+
+ return v // let terraform core handle it otherwise
+}
+
func flattenFilestoreInstanceTerraformLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
if v == nil {
return v
@@ -1189,6 +1334,78 @@ func expandFilestoreInstanceDeletionProtectionReason(v interface{}, d tpgresourc
return v, nil
}
+func expandFilestoreInstancePerformanceConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
+ l := v.([]interface{})
+ if len(l) == 0 || l[0] == nil {
+ return nil, nil
+ }
+ raw := l[0]
+ original := raw.(map[string]interface{})
+ transformed := make(map[string]interface{})
+
+ transformedIopsPerTb, err := expandFilestoreInstancePerformanceConfigIopsPerTb(original["iops_per_tb"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedIopsPerTb); val.IsValid() && !tpgresource.IsEmptyValue(val) {
+ transformed["iopsPerTb"] = transformedIopsPerTb
+ }
+
+ transformedFixedIops, err := expandFilestoreInstancePerformanceConfigFixedIops(original["fixed_iops"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedFixedIops); val.IsValid() && !tpgresource.IsEmptyValue(val) {
+ transformed["fixedIops"] = transformedFixedIops
+ }
+
+ return transformed, nil
+}
+
+func expandFilestoreInstancePerformanceConfigIopsPerTb(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
+ l := v.([]interface{})
+ if len(l) == 0 || l[0] == nil {
+ return nil, nil
+ }
+ raw := l[0]
+ original := raw.(map[string]interface{})
+ transformed := make(map[string]interface{})
+
+ transformedMaxIopsPerTb, err := expandFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(original["max_iops_per_tb"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedMaxIopsPerTb); val.IsValid() && !tpgresource.IsEmptyValue(val) {
+ transformed["maxIopsPerTb"] = transformedMaxIopsPerTb
+ }
+
+ return transformed, nil
+}
+
+func expandFilestoreInstancePerformanceConfigIopsPerTbMaxIopsPerTb(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
+ return v, nil
+}
+
+func expandFilestoreInstancePerformanceConfigFixedIops(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
+ l := v.([]interface{})
+ if len(l) == 0 || l[0] == nil {
+ return nil, nil
+ }
+ raw := l[0]
+ original := raw.(map[string]interface{})
+ transformed := make(map[string]interface{})
+
+ transformedMaxIops, err := expandFilestoreInstancePerformanceConfigFixedIopsMaxIops(original["max_iops"], d, config)
+ if err != nil {
+ return nil, err
+ } else if val := reflect.ValueOf(transformedMaxIops); val.IsValid() && !tpgresource.IsEmptyValue(val) {
+ transformed["maxIops"] = transformedMaxIops
+ }
+
+ return transformed, nil
+}
+
+func expandFilestoreInstancePerformanceConfigFixedIopsMaxIops(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
+ return v, nil
+}
+
func expandFilestoreInstanceEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
if v == nil {
return map[string]string{}, nil
diff --git a/google/services/filestore/resource_filestore_instance_test.go b/google/services/filestore/resource_filestore_instance_test.go
index 6ece8fa0b3b..87f9196cd08 100644
--- a/google/services/filestore/resource_filestore_instance_test.go
+++ b/google/services/filestore/resource_filestore_instance_test.go
@@ -287,3 +287,127 @@ resource "google_filestore_instance" "instance" {
}
`, name, location, tier, deletionProtection, deletionProtectionReason)
}
+
+func TestAccFilestoreInstance_performanceConfig(t *testing.T) {
+ t.Parallel()
+
+ name := fmt.Sprintf("tf-test-%d", acctest.RandInt(t))
+ location := "us-central1"
+ tier := "REGIONAL"
+
+ acctest.VcrTest(t, resource.TestCase{
+ PreCheck: func() { acctest.AccTestPreCheck(t) },
+ ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
+ CheckDestroy: testAccCheckFilestoreInstanceDestroyProducer(t),
+ Steps: []resource.TestStep{
+ {
+ Config: testAccFilestoreInstance_fixedIopsPerformanceConfig(name, location, tier),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("google_filestore_instance.instance", "performance_config.0.fixed_iops.0.max_iops", "17000"),
+ ),
+ },
+ {
+ ResourceName: "google_filestore_instance.instance",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"zone"},
+ },
+ {
+ Config: testAccFilestoreInstance_iopsPerTbPerformanceConfig(name, location, tier),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("google_filestore_instance.instance", "performance_config.0.iops_per_tb.0.max_iops_per_tb", "17000"),
+ ),
+ },
+ {
+ ResourceName: "google_filestore_instance.instance",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"zone"},
+ },
+ {
+ Config: testAccFilestoreInstance_defaultConfig(name, location, tier),
+ },
+ {
+ ResourceName: "google_filestore_instance.instance",
+ ImportState: true,
+ ImportStateVerify: true,
+ ImportStateVerifyIgnore: []string{"zone"},
+ },
+ },
+ })
+}
+
+func testAccFilestoreInstance_fixedIopsPerformanceConfig(name, location, tier string) string {
+ return fmt.Sprintf(`
+resource "google_filestore_instance" "instance" {
+ name = "%s"
+ location = "%s"
+ tier = "%s"
+ description = "An instance created during testing."
+
+ file_shares {
+ capacity_gb = 1024
+ name = "share"
+ }
+
+ networks {
+ network = "default"
+ modes = ["MODE_IPV4"]
+ }
+
+ performance_config {
+ fixed_iops {
+ max_iops = 17000
+ }
+ }
+}
+`, name, location, tier)
+}
+
+func testAccFilestoreInstance_iopsPerTbPerformanceConfig(name, location, tier string) string {
+ return fmt.Sprintf(`
+resource "google_filestore_instance" "instance" {
+ name = "%s"
+ zone = "%s"
+ tier = "%s"
+ description = "An instance created during testing."
+
+ file_shares {
+ capacity_gb = 1024
+ name = "share"
+ }
+
+ networks {
+ network = "default"
+ modes = ["MODE_IPV4"]
+ }
+
+ performance_config {
+ iops_per_tb {
+ max_iops_per_tb = 17000
+ }
+ }
+}
+`, name, location, tier)
+}
+
+func testAccFilestoreInstance_defaultConfig(name, location, tier string) string {
+ return fmt.Sprintf(`
+resource "google_filestore_instance" "instance" {
+ name = "%s"
+ zone = "%s"
+ tier = "%s"
+ description = "An instance created during testing."
+
+ file_shares {
+ capacity_gb = 1024
+ name = "share"
+ }
+
+ networks {
+ network = "default"
+ modes = ["MODE_IPV4"]
+ }
+}
+`, name, location, tier)
+}
diff --git a/website/docs/r/filestore_instance.html.markdown b/website/docs/r/filestore_instance.html.markdown
index b8b0e0f94e7..7a79b91950c 100644
--- a/website/docs/r/filestore_instance.html.markdown
+++ b/website/docs/r/filestore_instance.html.markdown
@@ -304,6 +304,12 @@ The following arguments are supported:
(Optional)
The reason for enabling deletion protection.
+* `performance_config` -
+ (Optional)
+ Performance configuration for the instance. If not provided,
+ the default performance settings will be used.
+ Structure is [documented below](#nested_performance_config).
+
* `zone` -
(Optional, Deprecated)
The name of the Filestore zone of the instance.
@@ -318,6 +324,39 @@ The following arguments are supported:
If it is not provided, the provider project is used.
+The `performance_config` block supports:
+
+* `iops_per_tb` -
+ (Optional)
+ The instance provisioned IOPS will change dynamically
+ based on the capacity of the instance.
+ Structure is [documented below](#nested_iops_per_tb).
+
+* `fixed_iops` -
+ (Optional)
+ The instance will have a fixed provisioned IOPS value,
+ which will remain constant regardless of instance
+ capacity.
+ Structure is [documented below](#nested_fixed_iops).
+
+
+The `iops_per_tb` block supports:
+
+* `max_iops_per_tb` -
+ (Optional)
+ The instance max IOPS will be calculated by multiplying
+ the capacity of the instance (TB) by max_iops_per_tb,
+ and rounding to the nearest 1000. The instance max IOPS
+ will be changed dynamically based on the instance
+ capacity.
+
+The `fixed_iops` block supports:
+
+* `max_iops` -
+ (Optional)
+ The number of IOPS to provision for the instance.
+ max_iops must be in multiple of 1000.
+
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are exported: