diff --git a/.tool-versions b/.tool-versions index e4d9ecd2..83b09b27 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -golang 1.21.0 -python 3.10.5 -terraform 1.5.7 +golang 1.21.3 +python 3.12.0 +terraform 1.6.1 diff --git a/go.mod b/go.mod index e227c75b..49ad23bc 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/iancoleman/strcase v0.3.0 github.com/jarcoal/httpmock v1.3.1 github.com/mattn/goveralls v0.0.12 - github.com/securego/gosec/v2 v2.18.0 + github.com/securego/gosec/v2 v2.18.1 github.com/stretchr/testify v1.8.4 gotest.tools/gotestsum v1.11.0 ) diff --git a/go.sum b/go.sum index 7c407d53..75d863ef 100644 --- a/go.sum +++ b/go.sum @@ -251,8 +251,8 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= -github.com/securego/gosec/v2 v2.18.0 h1:PEQsNSe8NjGrQ6oF3LrmMhZTm2iqYKUOYf+OWXzo+XQ= -github.com/securego/gosec/v2 v2.18.0/go.mod h1:06rgh4+5IrlRpi573DJRZ6y/tlIE+a0rFgMlJDxFIyQ= +github.com/securego/gosec/v2 v2.18.1 h1:xnnehWg7dIW8qrRPGm8ykY21zp2MueKyC99Vlcuj96I= +github.com/securego/gosec/v2 v2.18.1/go.mod h1:ZUTcKD9gAFip1lLGHWCjkoBQJyaEzePTNzjwlL2HHoE= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= diff --git a/twingate/internal/model/resource.go b/twingate/internal/model/resource.go index 89b2be27..c073bb60 100644 --- a/twingate/internal/model/resource.go +++ b/twingate/internal/model/resource.go @@ -2,6 +2,7 @@ package model import ( "fmt" + "strconv" "strings" "github.com/Twingate/terraform-provider-twingate/twingate/internal/attr" @@ -77,7 +78,7 @@ type PortRange struct { func (p PortRange) String() string { if p.Start == p.End { - return fmt.Sprintf("%d", p.Start) + return strconv.Itoa(p.Start) } return fmt.Sprintf("%d-%d", p.Start, p.End) diff --git a/twingate/internal/provider/resource/service-key.go b/twingate/internal/provider/resource/service-key.go index 22521a0a..e4343daa 100644 --- a/twingate/internal/provider/resource/service-key.go +++ b/twingate/internal/provider/resource/service-key.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/tfsdk" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -58,6 +59,9 @@ func (r *serviceKey) Schema(_ context.Context, _ resource.SchemaRequest, resp *r attr.ServiceAccountID: schema.StringAttribute{ Required: true, Description: "The id of the Service Account", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, // optional attr.Name: schema.StringAttribute{ diff --git a/twingate/internal/test/acctests/helper.go b/twingate/internal/test/acctests/helper.go index 8b24bf18..c2e79f5d 100644 --- a/twingate/internal/test/acctests/helper.go +++ b/twingate/internal/test/acctests/helper.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "os" + "strconv" "strings" "testing" "time" @@ -58,7 +59,7 @@ var ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ //n } func SetPageLimit(limit int) { - if err := os.Setenv(client.EnvPageLimit, fmt.Sprintf("%d", limit)); err != nil { + if err := os.Setenv(client.EnvPageLimit, strconv.Itoa(limit)); err != nil { log.Fatal("failed to set page limit", err) } } diff --git a/twingate/internal/test/acctests/resource/service-key_test.go b/twingate/internal/test/acctests/resource/service-key_test.go index e1ba852e..c81e3c84 100644 --- a/twingate/internal/test/acctests/resource/service-key_test.go +++ b/twingate/internal/test/acctests/resource/service-key_test.go @@ -326,3 +326,88 @@ func TestAccTwingateServiceKeyReCreateAfterChangingExpirationTime(t *testing.T) }) }) } + +func TestAccTwingateServiceKeyAndServiceAccountLifecycle(t *testing.T) { + t.Run("Test Twingate Resource : Acc Service Key and Service Account Lifecycle", func(t *testing.T) { + serviceAccountName := test.RandomName() + serviceAccountNameV2 := test.RandomName() + terraformServiceAccountName := test.TerraformRandName("test_acc") + terraformServiceAccountNameV2 := test.TerraformRandName("test_acc_v2") + terraformServiceAccountKeyName := test.TerraformRandName("test_key") + serviceAccount := acctests.TerraformServiceAccount(terraformServiceAccountName) + serviceAccountV2 := acctests.TerraformServiceAccount(terraformServiceAccountNameV2) + serviceKey := acctests.TerraformServiceKey(terraformServiceAccountKeyName) + + serviceKeyResourceID := new(string) + serviceAccountResourceID := new(string) + + sdk.Test(t, sdk.TestCase{ + ProtoV6ProviderFactories: acctests.ProviderFactories, + PreCheck: func() { acctests.PreCheck(t) }, + CheckDestroy: acctests.CheckTwingateServiceAccountDestroy, + Steps: []sdk.TestStep{ + { + Config: createServiceKeyV1(terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, terraformServiceAccountName), + Check: acctests.ComposeTestCheckFunc( + acctests.CheckTwingateResourceExists(serviceAccount), + sdk.TestCheckResourceAttr(serviceAccount, attr.Name, serviceAccountName), + acctests.CheckTwingateResourceExists(serviceKey), + sdk.TestCheckResourceAttrWith(serviceKey, attr.Token, nonEmptyValue), + acctests.GetTwingateResourceID(serviceKey, &serviceKeyResourceID), + acctests.GetTwingateResourceID(serviceKey, &serviceAccountResourceID), + ), + }, + { + Config: createServiceKeyV1(terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, terraformServiceAccountNameV2), + Check: acctests.ComposeTestCheckFunc( + acctests.CheckTwingateResourceExists(serviceAccountV2), + sdk.TestCheckResourceAttr(serviceAccountV2, attr.Name, serviceAccountNameV2), + acctests.CheckTwingateResourceExists(serviceKey), + sdk.TestCheckResourceAttrWith(serviceKey, attr.Token, nonEmptyValue), + + // test resources were re-created + sdk.TestCheckResourceAttrWith(serviceKey, attr.ID, func(value string) error { + if *serviceKeyResourceID == "" { + return errors.New("failed to fetch service_key resource id") + } + + if value == *serviceKeyResourceID { + return errors.New("service_key resource was not re-created") + } + + return nil + }), + + sdk.TestCheckResourceAttrWith(serviceAccountV2, attr.ID, func(value string) error { + if *serviceAccountResourceID == "" { + return errors.New("failed to fetch service_account resource id") + } + + if value == *serviceAccountResourceID { + return errors.New("service_account resource was not re-created") + } + + return nil + }), + ), + }, + }, + }) + }) +} + +func createServiceKeyV1(terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, serviceAccount string) string { + return fmt.Sprintf(` + resource "twingate_service_account" "%s" { + name = "%s" + } + + resource "twingate_service_account" "%s" { + name = "%s" + } + + resource "twingate_service_account_key" "%s" { + service_account_id = twingate_service_account.%s.id + } + `, terraformServiceAccountName, serviceAccountName, terraformServiceAccountNameV2, serviceAccountNameV2, terraformServiceAccountKeyName, serviceAccount) +}