Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New resource azurerm_storage_sync_cloud_endpoint #8540

Merged
merged 20 commits into from Dec 9, 2020
5 changes: 5 additions & 0 deletions azurerm/internal/services/storage/client/client.go
Expand Up @@ -28,6 +28,7 @@ type Client struct {
ADLSGen2PathsClient *paths.Client
ManagementPoliciesClient *storage.ManagementPoliciesClient
BlobServicesClient *storage.BlobServicesClient
CloudEndpointsClient *storagesync.CloudEndpointsClient
EncryptionScopesClient *storage.EncryptionScopesClient
Environment az.Environment
SyncServiceClient *storagesync.ServicesClient
Expand All @@ -54,6 +55,9 @@ func NewClient(options *common.ClientOptions) *Client {
blobServicesClient := storage.NewBlobServicesClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId)
options.ConfigureClient(&blobServicesClient.Client, options.ResourceManagerAuthorizer)

cloudEndpointsClient := storagesync.NewCloudEndpointsClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId)
options.ConfigureClient(&cloudEndpointsClient.Client, options.ResourceManagerAuthorizer)

encryptionScopesClient := storage.NewEncryptionScopesClientWithBaseURI(options.ResourceManagerEndpoint, options.SubscriptionId)
options.ConfigureClient(&encryptionScopesClient.Client, options.ResourceManagerAuthorizer)

Expand All @@ -71,6 +75,7 @@ func NewClient(options *common.ClientOptions) *Client {
ADLSGen2PathsClient: &adlsGen2PathsClient,
ManagementPoliciesClient: &managementPoliciesClient,
BlobServicesClient: &blobServicesClient,
CloudEndpointsClient: &cloudEndpointsClient,
EncryptionScopesClient: &encryptionScopesClient,
Environment: options.Environment,
SubscriptionId: options.SubscriptionId,
Expand Down
@@ -0,0 +1,39 @@
package parse

import "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"

type SyncCloudEndpointId struct {
Name string
StorageSyncName string
StorageSyncGroup string
ResourceGroup string
}

func SyncCloudEndpointID(input string) (*SyncCloudEndpointId, error) {
id, err := azure.ParseAzureResourceID(input)
if err != nil {
return nil, err
}

cloudEndpoint := SyncCloudEndpointId{
ResourceGroup: id.ResourceGroup,
}

if cloudEndpoint.StorageSyncName, err = id.PopSegment("storageSyncServices"); err != nil {
return nil, err
}

if cloudEndpoint.StorageSyncGroup, err = id.PopSegment("syncGroups"); err != nil {
return nil, err
}

if cloudEndpoint.Name, err = id.PopSegment("cloudEndpoints"); err != nil {
return nil, err
}

if err := id.ValidateNoEmptySegments(input); err != nil {
return nil, err
}

return &cloudEndpoint, nil
}
@@ -0,0 +1,96 @@
package parse

import "testing"

func TestSyncCloudEndpointID(t *testing.T) {
testData := []struct {
Name string
Input string
Expected *SyncCloudEndpointId
}{
{
Name: "Empty",
Input: "",
Expected: nil,
},
{
Name: "No Resource Groups Segment",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000",
Expected: nil,
},
{
Name: "No Resource Groups Value",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/",
Expected: nil,
},
{
Name: "Resource Group ID",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/",
Expected: nil,
},
{
Name: "Missing Sync Group",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.StorageSync/storageSyncServices/sync1",
Expected: nil,
},
{
Name: "Missing Sync Group Value",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.StorageSync/storageSyncServices/sync1/syncGroups/",
Expected: nil,
},
{
Name: "Missing Cloud Endpoint",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.StorageSync/storageSyncServices/sync1/syncGroups/group1",
Expected: nil,
},
{
Name: "Missing Cloud Endpoint Value",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.StorageSync/storageSyncServices/sync1/syncGroups/group1/cloudEndpoints",
Expected: nil,
},
{
Name: "Sync Group Id",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.StorageSync/storageSyncServices/sync1/syncGroups/group1/cloudEndpoints/cloudEndpoint1",
Expected: &SyncCloudEndpointId{
Name: "cloudEndpoint1",
StorageSyncGroup: "group1",
StorageSyncName: "sync1",
ResourceGroup: "resGroup1",
},
},
{
Name: "Wrong Casing",
Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.StorageSync/storageSyncServices/sync1/syncGroups/group1/CloudEndpoints/cloudEndpoints1",
Expected: nil,
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q", v.Name)

actual, err := SyncCloudEndpointID(v.Input)
if err != nil {
if v.Expected == nil {
continue
}

t.Fatalf("Expected a value but got an error: %s", err)
}

if actual.Name != v.Expected.Name {
t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name)
}

if actual.StorageSyncGroup != v.Expected.StorageSyncGroup {
t.Fatalf("Expected %q but got %q for Storage Sync Group", v.Expected.Name, actual.Name)
}

if actual.StorageSyncName != v.Expected.StorageSyncName {
t.Fatalf("Expected %q but got %q for Storage Sync Name", v.Expected.Name, actual.Name)
}

if actual.ResourceGroup != v.Expected.ResourceGroup {
t.Fatalf("Expected %q but got %q for Resource Group", v.Expected.ResourceGroup, actual.ResourceGroup)
}
}
}
1 change: 1 addition & 0 deletions azurerm/internal/services/storage/registration.go
Expand Up @@ -50,6 +50,7 @@ func (r Registration) SupportedResources() map[string]*schema.Resource {
"azurerm_storage_table": resourceArmStorageTable(),
"azurerm_storage_table_entity": resourceArmStorageTableEntity(),
"azurerm_storage_sync": resourceArmStorageSync(),
"azurerm_storage_sync_cloud_endpoint": resourceArmStorageSyncCloudEndpoint(),
"azurerm_storage_sync_group": resourceArmStorageSyncGroup(),
}
}
@@ -0,0 +1,196 @@
package storage

