Repository: grafana/terraform-provider-grafana

Issue:
Resources synthetic_monitoring_check causing constant state drift
### Terraform Version

v1.12.2

### Terraform Grafana Provider Version

v4.10.0

### Grafana Version

12.2.0

### Terragrunt Version

v0.81.7

### Affected Resource(s)

grafana_synthetic_monitoring_check (resource)

### Terraform Configuration Files

```hcl
resource "grafana_synthetic_monitoring_check" "sm_http" {
  job                = "sm_http"
  target             = "https://example.com"
  probes             = ["example-probe"]
  frequency          = 60000
  timeout            = 5000
  labels = {
    env         = "dev"
  }
  settings {
    http {
      valid_status_codes = [200]
      headers            = ["x-api-key: key.."]
      method             = "GET"
      fail_if_not_ssl    = true

      fail_if_header_not_matches_regexp {
        header        = "x-example-header"
        regexp        = "200"
        allow_missing = false
      }

      fail_if_header_not_matches_regexp {
        header        = "x-processed-by"
        regexp        = "example-service"
        allow_missing = false
      }

    }
  }
}
```

### Expected Behavior

The resource to be created on the first 'terraform apply' and on the next 'terraform plan' or 'terraform apply' no changes for that resource.

### Actual Behavior

The resource to be created on the first 'terraform apply' and on the next 'terraform plan' or 'terraform apply' we see a change for the 'settings {}':

```bash
13:18:57.160 STDOUT terraform:   # grafana_synthetic_monitoring_check.synthetic_monitoring_checks["sm_http"] will be updated in-place
13:18:57.160 STDOUT terraform:   ~ resource "grafana_synthetic_monitoring_check" "synthetic_monitoring_checks" {
13:18:57.160 STDOUT terraform:         id                 = "53825"
13:18:57.160 STDOUT terraform:         # (10 unchanged attributes hidden)
13:18:57.160 STDOUT terraform:       - settings {
13:18:57.160 STDOUT terraform:           # At least one attribute in this block is (or was) sensitive,
13:18:57.160 STDOUT terraform:           # so its contents will not be displayed.
13:18:57.160 STDOUT terraform:         }
13:18:57.160 STDOUT terraform:       + settings {
13:18:57.160 STDOUT terraform:           # At least one attribute in this block is (or was) sensitive,
13:18:57.160 STDOUT terraform:           # so its contents will not be displayed.
13:18:57.160 STDOUT terraform:         }
13:18:57.160 STDOUT terraform:     }
```

Removing the 'fail_if_header_not_matches_regexp' from the SM Check configuration, leads to the expected behavior.

```hcl
      fail_if_header_not_matches_regexp {
        header        = "x-example-header"
        regexp        = "200"
        allow_missing = false
      }

      fail_if_header_not_matches_regexp {
        header        = "x-processed-by"
        regexp        = "example-service"
        allow_missing = false
      }
```

### Steps to Reproduce

Use 'fail_if_header_not_matches_regexp' in the SM Check configuration and apply changes. After that, run 'terraform plan' or 'terraform apply'   , it will show changes on the resource in the 'settings {}' section.

Remove 'fail_if_header_not_matches_regexp' from the SM Check configuration, run 'terraform plan' or 'terraform apply', no changes to the state will be shown.

### Important Factoids

We use Terragrunt to provision Grafana resources and abstract the grafana_synthetic_monitoring_check resource.

### References

The resource configuration:
https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/synthetic_monitoring_check#nested-schema-for-settingshttpfail_if_header_not_matches_regexp

Hints from issue comments:
@peterschretlen: I was able to reproduce.  A single `fail_if_header_not_matches_regexp` entry works, but a set does not.  
I see the resource used to hash the set is incorrect in https://github.com/grafana/terraform-provider-grafana/blob/7bc73c1c5c62c91e3e742e7d8aad8a6981527fca/internal/resources/syntheticmonitoring/resource_check.go#L973 so I suspect that is the cause. 

Solution patch:
diff --git a/docs/resources/synthetic_monitoring_check.md b/docs/resources/synthetic_monitoring_check.md
index 84de8093..76315419 100644
--- a/docs/resources/synthetic_monitoring_check.md
+++ b/docs/resources/synthetic_monitoring_check.md
@@ -216,6 +216,12 @@ EOS
         regexp        = "application/soap*"
         allow_missing = true
       }
+
+      fail_if_header_matches_regexp {
+        header        = "Content-Type"
+        regexp        = "application/json"
+        allow_missing = true
+      }
     }
   }
 }
diff --git a/examples/resources/grafana_synthetic_monitoring_check/http_complex.tf b/examples/resources/grafana_synthetic_monitoring_check/http_complex.tf
index 65a7b0a3..c2333dd1 100644
--- a/examples/resources/grafana_synthetic_monitoring_check/http_complex.tf
+++ b/examples/resources/grafana_synthetic_monitoring_check/http_complex.tf
@@ -90,6 +90,12 @@ EOS
         regexp        = "application/soap*"
         allow_missing = true
       }
+
+      fail_if_header_matches_regexp {
+        header        = "Content-Type"
+        regexp        = "application/json"
+        allow_missing = true
+      }
     }
   }
 }
diff --git a/internal/resources/syntheticmonitoring/resource_check.go b/internal/resources/syntheticmonitoring/resource_check.go
index 67dede31..2bad0973 100644
--- a/internal/resources/syntheticmonitoring/resource_check.go
+++ b/internal/resources/syntheticmonitoring/resource_check.go
@@ -970,7 +970,7 @@ func resourceCheckRead(ctx context.Context, d *schema.ResourceData, c *smapi.Cli
 		}
 		headerMatch := func(hms []sm.HeaderMatch) *schema.Set {
 			hmSet := schema.NewSet(
-				schema.HashResource(syntheticMonitoringCheckSettingsTCPQueryResponse),
+				schema.HashResource(syntheticMonitoringCheckSettingsHTTPHeaderMatch),
 				[]any{},
 			)
 			for _, hm := range hms {



Test patch:
diff --git a/internal/resources/syntheticmonitoring/resource_check_test.go b/internal/resources/syntheticmonitoring/resource_check_test.go
index a67482cf..a7c099fc 100644
--- a/internal/resources/syntheticmonitoring/resource_check_test.go
+++ b/internal/resources/syntheticmonitoring/resource_check_test.go
@@ -139,8 +139,11 @@ func TestAccResourceCheck_http(t *testing.T) {
 					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_body_matches_regexp.0", ".*bad stuff.*"),
 					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_body_not_matches_regexp.0", ".*good stuff.*"),
 					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_header_matches_regexp.0.header", "Content-Type"),
-					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_header_matches_regexp.0.regexp", "application/soap*"),
+					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_header_matches_regexp.0.regexp", "application/json"),
 					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_header_matches_regexp.0.allow_missing", "true"),
+					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_header_matches_regexp.1.header", "Content-Type"),
+					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_header_matches_regexp.1.regexp", "application/soap*"),
+					resource.TestCheckResourceAttr("grafana_synthetic_monitoring_check.http", "settings.0.http.0.fail_if_header_matches_regexp.1.allow_missing", "true"),
 				),
 			},
 			{