Skip to content

Commit

Permalink
New Resource: azurerm_monitor_private_link_scope (hashicorp#14098)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Neil Ye authored Nov 11, 2021
1 parent 3c83e15 commit 060459c
Show file tree
Hide file tree
Showing 12 changed files with 740 additions and 0 deletions.
5 changes: 5 additions & 0 deletions internal/services/monitor/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Client struct {
DiagnosticSettingsCategoryClient *classic.DiagnosticSettingsCategoryClient
LogProfilesClient *classic.LogProfilesClient
MetricAlertsClient *classic.MetricAlertsClient
PrivateLinkScopesClient *classic.PrivateLinkScopesClient
ScheduledQueryRulesClient *classic.ScheduledQueryRulesClient
}

Expand Down Expand Up @@ -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)

Expand All @@ -79,6 +83,7 @@ func NewClient(o *common.ClientOptions) *Client {
DiagnosticSettingsCategoryClient: &DiagnosticSettingsCategoryClient,
LogProfilesClient: &LogProfilesClient,
MetricAlertsClient: &MetricAlertsClient,
PrivateLinkScopesClient: &PrivateLinkScopesClient,
ScheduledQueryRulesClient: &ScheduledQueryRulesClient,
}
}
138 changes: 138 additions & 0 deletions internal/services/monitor/monitor_private_link_scope_resource.go
Original file line number Diff line number Diff line change
@@ -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
}
150 changes: 150 additions & 0 deletions internal/services/monitor/monitor_private_link_scope_resource_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
69 changes: 69 additions & 0 deletions internal/services/monitor/parse/private_link_scope.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 060459c

Please sign in to comment.