From 060459c1be86b89e34ae2b2396fee0229f6d4739 Mon Sep 17 00:00:00 2001 From: Neil Ye Date: Fri, 12 Nov 2021 01:32:49 +0800 Subject: [PATCH] New Resource: azurerm_monitor_private_link_scope (#14098) This PR to support new resource Azure Monitor Private Link Scope. Background: docs.microsoft.com/en-us/azure/azure-monitor/logs/private-link-security Rest API Spec: Azure/azure-rest-api-specs@ddda805/specification/monitor/resource-manager/Microsoft.Insights/preview/2021-07-01-preview/privateLinkScopes_API.json#L196 --- internal/services/monitor/client/client.go | 5 + .../monitor_private_link_scope_resource.go | 138 ++++++++++++++++ ...onitor_private_link_scope_resource_test.go | 150 ++++++++++++++++++ .../monitor/parse/private_link_scope.go | 69 ++++++++ .../monitor/parse/private_link_scope_test.go | 112 +++++++++++++ internal/services/monitor/registration.go | 1 + internal/services/monitor/resourceids.go | 1 + .../monitor/validate/private_link_scope_id.go | 23 +++ .../validate/private_link_scope_id_test.go | 76 +++++++++ .../validate/private_link_scope_name.go | 31 ++++ .../validate/private_link_scope_name_test.go | 76 +++++++++ .../monitor_private_link_scope.html.markdown | 58 +++++++ 12 files changed, 740 insertions(+) create mode 100644 internal/services/monitor/monitor_private_link_scope_resource.go create mode 100644 internal/services/monitor/monitor_private_link_scope_resource_test.go create mode 100644 internal/services/monitor/parse/private_link_scope.go create mode 100644 internal/services/monitor/parse/private_link_scope_test.go create mode 100644 internal/services/monitor/validate/private_link_scope_id.go create mode 100644 internal/services/monitor/validate/private_link_scope_id_test.go create mode 100644 internal/services/monitor/validate/private_link_scope_name.go create mode 100644 internal/services/monitor/validate/private_link_scope_name_test.go create mode 100644 website/docs/r/monitor_private_link_scope.html.markdown diff --git a/internal/services/monitor/client/client.go b/internal/services/monitor/client/client.go index fccbecf85e2a..ed3e466bcaf0 100644 --- a/internal/services/monitor/client/client.go +++ b/internal/services/monitor/client/client.go @@ -27,6 +27,7 @@ type Client struct { DiagnosticSettingsCategoryClient *classic.DiagnosticSettingsCategoryClient LogProfilesClient *classic.LogProfilesClient MetricAlertsClient *classic.MetricAlertsClient + PrivateLinkScopesClient *classic.PrivateLinkScopesClient ScheduledQueryRulesClient *classic.ScheduledQueryRulesClient } @@ -64,6 +65,9 @@ func NewClient(o *common.ClientOptions) *Client { MetricAlertsClient := classic.NewMetricAlertsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&MetricAlertsClient.Client, o.ResourceManagerAuthorizer) + PrivateLinkScopesClient := classic.NewPrivateLinkScopesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&PrivateLinkScopesClient.Client, o.ResourceManagerAuthorizer) + ScheduledQueryRulesClient := classic.NewScheduledQueryRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&ScheduledQueryRulesClient.Client, o.ResourceManagerAuthorizer) @@ -79,6 +83,7 @@ func NewClient(o *common.ClientOptions) *Client { DiagnosticSettingsCategoryClient: &DiagnosticSettingsCategoryClient, LogProfilesClient: &LogProfilesClient, MetricAlertsClient: &MetricAlertsClient, + PrivateLinkScopesClient: &PrivateLinkScopesClient, ScheduledQueryRulesClient: &ScheduledQueryRulesClient, } } diff --git a/internal/services/monitor/monitor_private_link_scope_resource.go b/internal/services/monitor/monitor_private_link_scope_resource.go new file mode 100644 index 000000000000..54a83ae90ddc --- /dev/null +++ b/internal/services/monitor/monitor_private_link_scope_resource.go @@ -0,0 +1,138 @@ +package monitor + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2021-07-01-preview/insights" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceMonitorPrivateLinkScope() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceMonitorPrivateLinkScopeCreateUpdate, + Read: resourceMonitorPrivateLinkScopeRead, + Update: resourceMonitorPrivateLinkScopeCreateUpdate, + Delete: resourceMonitorPrivateLinkScopeDelete, + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.PrivateLinkScopeID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.PrivateLinkScopeName, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "tags": tags.Schema(), + }, + } +} + +func resourceMonitorPrivateLinkScopeCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + client := meta.(*clients.Client).Monitor.PrivateLinkScopesClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + id := parse.NewPrivateLinkScopeID(subscriptionId, resourceGroup, name) + + if d.IsNewResource() { + existing, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_monitor_private_link_scope", id.ID()) + } + } + + parameters := insights.AzureMonitorPrivateLinkScope{ + Location: utils.String("Global"), + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + } + + if _, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters); err != nil { + return fmt.Errorf("creating/updating %s: %+v", id, err) + } + + d.SetId(id.ID()) + + return resourceMonitorPrivateLinkScopeRead(d, meta) +} + +func resourceMonitorPrivateLinkScopeRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Monitor.PrivateLinkScopesClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.PrivateLinkScopeID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] %s does not exist - removing from state!", id) + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + d.Set("name", id.Name) + d.Set("resource_group_name", id.ResourceGroup) + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceMonitorPrivateLinkScopeDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Monitor.PrivateLinkScopesClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.PrivateLinkScopeID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.Name) + if err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for deletion of %s: %+v", *id, err) + } + + return nil +} diff --git a/internal/services/monitor/monitor_private_link_scope_resource_test.go b/internal/services/monitor/monitor_private_link_scope_resource_test.go new file mode 100644 index 000000000000..f7033ec686ee --- /dev/null +++ b/internal/services/monitor/monitor_private_link_scope_resource_test.go @@ -0,0 +1,150 @@ +package monitor_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type MonitorPrivateLinkScopeResource struct{} + +func TestAccMonitorPrivateLinkScope_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_private_link_scope", "test") + r := MonitorPrivateLinkScopeResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorPrivateLinkScope_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_private_link_scope", "test") + r := MonitorPrivateLinkScopeResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccMonitorPrivateLinkScope_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_private_link_scope", "test") + r := MonitorPrivateLinkScopeResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data, "Test"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccMonitorPrivateLinkScope_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_monitor_private_link_scope", "test") + r := MonitorPrivateLinkScopeResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data, "Test1"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data, "Test2"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r MonitorPrivateLinkScopeResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.PrivateLinkScopeID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.Monitor.PrivateLinkScopesClient.Get(ctx, id.ResourceGroup, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %q %+v", id, err) + } + + return utils.Bool(resp.AzureMonitorPrivateLinkScopeProperties != nil), nil +} + +func (r MonitorPrivateLinkScopeResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-pls-%d" + location = "%s" +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (r MonitorPrivateLinkScopeResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_private_link_scope" "test" { + name = "acctest-ampls-%d" + resource_group_name = azurerm_resource_group.test.name +} +`, r.template(data), data.RandomInteger) +} + +func (r MonitorPrivateLinkScopeResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_private_link_scope" "import" { + name = azurerm_monitor_private_link_scope.test.name + resource_group_name = azurerm_monitor_private_link_scope.test.resource_group_name +} +`, r.basic(data)) +} + +func (r MonitorPrivateLinkScopeResource) complete(data acceptance.TestData, tag string) string { + return fmt.Sprintf(` +%s + +resource "azurerm_monitor_private_link_scope" "test" { + name = "acctest-AMPLS-%d" + resource_group_name = azurerm_resource_group.test.name + + tags = { + ENV = "%s" + } +} +`, r.template(data), data.RandomInteger, tag) +} diff --git a/internal/services/monitor/parse/private_link_scope.go b/internal/services/monitor/parse/private_link_scope.go new file mode 100644 index 000000000000..5c3049a319a8 --- /dev/null +++ b/internal/services/monitor/parse/private_link_scope.go @@ -0,0 +1,69 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" +) + +type PrivateLinkScopeId struct { + SubscriptionId string + ResourceGroup string + Name string +} + +func NewPrivateLinkScopeID(subscriptionId, resourceGroup, name string) PrivateLinkScopeId { + return PrivateLinkScopeId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + Name: name, + } +} + +func (id PrivateLinkScopeId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Private Link Scope", segmentsStr) +} + +func (id PrivateLinkScopeId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Insights/privateLinkScopes/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.Name) +} + +// PrivateLinkScopeID parses a PrivateLinkScope ID into an PrivateLinkScopeId struct +func PrivateLinkScopeID(input string) (*PrivateLinkScopeId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := PrivateLinkScopeId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.Name, err = id.PopSegment("privateLinkScopes"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/monitor/parse/private_link_scope_test.go b/internal/services/monitor/parse/private_link_scope_test.go new file mode 100644 index 000000000000..1c09f4d69709 --- /dev/null +++ b/internal/services/monitor/parse/private_link_scope_test.go @@ -0,0 +1,112 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/resourceid" +) + +var _ resourceid.Formatter = PrivateLinkScopeId{} + +func TestPrivateLinkScopeIDFormatter(t *testing.T) { + actual := NewPrivateLinkScopeID("12345678-1234-9876-4563-123456789012", "group1", "pls1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/privateLinkScopes/pls1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestPrivateLinkScopeID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *PrivateLinkScopeId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/", + Error: true, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/privateLinkScopes/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/privateLinkScopes/pls1", + Expected: &PrivateLinkScopeId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "group1", + Name: "pls1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.INSIGHTS/PRIVATELINKSCOPES/PLS1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := PrivateLinkScopeID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/internal/services/monitor/registration.go b/internal/services/monitor/registration.go index 791dd7eea5b1..78a0beaf6643 100644 --- a/internal/services/monitor/registration.go +++ b/internal/services/monitor/registration.go @@ -41,6 +41,7 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_monitor_diagnostic_setting": resourceMonitorDiagnosticSetting(), "azurerm_monitor_log_profile": resourceMonitorLogProfile(), "azurerm_monitor_metric_alert": resourceMonitorMetricAlert(), + "azurerm_monitor_private_link_scope": resourceMonitorPrivateLinkScope(), "azurerm_monitor_scheduled_query_rules_alert": resourceMonitorScheduledQueryRulesAlert(), "azurerm_monitor_scheduled_query_rules_log": resourceMonitorScheduledQueryRulesLog(), "azurerm_monitor_smart_detector_alert_rule": resourceMonitorSmartDetectorAlertRule(), diff --git a/internal/services/monitor/resourceids.go b/internal/services/monitor/resourceids.go index 8b54ac58422f..1fa157bc3c84 100644 --- a/internal/services/monitor/resourceids.go +++ b/internal/services/monitor/resourceids.go @@ -7,4 +7,5 @@ package monitor //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=AutoscaleSetting -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/autoscaleSettings/setting1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=LogProfile -id=/subscriptions/12345678-1234-9876-4563-123456789012/providers/Microsoft.Insights/logProfiles/profile1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=MetricAlert -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/metricAlerts/alert1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=PrivateLinkScope -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/privateLinkScopes/pls1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=ScheduledQueryRules -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/scheduledQueryRules/rule1 diff --git a/internal/services/monitor/validate/private_link_scope_id.go b/internal/services/monitor/validate/private_link_scope_id.go new file mode 100644 index 000000000000..643b0f3aa8cd --- /dev/null +++ b/internal/services/monitor/validate/private_link_scope_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/monitor/parse" +) + +func PrivateLinkScopeID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.PrivateLinkScopeID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/monitor/validate/private_link_scope_id_test.go b/internal/services/monitor/validate/private_link_scope_id_test.go new file mode 100644 index 000000000000..1b6274d5b331 --- /dev/null +++ b/internal/services/monitor/validate/private_link_scope_id_test.go @@ -0,0 +1,76 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestPrivateLinkScopeID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/", + Valid: false, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/privateLinkScopes/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.Insights/privateLinkScopes/pls1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.INSIGHTS/PRIVATELINKSCOPES/PLS1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := PrivateLinkScopeID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/internal/services/monitor/validate/private_link_scope_name.go b/internal/services/monitor/validate/private_link_scope_name.go new file mode 100644 index 000000000000..2c0216ab4459 --- /dev/null +++ b/internal/services/monitor/validate/private_link_scope_name.go @@ -0,0 +1,31 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func PrivateLinkScopeName(i interface{}, k string) (warning []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + if len(v) < 1 { + errors = append(errors, fmt.Errorf("length should be greater than %d", 1)) + return + } + + if len(v) > 255 { + errors = append(errors, fmt.Errorf("length should be less and equal than %d", 255)) + return + } + + if !regexp.MustCompile(`^[a-zA-Z0-9()-_.]*[a-zA-Z0-9_-]$`).MatchString(v) { + errors = append(errors, fmt.Errorf("%q only allows alphanumeric characters, periods, underscores, hyphens and parenthesis and cannot end in a period", k)) + return + } + + return +} diff --git a/internal/services/monitor/validate/private_link_scope_name_test.go b/internal/services/monitor/validate/private_link_scope_name_test.go new file mode 100644 index 000000000000..805e7dc6f0f6 --- /dev/null +++ b/internal/services/monitor/validate/private_link_scope_name_test.go @@ -0,0 +1,76 @@ +package validate + +import ( + "strings" + "testing" +) + +func TestPrivateLinkScopeName(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + input: "", + expected: false, + }, + { + input: "a", + expected: true, + }, + { + input: "A", + expected: true, + }, + { + input: "8", + expected: true, + }, + { + input: "a_-.b", + expected: true, + }, + { + input: "ab_", + expected: true, + }, + { + input: "ab-", + expected: true, + }, + { + input: "a+", + expected: false, + }, + { + input: "a.", + expected: false, + }, + { + input: "a.", + expected: false, + }, + { + input: strings.Repeat("s", 254), + expected: true, + }, + { + input: strings.Repeat("s", 255), + expected: true, + }, + { + input: strings.Repeat("s", 256), + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := PrivateLinkScopeName(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} diff --git a/website/docs/r/monitor_private_link_scope.html.markdown b/website/docs/r/monitor_private_link_scope.html.markdown new file mode 100644 index 000000000000..63c5f7eda4aa --- /dev/null +++ b/website/docs/r/monitor_private_link_scope.html.markdown @@ -0,0 +1,58 @@ +--- +subcategory: "Monitor" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_monitor_private_link_scope" +description: |- + Manages an Azure Monitor Private Link Scope +--- + +# azurerm_monitor_private_link_scope + +Manages an Azure Monitor Private Link Scope. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_monitor_private_link_scope" "example" { + name = "example-ampls" + resource_group_name = azurerm_resource_group.example.name +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Azure Monitor Private Link Scope. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the Resource Group where the Azure Monitor Private Link Scope should exist. Changing this forces a new resource to be created. + +* `tags` - (Optional) A mapping of tags which should be assigned to the Azure Monitor Private Link Scope. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Azure Monitor Private Link Scope. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 5 hours) Used when creating the Azure Monitor Private Link Scope. +* `read` - (Defaults to 5 minutes) Used when retrieving the Azure Monitor Private Link Scope. +* `update` - (Defaults to 5 hours) Used when updating the Azure Monitor Private Link Scope. +* `delete` - (Defaults to 5 hours) Used when deleting the Azure Monitor Private Link Scope. + +## Import + +Azure Monitor Private Link Scopes can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_monitor_private_link_scope.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Insights/privateLinkScopes/pls1 +```