From 2616ab1b42f2d7b7da9db19e578ca8ff65eccb6a Mon Sep 17 00:00:00 2001 From: Tomer Heber Date: Sun, 15 May 2022 12:28:21 -0500 Subject: [PATCH] Fix: env0_azure_credentials - apply after import will delete+create --- env0/resource_azure_credentials.go | 39 +++++-- env0/resource_azure_credentials_test.go | 109 +++++++++++++++++- .../env0_azure_credentials/import.sh | 2 + 3 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 examples/resources/env0_azure_credentials/import.sh diff --git a/env0/resource_azure_credentials.go b/env0/resource_azure_credentials.go index 0181d1ef..5ad710d6 100644 --- a/env0/resource_azure_credentials.go +++ b/env0/resource_azure_credentials.go @@ -2,6 +2,7 @@ package env0 import ( "context" + "fmt" "github.com/env0/terraform-provider-env0/client" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -14,6 +15,8 @@ func resourceAzureCredentials() *schema.Resource { ReadContext: resourceAzureCredentialsRead, DeleteContext: resourceAzureCredentialsDelete, + Importer: &schema.ResourceImporter{StateContext: resourceAzureCredentialsImport}, + Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -51,14 +54,13 @@ func resourceAzureCredentials() *schema.Resource { } func resourceAzureCredentialsCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + value := client.AzureCredentialsValuePayload{} + if err := readResourceData(&value, d); err != nil { + return diag.Errorf("schema resource data deserialization failed: %v", err) + } + apiClient := meta.(client.ApiClientInterface) - value := client.AzureCredentialsValuePayload{ - ClientId: d.Get("client_id").(string), - ClientSecret: d.Get("client_secret").(string), - SubscriptionId: d.Get("subscription_id").(string), - TenantId: d.Get("tenant_id").(string), - } requestType := client.AzureServicePrincipalCredentialsType request := client.AzureCredentialsCreatePayload{ @@ -66,6 +68,7 @@ func resourceAzureCredentialsCreate(ctx context.Context, d *schema.ResourceData, Value: value, Type: requestType, } + credentials, err := apiClient.AzureCredentialsCreate(request) if err != nil { return diag.Errorf("could not create credentials key: %v", err) @@ -80,10 +83,16 @@ func resourceAzureCredentialsRead(ctx context.Context, d *schema.ResourceData, m apiClient := meta.(client.ApiClientInterface) id := d.Id() - _, err := apiClient.CloudCredentials(id) + + credentials, err := apiClient.CloudCredentials(id) if err != nil { return ResourceGetFailure("azure credentials", d, err) } + + if err := writeResourceData(&credentials, d); err != nil { + return diag.Errorf("schema resource data serialization failed: %v", err) + } + return nil } @@ -97,3 +106,19 @@ func resourceAzureCredentialsDelete(ctx context.Context, d *schema.ResourceData, } return nil } + +func resourceAzureCredentialsImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + credentials, err := getCredentials(d.Id(), "AZURE_", meta) + if err != nil { + if _, ok := err.(*client.NotFoundError); ok { + return nil, fmt.Errorf("azure credentials resource with id %v not found", d.Id()) + } + return nil, err + } + + if err := writeResourceData(&credentials, d); err != nil { + return nil, fmt.Errorf("schema resource data serialization failed: %v", err) + } + + return []*schema.ResourceData{d}, nil +} diff --git a/env0/resource_azure_credentials_test.go b/env0/resource_azure_credentials_test.go index bba6c57e..6f94ac12 100644 --- a/env0/resource_azure_credentials_test.go +++ b/env0/resource_azure_credentials_test.go @@ -1,6 +1,8 @@ package env0 import ( + "fmt" + "regexp" "testing" "github.com/env0/terraform-provider-env0/client" @@ -14,6 +16,7 @@ func TestUnitAzureCredentialsResource(t *testing.T) { resourceType := "env0_azure_credentials" resourceName := "test" + resourceNameImport := resourceType + "." + resourceName accessor := resourceAccessor(resourceType, resourceName) azureCredentialResource := map[string]interface{}{ @@ -55,15 +58,22 @@ func TestUnitAzureCredentialsResource(t *testing.T) { } returnValues := client.Credentials{ - Id: "id", + Id: "f595c4b6-0a24-4c22-89f7-7030045de3ff", Name: "test", OrganizationId: "id", Type: string(client.AzureServicePrincipalCredentialsType), } + otherTypeReturnValues := client.Credentials{ + Id: "f595c4b6-0a24-4c22-89f7-7030045de30a", + Name: "test", + OrganizationId: "id", + Type: "GCP_....", + } + updateReturnValues := client.Credentials{ - Id: "id2", - Name: "update", + Id: "f595c4b6-0a24-4c22-89f7-7030045de3aa", + Name: "testUpdate", OrganizationId: "id", Type: string(client.AzureServicePrincipalCredentialsType), } @@ -176,4 +186,97 @@ func TestUnitAzureCredentialsResource(t *testing.T) { ) }) }) + + t.Run("import by name", func(t *testing.T) { + testCase := resource.TestCase{ + Steps: []resource.TestStep{ + { + Config: resourceConfigCreate(resourceType, resourceName, azureCredentialResource), + }, + { + ResourceName: resourceNameImport, + ImportState: true, + ImportStateId: azureCredentialResource["name"].(string), + ImportStateVerify: false, + }, + }, + } + + runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { + mock.EXPECT().AzureCredentialsCreate(azureCredCreatePayload).Times(1).Return(returnValues, nil) + mock.EXPECT().CloudCredentials(returnValues.Id).Times(2).Return(returnValues, nil) + mock.EXPECT().CloudCredentialsList().Times(1).Return([]client.Credentials{otherTypeReturnValues, returnValues}, nil) + mock.EXPECT().CloudCredentialsDelete(returnValues.Id).Times(1).Return(nil) + }) + }) + + t.Run("import by id", func(t *testing.T) { + testCase := resource.TestCase{ + Steps: []resource.TestStep{ + { + Config: resourceConfigCreate(resourceType, resourceName, azureCredentialResource), + }, + { + ResourceName: resourceNameImport, + ImportState: true, + ImportStateId: returnValues.Id, + ImportStateVerify: false, + }, + }, + } + + runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { + mock.EXPECT().AzureCredentialsCreate(azureCredCreatePayload).Times(1).Return(returnValues, nil) + mock.EXPECT().CloudCredentials(returnValues.Id).Times(3).Return(returnValues, nil) + mock.EXPECT().CloudCredentialsDelete(returnValues.Id).Times(1).Return(nil) + }) + }) + + t.Run("import by id not found", func(t *testing.T) { + testCase := resource.TestCase{ + Steps: []resource.TestStep{ + { + Config: resourceConfigCreate(resourceType, resourceName, azureCredentialResource), + }, + { + ResourceName: resourceNameImport, + ImportState: true, + ImportStateId: otherTypeReturnValues.Id, + ImportStateVerify: true, + ExpectError: regexp.MustCompile(fmt.Sprintf("azure credentials resource with id %v not found", otherTypeReturnValues.Id)), + }, + }, + } + + runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { + mock.EXPECT().AzureCredentialsCreate(azureCredCreatePayload).Times(1).Return(returnValues, nil) + mock.EXPECT().CloudCredentials(returnValues.Id).Times(1).Return(returnValues, nil) + mock.EXPECT().CloudCredentials(otherTypeReturnValues.Id).Times(1).Return(client.Credentials{}, &client.NotFoundError{}) + mock.EXPECT().CloudCredentialsDelete(returnValues.Id).Times(1).Return(nil) + }) + }) + + t.Run("import by name not found", func(t *testing.T) { + testCase := resource.TestCase{ + Steps: []resource.TestStep{ + { + Config: resourceConfigCreate(resourceType, resourceName, azureCredentialResource), + }, + { + ResourceName: resourceNameImport, + ImportState: true, + ImportStateId: azureCredentialResource["name"].(string), + ImportStateVerify: true, + ExpectError: regexp.MustCompile(fmt.Sprintf("credentials with name %v not found", azureCredentialResource["name"].(string))), + }, + }, + } + + runUnitTest(t, testCase, func(mock *client.MockApiClientInterface) { + mock.EXPECT().AzureCredentialsCreate(azureCredCreatePayload).Times(1).Return(returnValues, nil) + mock.EXPECT().CloudCredentials(returnValues.Id).Times(1).Return(returnValues, nil) + mock.EXPECT().CloudCredentialsList().Times(1).Return([]client.Credentials{otherTypeReturnValues}, nil) + mock.EXPECT().CloudCredentialsDelete(returnValues.Id).Times(1).Return(nil) + }) + }) } diff --git a/examples/resources/env0_azure_credentials/import.sh b/examples/resources/env0_azure_credentials/import.sh new file mode 100644 index 00000000..3ed0d52e --- /dev/null +++ b/examples/resources/env0_azure_credentials/import.sh @@ -0,0 +1,2 @@ +terraform import env0_azure_credentials.by_id d31a6b30-5f69-4d24-937c-22322754934e +terraform import env0_azure_credentials.by_name "credentials name" \ No newline at end of file