Repository: grafana/terraform-provider-grafana

Issue:
`grafana_folder` Data Source no longer returns UID for Folder with Title Lookup
### Terraform Version

1.12.2

### Terraform Grafana Provider Version

4.5.1

### Grafana Version

Cloud

### Affected Resource(s)

* `grafana_folder` **Data Source**

### Terraform Configuration Files

```hcl
// Step 1: Create a stack
provider "grafana" {
  alias                     = "cloud"
  cloud_access_policy_token = "my-token"
}

resource "grafana_cloud_stack" "my_stack" {
  provider = grafana.cloud

  name        = "myteststack"
  slug        = "myteststack"
  region_slug = "prod-us-east-0"
}

// Step 2: Create a service account and key for the stack
resource "grafana_cloud_stack_service_account" "cloud_sa" {
  provider   = grafana.cloud
  stack_slug = grafana_cloud_stack.my_stack.slug

  name        = "cloud service account"
  role        = "Admin"
  is_disabled = false
}

resource "grafana_cloud_stack_service_account_token" "cloud_sa" {
  provider   = grafana.cloud
  stack_slug = grafana_cloud_stack.my_stack.slug

  name               = "my_stack cloud_sa key"
  service_account_id = grafana_cloud_stack_service_account.cloud_sa.id
}

// Step 3: Create resources within the stack
provider "grafana" {
  alias = "my_stack"

  url  = grafana_cloud_stack.my_stack.url
  auth = grafana_cloud_stack_service_account_token.cloud_sa.key
}

// Step 4: Get GrafanaCloud Folder UID
data "grafana_folder" "grafanacloud" {
  title      = "GrafanaCloud"
}

// Step 5: Try and Use UID
resource "grafana_rule_group" "demo" {
  name             = "Demo"
  folder_uid       = data.grafana_folder.grafanacloud.uid
  interval_seconds = 60
rule {
// Removed for length
}
}
```

### Expected Behavior

Before #2301 was merged, this returned the UID as expected, and the UID could be used to provision resources.

### Actual Behavior

#2301 appears to introduce a breaking change, where the UID is not returned if its not provided, example from `terraform plan` for a new Environment.

In this case we are attempting to create a new Grafana Cloud Stack, and get the UID of the `GrafanaCloud` Folder, so we can create some alert rules within it.

```
  # module.mymodule.data.grafana_folder.grafanacloud will be read during apply
  # (depends on a resource or a module with changes pending)
 <= data "grafana_folder" "grafanacloud" {
      + id                = (known after apply)
      + parent_folder_uid = (known after apply)
      + title             = "GrafanaCloud"
      + url               = (known after apply)
    }
```

The UID is not returned in the Data look up anymore, meaning you cant reference it in other Resources later on.

It also means a `terraform plan` or `terraform apply` fails, and does not let it complete, it returns the below error;

```
Error: Missing required argument

  with module.mymodule.grafana_rule_group.cloud_usage_limits,
  on ../../../modules/smymodule/alerts_grafanacloud_usage.tf line 351, in resource "grafana_rule_group" "demo":
 351:   folder_uid       = data.grafana_folder.grafanacloud.uid

The argument "folder_uid" is required, but no definition was found.
```


### Steps to Reproduce

Using the example above, add credentials for Grafana Cloud, and run `terraform plan`, it will return an error saying `The argument "folder_uid" is required, but no definition was found.`

### Important Factoids

The UID is returned if the Stack is already created and available, but does not return it, if the Stack is not. - This is a common use case, where the Stack is created as part of an overall terraform provisioning along with additional resources on the stack.

### References

- Appears to be related to #2301 

Discussion:

Solution patch:
diff --git a/internal/resources/grafana/data_source_folder.go b/internal/resources/grafana/data_source_folder.go
index ff73ca37..7024d854 100644
--- a/internal/resources/grafana/data_source_folder.go
+++ b/internal/resources/grafana/data_source_folder.go
@@ -30,6 +30,7 @@ func datasourceFolder() *common.DataSource {
 			"uid": {
 				Type:        schema.TypeString,
 				Optional:    true,
+				Computed:    true, // If not set by user, this will be populated by reading the folder.
 				Description: "The uid of the folder. If not set, only the title of the folder is used to find the folder.",
 			},
 			"prevent_destroy_if_not_empty": nil,



Test patch:
diff --git a/internal/resources/grafana/data_source_folder_test.go b/internal/resources/grafana/data_source_folder_test.go
index c4d53d2b..bca6d984 100644
--- a/internal/resources/grafana/data_source_folder_test.go
+++ b/internal/resources/grafana/data_source_folder_test.go
@@ -257,3 +257,53 @@ data "grafana_folder" "f3" {
 }
 `, name)
 }
+
+func TestAccDatasourceFolderUidSetCorrectly(t *testing.T) {
+	// Test uses nested folders feature, which was introduced in Grafana 10 (behind feature-toggle) and made GA in Grafana 11.
+	testutils.CheckOSSTestsEnabled(t, ">=10.3.0")
+
+	var folder models.Folder
+	randomName := acctest.RandStringFromCharSet(6, acctest.CharSetAlpha)
+
+	resource.ParallelTest(t, resource.TestCase{
+		ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
+		CheckDestroy: resource.ComposeTestCheckFunc(
+			folderCheckExists.destroyed(&folder, nil),
+		),
+		Steps: []resource.TestStep{
+			{
+				// Find folder by title. We should get correct uid back.
+				Config: fmt.Sprintf(`
+					resource "grafana_folder" "folder" {
+						title = "%[1]s"
+					}
+					data "grafana_folder" "test" {
+						depends_on = ["grafana_folder.folder"]
+						title = "%[1]s"		
+					}
+					resource "grafana_folder" "nested_folder" {
+						title = "%[1]s Nested"
+						parent_folder_uid = data.grafana_folder.test.uid
+					}
+				`, randomName),
+				Check: resource.ComposeTestCheckFunc(
+					folderCheckExists.exists("grafana_folder.folder", &folder),
+					resource.TestCheckResourceAttr("grafana_folder.nested_folder", "title", randomName+" Nested"),
+					// Next line doesn't work, because resource.TestCheckResourceAttr is called too early -- before folder.UID is set.
+					//	   resource.TestCheckResourceAttr("grafana_folder.nested_folder", "parent_folder_uid", folder.UID)
+					//
+					// This works:
+					resource.TestCheckResourceAttrWith("grafana_folder.nested_folder", "parent_folder_uid", func(value string) error {
+						if folder.UID == "" {
+							return fmt.Errorf("grafana_folder.folder.uid should not be empty")
+						}
+						if value == folder.UID {
+							return nil
+						}
+						return fmt.Errorf("expected uid to be %q but got %q", folder.UID, value)
+					}),
+				),
+			},
+		},
+	})
+}