Skip to content

Commit 2e11cc0

Browse files
Dashboard: Support folder UID as reference (#900)
* Dashboard: Support folder UID as reference Currently, the dashboard resource only supports numeric folder IDs However, in a context where someone is managing the dashboard but not the folder, it's much easier to use a UID In this PR, I add UID support to the `folder` field. Numeric ID support remains the same * go generate
1 parent 1304d6a commit 2e11cc0

File tree

7 files changed

+78
-37
lines changed

7 files changed

+78
-37
lines changed

docs/resources/dashboard.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ resource "grafana_dashboard" "metrics" {
3131

3232
### Optional
3333

34-
- `folder` (String) The id of the folder to save the dashboard in. This attribute is a string to reflect the type of the folder's id.
34+
- `folder` (String) The id or UID of the folder to save the dashboard in.
3535
- `message` (String) Set a commit message for the version history.
3636
- `org_id` (String) The Organization ID. If not set, the Org ID defined in the provider block will be used.
3737
- `overwrite` (Boolean) Set to true if you want to overwrite existing dashboard with newer version, same dashboard title in folder or same dashboard uid.
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
resource "grafana_folder" "test_folder" {
2-
title = "Terraform Folder Test Folder"
2+
title = "Terraform Folder Folder ID Test"
3+
uid = "folder-dashboard-id-test"
34
}
45

56
resource "grafana_dashboard" "test_folder" {
6-
folder = grafana_folder.test_folder.id
7-
config_json = <<EOD
8-
{
9-
"title": "Terraform Folder Test Dashboard",
10-
"id": 12,
11-
"version": "43",
12-
"uid": "folder"
13-
}
14-
EOD
7+
folder = grafana_folder.test_folder.id
8+
config_json = jsonencode({
9+
"title" : "Terraform Folder Test Dashboard With ID",
10+
"id" : 123,
11+
"version" : "434",
12+
"uid" : "folder-dashboard-test-ref-with-id"
13+
})
1514
}
15+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
resource "grafana_folder" "test_folder" {
2+
title = "Terraform Folder Folder UID Test"
3+
uid = "folder-dashboard-uid-test"
4+
}
5+
6+
resource "grafana_dashboard" "test_folder" {
7+
folder = grafana_folder.test_folder.uid
8+
config_json = jsonencode({
9+
"title" : "Terraform Folder Test Dashboard With UID",
10+
"id" : 1234,
11+
"version" : "4345",
12+
"uid" : "folder-dashboard-test-ref-with-uid"
13+
})
14+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.18
55
require (
66
github.com/Masterminds/semver/v3 v3.2.1
77
github.com/grafana/amixr-api-go-client v0.0.7
8-
github.com/grafana/grafana-api-golang-client v0.19.1
8+
github.com/grafana/grafana-api-golang-client v0.20.1
99
github.com/grafana/machine-learning-go-client v0.5.0
1010
github.com/grafana/synthetic-monitoring-agent v0.14.4
1111
github.com/grafana/synthetic-monitoring-api-go-client v0.7.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
7272
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
7373
github.com/grafana/amixr-api-go-client v0.0.7 h1:U6W6yKxMMybI+Qz4zl+Vih48o6CczLaU/vjk2m7omvU=
7474
github.com/grafana/amixr-api-go-client v0.0.7/go.mod h1:N6x26XUrM5zGtK5zL5vNJnAn2JFMxLFPPLTw/6pDkFE=
75-
github.com/grafana/grafana-api-golang-client v0.19.1 h1:eKekLtjX+dSYcxJIsMpz0HkTk2QxUkXf1pyLtZCuxNk=
76-
github.com/grafana/grafana-api-golang-client v0.19.1/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E=
75+
github.com/grafana/grafana-api-golang-client v0.20.1 h1:xadfMY9PDcWd2ppU/AgYvMeBeWqioed7cetPhBYoNSk=
76+
github.com/grafana/grafana-api-golang-client v0.20.1/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E=
7777
github.com/grafana/machine-learning-go-client v0.5.0 h1:Q1K+MPSy8vfMm2jsk3WQ7O77cGr2fM5hxwtPSoPc5NU=
7878
github.com/grafana/machine-learning-go-client v0.5.0/go.mod h1:QFfZz8NkqVF8++skjkKQXJEZfpCYd8S0yTWJUpsLLTA=
7979
github.com/grafana/synthetic-monitoring-agent v0.14.4 h1:amLwPpBvWnqoYHg4Dn2IBdkDy9szcRLr7yCJHMXNhG8=

internal/resources/grafana/resource_dashboard.go

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1313
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
14-
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1514

1615
gapi "github.com/grafana/grafana-api-golang-client"
1716
"github.com/grafana/terraform-provider-grafana/internal/common"
@@ -71,11 +70,10 @@ Manages Grafana dashboards.
7170
"so that previous versions of your dashboard are not lost.",
7271
},
7372
"folder": {
74-
Type: schema.TypeString,
75-
Optional: true,
76-
ForceNew: true,
77-
Description: "The id of the folder to save the dashboard in. This attribute is a string to reflect the type of the folder's id.",
78-
ValidateFunc: validation.StringMatch(common.IDRegexp, "must be a valid folder id"),
73+
Type: schema.TypeString,
74+
Optional: true,
75+
ForceNew: true,
76+
Description: "The id or UID of the folder to save the dashboard in.",
7977
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
8078
return old == "0" && new == "" || old == "" && new == "0"
8179
},
@@ -221,10 +219,13 @@ func ReadDashboard(ctx context.Context, d *schema.ResourceData, meta interface{}
221219
d.Set("dashboard_id", int64(dashboard.Model["id"].(float64)))
222220
d.Set("version", int64(dashboard.Model["version"].(float64)))
223221
d.Set("url", strings.TrimRight(gapiURL, "/")+dashboard.Meta.URL)
224-
if dashboard.FolderID > 0 {
222+
223+
// If the folder was originally set to a numeric ID, we read the folder ID
224+
// Othwerwise, we read the folder UID
225+
if common.IDRegexp.MatchString(d.Get("folder").(string)) && dashboard.Meta.Folder > 0 {
225226
d.Set("folder", strconv.FormatInt(dashboard.FolderID, 10))
226227
} else {
227-
d.Set("folder", "")
228+
d.Set("folder", dashboard.Meta.FolderUID)
228229
}
229230

230231
configJSONBytes, err := json.Marshal(dashboard.Model)
@@ -288,20 +289,17 @@ func DeleteDashboard(ctx context.Context, d *schema.ResourceData, meta interface
288289
}
289290

290291
func makeDashboard(d *schema.ResourceData) (gapi.Dashboard, error) {
291-
var parsedFolder int64 = 0
292-
var err error
293-
if folderStr := d.Get("folder").(string); folderStr != "" {
294-
parsedFolder, err = strconv.ParseInt(d.Get("folder").(string), 10, 64)
295-
if err != nil {
296-
return gapi.Dashboard{}, fmt.Errorf("error parsing folder: %s", err)
297-
}
298-
}
299-
300292
dashboard := gapi.Dashboard{
301-
FolderID: parsedFolder,
302293
Overwrite: d.Get("overwrite").(bool),
303294
Message: d.Get("message").(string),
304295
}
296+
297+
if folderInt, err := strconv.ParseInt(d.Get("folder").(string), 10, 64); err == nil {
298+
dashboard.FolderID = folderInt
299+
} else {
300+
dashboard.FolderUID = d.Get("folder").(string)
301+
}
302+
305303
configJSON := d.Get("config_json").(string)
306304
dashboardJSON, err := UnmarshalDashboardConfigJSON(configJSON)
307305
if err != nil {

internal/resources/grafana/resource_dashboard_test.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,17 +174,46 @@ func TestAccDashboard_folder(t *testing.T) {
174174
testAccDashboardCheckExists("grafana_dashboard.test_folder", &dashboard),
175175
testAccFolderCheckExists("grafana_folder.test_folder", &folder),
176176
testAccDashboardCheckExistsInFolder(&dashboard, &folder),
177-
resource.TestCheckResourceAttr("grafana_dashboard.test_folder", "id", "0:folder"), // <org id>:<uid>
178-
resource.TestCheckResourceAttr("grafana_dashboard.test_folder", "uid", "folder"),
179-
resource.TestMatchResourceAttr(
180-
"grafana_dashboard.test_folder", "folder", common.IDRegexp,
181-
),
177+
resource.TestCheckResourceAttr("grafana_dashboard.test_folder", "id", "0:folder-dashboard-test-ref-with-id"), // <org id>:<uid>
178+
resource.TestCheckResourceAttr("grafana_dashboard.test_folder", "uid", "folder-dashboard-test-ref-with-id"),
179+
resource.TestMatchResourceAttr("grafana_dashboard.test_folder", "folder", common.IDRegexp),
182180
),
183181
},
184182
},
185183
})
186184
}
187185

186+
func TestAccDashboard_folder_uid(t *testing.T) {
187+
testutils.CheckOSSTestsEnabled(t)
188+
testutils.CheckOSSTestsSemver(t, ">=8.0.0") // UID in folders were added in v8
189+
190+
var dashboard gapi.Dashboard
191+
var folder gapi.Folder
192+
193+
resource.ParallelTest(t, resource.TestCase{
194+
ProviderFactories: testutils.ProviderFactories,
195+
CheckDestroy: testAccDashboardFolderCheckDestroy(&dashboard, &folder),
196+
Steps: []resource.TestStep{
197+
{
198+
Config: testutils.TestAccExample(t, "resources/grafana_dashboard/_acc_folder_uid_ref.tf"),
199+
Check: resource.ComposeTestCheckFunc(
200+
testAccFolderCheckExists("grafana_folder.test_folder", &folder),
201+
testAccDashboardCheckExists("grafana_dashboard.test_folder", &dashboard),
202+
testAccDashboardCheckExistsInFolder(&dashboard, &folder),
203+
resource.TestCheckResourceAttr("grafana_dashboard.test_folder", "id", "0:folder-dashboard-test-ref-with-uid"), // <org id>:<uid>
204+
resource.TestCheckResourceAttr("grafana_dashboard.test_folder", "uid", "folder-dashboard-test-ref-with-uid"),
205+
resource.TestCheckResourceAttr("grafana_dashboard.test_folder", "folder", "folder-dashboard-uid-test"),
206+
),
207+
},
208+
{
209+
ImportState: true,
210+
ResourceName: "grafana_dashboard.test_folder",
211+
ImportStateVerify: true,
212+
},
213+
},
214+
})
215+
}
216+
188217
func TestAccDashboard_inOrg(t *testing.T) {
189218
testutils.CheckOSSTestsEnabled(t)
190219

0 commit comments

Comments
 (0)