@@ -12,6 +12,7 @@ import (
1212 "github.com/grafana/grafana-com-public-clients/go/gcom"
1313 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
1414 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
15+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
1516
1617 "github.com/grafana/terraform-provider-grafana/v4/internal/common"
1718 "github.com/grafana/terraform-provider-grafana/v4/internal/testutils"
@@ -24,21 +25,22 @@ func TestResourceAccessPolicyRotatingToken_Basic(t *testing.T) {
2425
2526 var policy gcom.AuthAccessPolicy
2627 var policyToken gcom.AuthToken
28+ var policyToken2 gcom.AuthToken
2729
28- // Set up rotation parameters
2930 rotateAfter := time .Now ().Add (time .Hour * 2 ).UTC ()
31+ updatedRotateAfter := time .Now ().Add (time .Hour * 4 ).UTC ()
3032 postRotationLifetime := "24h"
31- namePrefix := "test-rotating"
33+ namePrefix := "test-rotating-token-terraform "
3234 expectedExpiresAt := rotateAfter .Add (24 * time .Hour ).Format (time .RFC3339 )
3335 expectedName := fmt .Sprintf ("%s-%d-%s" , namePrefix , rotateAfter .Unix (), postRotationLifetime )
34-
35- accessPolicyName := fmt .Sprintf ("initial-%s" , acctest .RandStringFromCharSet (6 , acctest .CharSetAlpha ))
36+ accessPolicyName := fmt .Sprintf ("test-rotating-token-terraform-initial-%s" , acctest .RandStringFromCharSet (6 , acctest .CharSetAlpha ))
3637
3738 resource .Test (t , resource.TestCase {
3839 ProtoV5ProviderFactories : testutils .ProtoV5ProviderFactories ,
3940 CheckDestroy : resource .ComposeTestCheckFunc (
4041 testAccCloudAccessPolicyCheckDestroy ("prod-us-east-0" , & policy ),
4142 testAccCloudAccessPolicyTokenCheckDestroy ("prod-us-east-0" , & policyToken ),
43+ testAccCloudAccessPolicyTokenCheckDestroy ("prod-us-east-0" , & policyToken2 ),
4244 ),
4345 Steps : []resource.TestStep {
4446 // Test that the cloud access policy and rotating token get created
@@ -52,7 +54,7 @@ func TestResourceAccessPolicyRotatingToken_Basic(t *testing.T) {
5254 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "name" , expectedName ),
5355 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "expires_at" , expectedExpiresAt ),
5456 // Input fields
55- resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "name_prefix" , "test-rotating" ),
57+ resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "name_prefix" , namePrefix ),
5658 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "rotate_after" , strconv .FormatInt (rotateAfter .Unix (), 10 )),
5759 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "post_rotation_lifetime" , postRotationLifetime ),
5860 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "region" , "prod-us-east-0" ),
@@ -71,7 +73,7 @@ func TestResourceAccessPolicyRotatingToken_Basic(t *testing.T) {
7173 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "name" , expectedName ),
7274 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "expires_at" , expectedExpiresAt ),
7375 // Input fields
74- resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "name_prefix" , "test-rotating" ),
76+ resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "name_prefix" , namePrefix ),
7577 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "rotate_after" , strconv .FormatInt (rotateAfter .Unix (), 10 )),
7678 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "post_rotation_lifetime" , postRotationLifetime ),
7779 resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "region" , "prod-us-east-0" ),
@@ -109,6 +111,54 @@ func TestResourceAccessPolicyRotatingToken_Basic(t *testing.T) {
109111 ImportStateVerify : true ,
110112 ImportStateVerifyIgnore : []string {"token" },
111113 },
114+ // Test rotation time change should force recreation
115+ {
116+ Config : testAccCloudAccessPolicyRotatingTokenConfigBasic (accessPolicyName , "" , "prod-us-east-0" , namePrefix , updatedRotateAfter .Unix (), postRotationLifetime ),
117+ Check : resource .ComposeTestCheckFunc (
118+ testAccCloudAccessPolicyCheckExists ("grafana_cloud_access_policy.rotating_token_test" , & policy ),
119+ testAccCloudAccessPolicyTokenCheckExists ("grafana_cloud_access_policy_rotating_token.test" , & policyToken2 ),
120+ resource .TestCheckResourceAttr ("grafana_cloud_access_policy_rotating_token.test" , "rotate_after" , strconv .Itoa (int (updatedRotateAfter .Unix ()))),
121+ // Verify the token ID changed (indicating recreation)
122+ func (s * terraform.State ) error {
123+ if policyToken .Id == nil || policyToken2 .Id == nil {
124+ return fmt .Errorf ("expected token to be recreated, but ID is nil" )
125+ }
126+ if * policyToken .Id == * policyToken2 .Id {
127+ return fmt .Errorf ("expected token to be recreated, but ID remained the same: %s" , * policyToken .Id )
128+ }
129+ return nil
130+ },
131+ ),
132+ },
133+ // Test import with different values in config vs what is inferred from the name
134+ {
135+ Config : testAccCloudAccessPolicyRotatingTokenConfigMismatch (accessPolicyName , "" , "prod-us-east-0" , "different-prefix" , time .Now ().Add (time .Hour * 6 ).Unix (), "12h" ),
136+ ResourceName : "grafana_cloud_access_policy_rotating_token.test" ,
137+ ImportState : true ,
138+ ImportStateVerify : false , // We expect this to fail verification
139+ ImportStateCheck : func (s []* terraform.InstanceState ) error {
140+ if len (s ) != 1 {
141+ return fmt .Errorf ("expected 1 state, got %d" , len (s ))
142+ }
143+
144+ state := s [0 ]
145+
146+ // Verify that imported values come from the token name in Grafana Cloud, not the Terraform state we had
147+ if state .Attributes ["name_prefix" ] != namePrefix {
148+ return fmt .Errorf ("expected name_prefix to be inferred as %s from token name, got %s" , namePrefix , state .Attributes ["name_prefix" ])
149+ }
150+
151+ if state .Attributes ["rotate_after" ] != strconv .Itoa (int (updatedRotateAfter .Unix ())) {
152+ return fmt .Errorf ("expected rotate_after to be inferred as %s from token name, got %s" , strconv .Itoa (int (updatedRotateAfter .Unix ())), state .Attributes ["rotate_after" ])
153+ }
154+
155+ if state .Attributes ["post_rotation_lifetime" ] != postRotationLifetime {
156+ return fmt .Errorf ("expected post_rotation_lifetime to be inferred as %s from token name, got %s" , postRotationLifetime , state .Attributes ["post_rotation_lifetime" ])
157+ }
158+
159+ return nil
160+ },
161+ },
112162 // Test that destroy does not actually delete the token (it should only show a warning instead)
113163 {
114164 Config : testAccCloudAccessPolicyRotatingTokenConfigBasic (accessPolicyName , "" , "prod-us-east-0" , "test-no-delete" , rotateAfter .Unix (), postRotationLifetime ),
@@ -166,3 +216,50 @@ func testAccCloudAccessPolicyRotatingTokenConfigBasic(name, displayName, region
166216 }
167217 ` , name , displayName , strings .Join (scopes , `","` ), os .Getenv ("GRAFANA_CLOUD_ORG" ), namePrefix , rotateAfter , region , postRotationLifetime )
168218}
219+
220+ func testAccCloudAccessPolicyRotatingTokenConfigMismatch (name , displayName , region string , namePrefix string , rotateAfter int64 , postRotationLifetime string ) string {
221+ if displayName != "" {
222+ displayName = fmt .Sprintf ("display_name = \" %s\" " , displayName )
223+ }
224+
225+ scopes := []string {
226+ "metrics:read" ,
227+ "logs:write" ,
228+ "accesspolicies:read" ,
229+ "accesspolicies:write" ,
230+ "accesspolicies:delete" ,
231+ "datadog:validate" ,
232+ }
233+
234+ return fmt .Sprintf (`
235+ data "grafana_cloud_organization" "current" {
236+ slug = "%[4]s"
237+ }
238+
239+ resource "grafana_cloud_access_policy" "rotating_token_test" {
240+ region = "%[7]s"
241+ name = "%[1]s"
242+ %[2]s
243+
244+ scopes = ["%[3]s"]
245+
246+ realm {
247+ type = "org"
248+ identifier = data.grafana_cloud_organization.current.id
249+
250+ label_policy {
251+ selector = "{namespace=\"default\"}"
252+ }
253+ }
254+ }
255+
256+ resource "grafana_cloud_access_policy_rotating_token" "test" {
257+ region = "%[7]s"
258+ access_policy_id = grafana_cloud_access_policy.rotating_token_test.policy_id
259+ name_prefix = "%[5]s"
260+ rotate_after = "%[6]d"
261+ post_rotation_lifetime = "%[8]s"
262+ %[2]s
263+ }
264+ ` , name , displayName , strings .Join (scopes , `","` ), os .Getenv ("GRAFANA_CLOUD_ORG" ), namePrefix , rotateAfter , region , postRotationLifetime )
265+ }
0 commit comments