import (
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/storagesync/mgmt/2020-03-01/storagesync"
"github.com/hashicorp/go-azure-helpers/response"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/validate"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmStorageSyncCloudEndpoint() *schema.Resource {
return &schema.Resource{
Create: resourceArmStorageSyncCloudEndpointCreate,
Read: resourceArmStorageSyncCloudEndpointRead,
Delete: resourceArmStorageSyncCloudEndpointDelete,

Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error {
_, err := parse.SyncCloudEndpointID(id)
return err
}),

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(45 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Delete: schema.DefaultTimeout(45 * time.Minute),
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.StorageSyncName,
},

"storage_sync_group_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.StorageSyncGroupId,
},

"file_share_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.ShareName,
},

"storage_account_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.StorageAccountID,
},
katbyte marked this conversation as resolved.
Show resolved Hide resolved

"storage_account_tenant_id": {
yupwei68 marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validation.IsUUID,
},
},
}
}

func resourceArmStorageSyncCloudEndpointCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Storage.CloudEndpointsClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

name := d.Get("name").(string)
storagesyncGroupId, _ := parse.StorageSyncGroupID(d.Get("storage_sync_group_id").(string))

existing, err := client.Get(ctx, storagesyncGroupId.ResourceGroup, storagesyncGroupId.StorageSyncName, storagesyncGroupId.Name, name)
if err != nil {
if !utils.ResponseWasNotFound(existing.Response) {
return fmt.Errorf("checking for present of existing Storage Sync Cloud Endpoint %q (Storage Sync Group %q / Storage Sync Name %q / Resource Group %q): %+v", name, storagesyncGroupId.Name, storagesyncGroupId.StorageSyncName, storagesyncGroupId.ResourceGroup, err)
}
}
if existing.ID != nil && *existing.ID != "" {
return tf.ImportAsExistsError("azurerm_storage_sync_cloud_endpoint", *existing.ID)
}

parameters := storagesync.CloudEndpointCreateParameters{
CloudEndpointCreateParametersProperties: &storagesync.CloudEndpointCreateParametersProperties{
StorageAccountResourceID: utils.String(d.Get("storage_account_id").(string)),
AzureFileShareName: utils.String(d.Get("file_share_name").(string)),
StorageAccountTenantID: utils.String(d.Get("storage_account_tenant_id").(string)),
},
}

storageAccountTenantId := meta.(*clients.Client).Account.TenantId
if v, ok := d.GetOk("storage_account_tenant_id"); ok {
storageAccountTenantId = v.(string)
}
parameters.CloudEndpointCreateParametersProperties.StorageAccountTenantID = utils.String(storageAccountTenantId)

future, err := client.Create(ctx, storagesyncGroupId.ResourceGroup, storagesyncGroupId.StorageSyncName, storagesyncGroupId.Name, name, parameters)
if err != nil {
return fmt.Errorf("creating Storage Sync Cloud Endpoint %q (Storage Sync Group %q / Storage Sync %q / Resource Group %q): %+v", name, storagesyncGroupId.Name, storagesyncGroupId.StorageSyncName, storagesyncGroupId.ResourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("waiting for Storage Sync Cloud Endpoint %q to be created: %+v", name, err)
}

resp, err := client.Get(ctx, storagesyncGroupId.ResourceGroup, storagesyncGroupId.StorageSyncName, storagesyncGroupId.Name, name)
if err != nil {
return fmt.Errorf("retrieving Storage Sync Cloud Endpoint %q (Storage Sync Group %q / Storage Sync %q / Resource Group %q): %+v", name, storagesyncGroupId.Name, storagesyncGroupId.StorageSyncName, storagesyncGroupId.ResourceGroup, err)
}

if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("reading Storage Sync Cloud Endpoint %q (Storage Sync Group %q / Storage Sync %q / Resource Group %q) ID is nil or empty", name, storagesyncGroupId.Name, storagesyncGroupId.StorageSyncName, storagesyncGroupId.ResourceGroup)
}

d.SetId(*resp.ID)

return resourceArmStorageSyncCloudEndpointRead(d, meta)
}

func resourceArmStorageSyncCloudEndpointRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Storage.CloudEndpointsClient
gpClient := meta.(*clients.Client).Storage.SyncGroupsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.SyncCloudEndpointID(d.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, id.ResourceGroup, id.StorageSyncName, id.StorageSyncGroup, id.Name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[INFO] Storage Sync Cloud Endpoint %q does not exist - removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("reading Storage Sync Cloud Endpoint %q (Storage Sync Group %q / Storage Sync %q / Resource Group %q): %+v", id.Name, id.StorageSyncGroup, id.StorageSyncName, id.ResourceGroup, err)
}
d.Set("name", resp.Name)

gpResp, err := gpClient.Get(ctx, id.ResourceGroup, id.StorageSyncName, id.StorageSyncGroup)
if err != nil {
return fmt.Errorf("reading Storage Sync Group (Storage Sync Group Name %q / Storage Sync Name %q /Resource Group %q): %+v", id.StorageSyncGroup, id.StorageSyncName, id.ResourceGroup, err)
}

if gpResp.ID == nil || *gpResp.ID == "" {
return fmt.Errorf("reading Storage Sync Group %q (Resource Group %q) ID is empty or nil", id.StorageSyncGroup, id.ResourceGroup)
}

d.Set("storage_sync_group_id", gpResp.ID)
if props := resp.CloudEndpointProperties; props != nil {
d.Set("storage_account_id", props.StorageAccountResourceID)
d.Set("file_share_name", props.AzureFileShareName)
d.Set("storage_account_tenant_id", props.StorageAccountTenantID)
}

return nil
}

func resourceArmStorageSyncCloudEndpointDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Storage.CloudEndpointsClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.SyncCloudEndpointID(d.Id())
if err != nil {
return err
}

future, err := client.Delete(ctx, id.ResourceGroup, id.StorageSyncName, id.StorageSyncGroup, id.Name)
if err != nil {
return fmt.Errorf("deleting Storage Sync Cloud Endpoint %q (Storage Sync Group %q / Storage Sync %q / Resource Group %q): %+v", id.Name, id.StorageSyncGroup, id.StorageSyncName, id.ResourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
if !response.WasNotFound(future.Response()) {
return fmt.Errorf("waiting for deletion of Storage Sync Cloud Endpoint %q (Storage Sync Group %q / Storage Sync %q / Resource Group %q): %+v", id.Name, id.StorageSyncGroup, id.StorageSyncName, id.ResourceGroup, err)
}
}

return nil
}