From c652fc2f4a480514a3026c443582c03f54d0d898 Mon Sep 17 00:00:00 2001 From: Matthew Lewinski Date: Wed, 23 Jan 2019 17:15:28 -0600 Subject: [PATCH 1/6] Add data source for Network Watcher --- azurerm/data_source_network_watcher.go | 52 +++++++++++++++++ azurerm/data_source_network_watcher_test.go | 59 ++++++++++++++++++++ azurerm/provider.go | 1 + azurerm/resource_arm_network_watcher_test.go | 3 + 4 files changed, 115 insertions(+) create mode 100644 azurerm/data_source_network_watcher.go create mode 100644 azurerm/data_source_network_watcher_test.go diff --git a/azurerm/data_source_network_watcher.go b/azurerm/data_source_network_watcher.go new file mode 100644 index 000000000000..1aaa93d1d1e0 --- /dev/null +++ b/azurerm/data_source_network_watcher.go @@ -0,0 +1,52 @@ +package azurerm + +import ( + "fmt" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func dataSourceArmNetworkWatcher() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmNetworkWatcherRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + + "resource_group_name": resourceGroupNameForDataSourceSchema(), + "location": locationForDataSourceSchema(), + "tags": tagsForDataSourceSchema(), + }, + } +} + +func dataSourceArmNetworkWatcherRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).watcherClient + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + ctx := meta.(*ArmClient).StopContext + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Error: Network Watcher %q (Resource Group %q) was not found", name, resourceGroup) + } + return fmt.Errorf("Error making Read request on Network Watcher %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.SetId(*resp.ID) + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + if location := resp.Location; location != nil { + d.Set("location", azureRMNormalizeLocation(*location)) + } + flattenAndSetTags(d, resp.Tags) + + return nil +} diff --git a/azurerm/data_source_network_watcher_test.go b/azurerm/data_source_network_watcher_test.go new file mode 100644 index 000000000000..63df90992d21 --- /dev/null +++ b/azurerm/data_source_network_watcher_test.go @@ -0,0 +1,59 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func testAccDataSourceAzureRMNetworkWatcher_basic(t *testing.T) { + dataSourceName := "data.azurerm_network_watcher.test" + + ri := tf.AccRandTimeInt() + name := fmt.Sprintf("acctestnw-%d", ri) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceAzureRMNetworkWatcher_basicConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "id"), + resource.TestCheckResourceAttr(dataSourceName, "name", name), + resource.TestCheckResourceAttrSet(dataSourceName, "resource_group_name"), + resource.TestCheckResourceAttr(dataSourceName, "location", azureRMNormalizeLocation(location)), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(dataSourceName, "tags.env", "test"), + ), + }, + }, + }) +} + +func testAccDataSourceAzureRMNetworkWatcher_basicConfig(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_network_watcher" "test" { + name = "acctestnw-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + tags { + env = "test" + } +} + +data "azurerm_network_watcher" "test" { + name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 5af4de2cbf80..37c9c75eed13 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -133,6 +133,7 @@ func Provider() terraform.ResourceProvider { "azurerm_monitor_log_profile": dataSourceArmMonitorLogProfile(), "azurerm_network_interface": dataSourceArmNetworkInterface(), "azurerm_network_security_group": dataSourceArmNetworkSecurityGroup(), + "azurerm_network_watcher": dataSourceArmNetworkWatcher(), "azurerm_notification_hub_namespace": dataSourceNotificationHubNamespace(), "azurerm_notification_hub": dataSourceNotificationHub(), "azurerm_platform_image": dataSourceArmPlatformImage(), diff --git a/azurerm/resource_arm_network_watcher_test.go b/azurerm/resource_arm_network_watcher_test.go index 144d714f72ee..39b823baaf0f 100644 --- a/azurerm/resource_arm_network_watcher_test.go +++ b/azurerm/resource_arm_network_watcher_test.go @@ -23,6 +23,9 @@ func TestAccAzureRMNetworkWatcher(t *testing.T) { "update": testAccAzureRMNetworkWatcher_update, "disappears": testAccAzureRMNetworkWatcher_disappears, }, + "DataSource": { + "basic": testAccDataSourceAzureRMNetworkWatcher_basic, + }, "PacketCapture": { "localDisk": testAccAzureRMPacketCapture_localDisk, "storageAccount": testAccAzureRMPacketCapture_storageAccount, From 062d6220caafa757b8592cfebe81bf0364056c28 Mon Sep 17 00:00:00 2001 From: Matthew Lewinski Date: Wed, 23 Jan 2019 17:30:21 -0600 Subject: [PATCH 2/6] Add documentation for Network Watcher data source --- website/azurerm.erb | 4 +++ website/docs/d/network_watcher.html.markdown | 38 ++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 website/docs/d/network_watcher.html.markdown diff --git a/website/azurerm.erb b/website/azurerm.erb index 3d1150a68e3c..b2a67048c4fb 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -199,6 +199,10 @@ azurerm_network_security_group + > + azurerm_network_watcher + + > azurerm_notification_hub_namespace diff --git a/website/docs/d/network_watcher.html.markdown b/website/docs/d/network_watcher.html.markdown new file mode 100644 index 000000000000..a69d7a1f4059 --- /dev/null +++ b/website/docs/d/network_watcher.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_network_watcher" +sidebar_current: "docs-azurerm-datasource-network-watcher" +description: |- + Gets information about an existing Network Watcher. +--- + +# Data Source: azurerm_network_watcher + +Use this data source to access information about an existing Network Watcher. + +## Example Usage + +```hcl +data "azurerm_network_watcher" "test" { + name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +output "network_watcher_id" { + value = "${data.azurerm_network_watcher.test.id}" +} +``` + +## Argument Reference + +* `name` - (Required) Specifies the Name of the Network Watcher. +* `resource_group_name` - (Required) Specifies the Name of the Resource Group within which the Network Watcher exists. + + +## Attributes Reference + +* `id` - The ID of the Network Watcher. + +* `location` - The supported Azure location where the resource exists. + +* `tags` - A mapping of tags assigned to the resource. From 6756e89392d622405ba04367a11291a650f077cd Mon Sep 17 00:00:00 2001 From: Matthew Lewinski Date: Fri, 25 Jan 2019 17:21:34 -0600 Subject: [PATCH 3/6] Add Connection Monitor resource --- azurerm/config.go | 5 + azurerm/provider.go | 1 + azurerm/resource_arm_connection_monitor.go | 322 +++++++++ .../resource_arm_connection_monitor_test.go | 641 ++++++++++++++++++ azurerm/resource_arm_network_watcher_test.go | 11 + 5 files changed, 980 insertions(+) create mode 100644 azurerm/resource_arm_connection_monitor.go create mode 100644 azurerm/resource_arm_connection_monitor_test.go diff --git a/azurerm/config.go b/azurerm/config.go index 0664504fad9b..73ba13990899 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -244,6 +244,7 @@ type ArmClient struct { applicationGatewayClient network.ApplicationGatewaysClient applicationSecurityGroupsClient network.ApplicationSecurityGroupsClient azureFirewallsClient network.AzureFirewallsClient + connectionMonitorsClient network.ConnectionMonitorsClient ddosProtectionPlanClient network.DdosProtectionPlansClient expressRouteAuthsClient network.ExpressRouteCircuitAuthorizationsClient expressRouteCircuitClient network.ExpressRouteCircuitsClient @@ -909,6 +910,10 @@ func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, a c.configureClient(&azureFirewallsClient.Client, auth) c.azureFirewallsClient = azureFirewallsClient + connectionMonitorsClient := network.NewConnectionMonitorsClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&connectionMonitorsClient.Client, auth) + c.connectionMonitorsClient = connectionMonitorsClient + ddosProtectionPlanClient := network.NewDdosProtectionPlansClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&ddosProtectionPlanClient.Client, auth) c.ddosProtectionPlanClient = ddosProtectionPlanClient diff --git a/azurerm/provider.go b/azurerm/provider.go index 37c9c75eed13..e27409b8f090 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -189,6 +189,7 @@ func Provider() terraform.ResourceProvider { "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), "azurerm_cdn_profile": resourceArmCdnProfile(), "azurerm_cognitive_account": resourceArmCognitiveAccount(), + "azurerm_connection_monitor": resourceArmConnectionMonitor(), "azurerm_container_group": resourceArmContainerGroup(), "azurerm_container_registry": resourceArmContainerRegistry(), "azurerm_container_service": resourceArmContainerService(), diff --git a/azurerm/resource_arm_connection_monitor.go b/azurerm/resource_arm_connection_monitor.go new file mode 100644 index 000000000000..d5455aeaf549 --- /dev/null +++ b/azurerm/resource_arm_connection_monitor.go @@ -0,0 +1,322 @@ +package azurerm + +import ( + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-08-01/network" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceArmConnectionMonitor() *schema.Resource { + return &schema.Resource{ + Create: resourceArmConnectionMonitorCreateUpdate, + Read: resourceArmConnectionMonitorRead, + Update: resourceArmConnectionMonitorCreateUpdate, + Delete: resourceArmConnectionMonitorDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "network_watcher_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "location": locationSchema(), + + "auto_start": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: true, + }, + + "interval_in_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 60, + ValidateFunc: validation.IntAtLeast(30), + }, + + "source": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "virtual_machine_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "port": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validate.PortNumber, + }, + }, + }, + }, + + "destination": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "virtual_machine_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: azure.ValidateResourceID, + ConflictsWith: []string{"destination.0.address"}, + }, + "address": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"destination.0.virtual_machine_id"}, + }, + "port": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validate.PortNumber, + }, + }, + }, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmConnectionMonitorCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).connectionMonitorsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + watcherName := d.Get("network_watcher_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + location := azureRMNormalizeLocation(d.Get("location").(string)) + autoStart := d.Get("auto_start").(bool) + intervalInSeconds := int32(d.Get("interval_in_seconds").(int)) + + source, err := expandArmConnectionMonitorSource(d) + if err != nil { + return err + } + + dest, err := expandArmConnectionMonitorDestination(d) + if err != nil { + return err + } + + tags := d.Get("tags").(map[string]interface{}) + + properties := network.ConnectionMonitor{ + Location: utils.String(location), + Tags: expandTags(tags), + ConnectionMonitorParameters: &network.ConnectionMonitorParameters{ + Source: source, + Destination: dest, + AutoStart: utils.Bool(autoStart), + MonitoringIntervalInSeconds: utils.Int32(intervalInSeconds), + }, + } + + future, err := client.CreateOrUpdate(ctx, resourceGroup, watcherName, name, properties) + if err != nil { + return fmt.Errorf("Error creating Connection Monitor %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for completion of Connection Monitor %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, watcherName, name) + if err != nil { + return fmt.Errorf("Error retrieving Connection Monitor %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + if resp.ID == nil { + return fmt.Errorf("Cannot read Connection Monitor %q (Watcher %q / Resource Group %q) ID", name, watcherName, resourceGroup) + } + + d.SetId(*resp.ID) + + return resourceArmConnectionMonitorRead(d, meta) +} + +func resourceArmConnectionMonitorRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).connectionMonitorsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + watcherName := id.Path["networkWatchers"] + name := id.Path["connectionMonitors"] + + resp, err := client.Get(ctx, resourceGroup, watcherName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading Connection Monitor %q (Watcher %q / Resource Group %q) %+v", name, watcherName, resourceGroup, err) + } + + d.Set("name", name) + d.Set("network_watcher_name", watcherName) + d.Set("resource_group_name", resourceGroup) + if location := resp.Location; location != nil { + d.Set("location", azureRMNormalizeLocation(*location)) + } + + if props := resp.ConnectionMonitorResultProperties; props != nil { + d.Set("auto_start", props.AutoStart) + d.Set("interval_in_seconds", int(*props.MonitoringIntervalInSeconds)) + + source := flattenArmConnectionMonitorSource(props.Source) + if err := d.Set("source", source); err != nil { + return fmt.Errorf("Error setting `source`: %+v", err) + } + + dest := flattenArmConnectionMonitorDestination(props.Destination) + if err := d.Set("destination", dest); err != nil { + return fmt.Errorf("Error setting `destination`: %+v", err) + } + } + + flattenAndSetTags(d, resp.Tags) + + return nil +} + +func resourceArmConnectionMonitorDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).connectionMonitorsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + watcherName := id.Path["networkWatchers"] + name := id.Path["connectionMonitors"] + + future, err := client.Delete(ctx, resourceGroup, watcherName, name) + if err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error deleting Connection Monitor %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for the deletion of Connection Monitor %q (Watcher %q / Resource Group %q): %+v", name, watcherName, resourceGroup, err) + } + + return nil +} + +func flattenArmConnectionMonitorSource(input *network.ConnectionMonitorSource) []interface{} { + if input == nil { + return []interface{}{} + } + + output := make(map[string]interface{}) + + if resourceID := input.ResourceID; resourceID != nil { + output["virtual_machine_id"] = *resourceID + } + if port := input.Port; port != nil { + output["port"] = *port + } + + return []interface{}{output} +} + +func expandArmConnectionMonitorSource(d *schema.ResourceData) (*network.ConnectionMonitorSource, error) { + sources := d.Get("source").([]interface{}) + if len(sources) == 0 { + return nil, fmt.Errorf("Error expanding `source`: not found") + } + source := sources[0].(map[string]interface{}) + + monitorSource := network.ConnectionMonitorSource{} + if v := source["virtual_machine_id"]; v != "" { + monitorSource.ResourceID = utils.String(v.(string)) + } + if v := source["port"]; v != "" { + monitorSource.Port = utils.Int32(int32(v.(int))) + } + + return &monitorSource, nil +} + +func flattenArmConnectionMonitorDestination(input *network.ConnectionMonitorDestination) []interface{} { + if input == nil { + return []interface{}{} + } + + output := make(map[string]interface{}) + + // When monitoring a VM, the address field will contain the current address + // of the VM. We only want to copy over the address field if the virtual + // machine field is not set to avoid unwanted diffs. + if resourceID := input.ResourceID; resourceID != nil { + output["virtual_machine_id"] = *resourceID + } else if address := input.Address; address != nil { + output["address"] = *address + } + + if port := input.Port; port != nil { + output["port"] = *port + } + + return []interface{}{output} +} + +func expandArmConnectionMonitorDestination(d *schema.ResourceData) (*network.ConnectionMonitorDestination, error) { + dests := d.Get("destination").([]interface{}) + if len(dests) == 0 { + return nil, fmt.Errorf("Error expanding `destination`: not found") + } + dest := dests[0].(map[string]interface{}) + + monitorDest := network.ConnectionMonitorDestination{} + + if v := dest["virtual_machine_id"]; v != "" { + monitorDest.ResourceID = utils.String(v.(string)) + } + if v := dest["address"]; v != "" { + monitorDest.Address = utils.String(v.(string)) + } + if v := dest["port"]; v != "" { + monitorDest.Port = utils.Int32(int32(v.(int))) + } + + if monitorDest.ResourceID == nil && monitorDest.Address == nil { + return nil, fmt.Errorf("Error: either `destination.virtual_machine_id` or `destination.address` must be specified") + } + + return &monitorDest, nil +} diff --git a/azurerm/resource_arm_connection_monitor_test.go b/azurerm/resource_arm_connection_monitor_test.go new file mode 100644 index 000000000000..5d112aec3a87 --- /dev/null +++ b/azurerm/resource_arm_connection_monitor_test.go @@ -0,0 +1,641 @@ +package azurerm + +import ( + "fmt" + "net/http" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func testAccAzureRMConnectionMonitor_addressBasic(t *testing.T) { + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_basicAddressConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "resource_group_name"), + resource.TestCheckResourceAttr(resourceName, "location", azureRMNormalizeLocation(location)), + resource.TestCheckResourceAttr(resourceName, "auto_start", "true"), + resource.TestCheckResourceAttr(resourceName, "interval_in_seconds", "60"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_addressComplete(t *testing.T) { + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + autoStart := "false" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_completeAddressConfig(ri, location, autoStart), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_start", "false"), + resource.TestCheckResourceAttr(resourceName, "interval_in_seconds", "30"), + resource.TestCheckResourceAttr(resourceName, "source.0.port", "20020"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.env", "test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_addressUpdate(t *testing.T) { + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + autoStart := "true" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_basicAddressConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + ), + }, + { + Config: testAccAzureRMConnectionMonitor_completeAddressConfig(ri, location, autoStart), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_start", "true"), + resource.TestCheckResourceAttr(resourceName, "interval_in_seconds", "30"), + resource.TestCheckResourceAttr(resourceName, "source.0.port", "20020"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.env", "test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_vmBasic(t *testing.T) { + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_basicVmConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "resource_group_name"), + resource.TestCheckResourceAttr(resourceName, "location", azureRMNormalizeLocation(location)), + resource.TestCheckResourceAttr(resourceName, "auto_start", "true"), + resource.TestCheckResourceAttr(resourceName, "interval_in_seconds", "60"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_vmComplete(t *testing.T) { + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + autoStart := "false" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_completeVmConfig(ri, location, autoStart), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_start", "false"), + resource.TestCheckResourceAttr(resourceName, "interval_in_seconds", "30"), + resource.TestCheckResourceAttr(resourceName, "source.0.port", "20020"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.env", "test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_vmUpdate(t *testing.T) { + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_basicVmConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + ), + }, + { + Config: testAccAzureRMConnectionMonitor_completeVmConfig(ri, location, "true"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_start", "true"), + resource.TestCheckResourceAttr(resourceName, "interval_in_seconds", "30"), + resource.TestCheckResourceAttr(resourceName, "source.0.port", "20020"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.env", "test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_destinationUpdate(t *testing.T) { + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_basicAddressConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "destination.0.address"), + ), + }, + { + Config: testAccAzureRMConnectionMonitor_basicVmConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "destination.0.virtual_machine_id"), + ), + }, + { + Config: testAccAzureRMConnectionMonitor_basicAddressConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "destination.0.address"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_missingDestination(t *testing.T) { + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_missingDestinationConfig(ri, location), + ExpectError: regexp.MustCompile("Error: either `destination.virtual_machine_id` or `destination.address` must be specified"), + }, + }, + }) +} + +func testAccAzureRMConnectionMonitor_conflictingDestinations(t *testing.T) { + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_conflictingDestinationsConfig(ri, location), + ExpectError: regexp.MustCompile("conflicts with destination.0.address"), + }, + }, + }) +} + +func testCheckAzureRMConnectionMonitorExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + watcherName := rs.Primary.Attributes["network_watcher_name"] + connectionMonitorName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).connectionMonitorsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, watcherName, connectionMonitorName) + if err != nil { + return fmt.Errorf("Bad: Get on connectionMonitorsClient: %s", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Connection Monitor does not exist: %s", connectionMonitorName) + } + + return nil + } +} + +func testCheckAzureRMConnectionMonitorDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).connectionMonitorsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_connection_monitor" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + watcherName := rs.Primary.Attributes["network_watcher_name"] + connectionMonitorName := rs.Primary.Attributes["name"] + + resp, err := client.Get(ctx, resourceGroup, watcherName, connectionMonitorName) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Connection Monitor still exists:%s", *resp.Name) + } + } + + return nil +} + +func testAccAzureRMConnectionMonitor_baseConfig(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_network_watcher" "test" { + name = "acctnw-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "src" { + name = "acctni-src%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_virtual_machine" "src" { + name = "acctvm-src%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.src.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "osdisk-src%d" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "hostname%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} + +resource "azurerm_virtual_machine_extension" "src" { + name = "network-watcher" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_machine_name = "${azurerm_virtual_machine.src.name}" + publisher = "Microsoft.Azure.NetworkWatcher" + type = "NetworkWatcherAgentLinux" + type_handler_version = "1.4" + auto_upgrade_minor_version = true +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMConnectionMonitor_baseWithDestConfig(rInt int, location string) string { + config := testAccAzureRMConnectionMonitor_baseConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_network_interface" "dest" { + name = "acctni-dest%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_virtual_machine" "dest" { + name = "acctvm-dest%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.dest.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "osdisk-dest%d" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "hostname%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} +`, config, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMConnectionMonitor_basicAddressConfig(rInt int, location string) string { + config := testAccAzureRMConnectionMonitor_baseConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_connection_monitor" "test" { + name = "acctestcm-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_network_watcher.test.location}" + + source { + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + } + + destination { + address = "terraform.io" + port = 80 + } + + depends_on = ["azurerm_virtual_machine_extension.src"] +} +`, config, rInt) +} + +func testAccAzureRMConnectionMonitor_completeAddressConfig(rInt int, location, autoStart string) string { + config := testAccAzureRMConnectionMonitor_baseConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_connection_monitor" "test" { + name = "acctestcm-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_network_watcher.test.location}" + + auto_start = %s + interval_in_seconds = 30 + + source { + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + port = 20020 + } + + destination { + address = "terraform.io" + port = 443 + } + + tags { + env = "test" + } + + depends_on = ["azurerm_virtual_machine_extension.src"] +} +`, config, rInt, autoStart) +} + +func testAccAzureRMConnectionMonitor_basicVmConfig(rInt int, location string) string { + config := testAccAzureRMConnectionMonitor_baseWithDestConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_connection_monitor" "test" { + name = "acctestcm-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_network_watcher.test.location}" + + source { + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + } + + destination { + virtual_machine_id = "${azurerm_virtual_machine.dest.id}" + port = 80 + } + + depends_on = ["azurerm_virtual_machine_extension.src"] +} +`, config, rInt) +} + +func testAccAzureRMConnectionMonitor_completeVmConfig(rInt int, location, autoStart string) string { + config := testAccAzureRMConnectionMonitor_baseWithDestConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_connection_monitor" "test" { + name = "acctestcm-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_network_watcher.test.location}" + + auto_start = %s + interval_in_seconds = 30 + + source { + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + port = 20020 + } + + destination { + virtual_machine_id = "${azurerm_virtual_machine.dest.id}" + port = 443 + } + + tags { + env = "test" + } + + depends_on = ["azurerm_virtual_machine_extension.src"] +} +`, config, rInt, autoStart) +} + +func testAccAzureRMConnectionMonitor_missingDestinationConfig(rInt int, location string) string { + config := testAccAzureRMConnectionMonitor_baseConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_connection_monitor" "test" { + name = "acctestcm-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_network_watcher.test.location}" + + source { + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + } + + destination { + port = 80 + } + + depends_on = ["azurerm_virtual_machine_extension.src"] +} +`, config, rInt) +} + +func testAccAzureRMConnectionMonitor_conflictingDestinationsConfig(rInt int, location string) string { + config := testAccAzureRMConnectionMonitor_baseConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_connection_monitor" "test" { + name = "acctestcm-%d" + network_watcher_name = "${azurerm_network_watcher.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_network_watcher.test.location}" + + source { + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + } + + destination { + address = "terraform.io" + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + port = 80 + } + + depends_on = ["azurerm_virtual_machine_extension.src"] +} +`, config, rInt) +} diff --git a/azurerm/resource_arm_network_watcher_test.go b/azurerm/resource_arm_network_watcher_test.go index 39b823baaf0f..6952a9198783 100644 --- a/azurerm/resource_arm_network_watcher_test.go +++ b/azurerm/resource_arm_network_watcher_test.go @@ -26,6 +26,17 @@ func TestAccAzureRMNetworkWatcher(t *testing.T) { "DataSource": { "basic": testAccDataSourceAzureRMNetworkWatcher_basic, }, + "ConnectionMonitor": { + "addressBasic": testAccAzureRMConnectionMonitor_addressBasic, + "addressComplete": testAccAzureRMConnectionMonitor_addressComplete, + "addressUpdate": testAccAzureRMConnectionMonitor_addressUpdate, + "vmBasic": testAccAzureRMConnectionMonitor_vmBasic, + "vmComplete": testAccAzureRMConnectionMonitor_vmComplete, + "vmUpdate": testAccAzureRMConnectionMonitor_vmUpdate, + "destinationUpdate": testAccAzureRMConnectionMonitor_destinationUpdate, + "missingDestinationInvalid": testAccAzureRMConnectionMonitor_missingDestination, + "bothDestinationsInvalid": testAccAzureRMConnectionMonitor_conflictingDestinations, + }, "PacketCapture": { "localDisk": testAccAzureRMPacketCapture_localDisk, "storageAccount": testAccAzureRMPacketCapture_storageAccount, From 32ef3906d1382dd57961af3b868623e6f39ed3d7 Mon Sep 17 00:00:00 2001 From: Matthew Lewinski Date: Mon, 28 Jan 2019 15:00:28 -0600 Subject: [PATCH 4/6] Add documentation for Connection Monitor resource --- website/azurerm.erb | 4 + .../docs/r/connection_monitor.html.markdown | 170 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 website/docs/r/connection_monitor.html.markdown diff --git a/website/azurerm.erb b/website/azurerm.erb index b2a67048c4fb..d6526aed81fa 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -964,6 +964,10 @@ azurerm_application_security_group + > + azurerm_connection_monitor + + > azurerm_ddos_protection_plan diff --git a/website/docs/r/connection_monitor.html.markdown b/website/docs/r/connection_monitor.html.markdown new file mode 100644 index 000000000000..22f347bd4e9b --- /dev/null +++ b/website/docs/r/connection_monitor.html.markdown @@ -0,0 +1,170 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_connection_monitor" +sidebar_current: "docs-azurerm-resource-connection-monitor" +description: |- + Configures a Connection Monitor to monitor communication between a Virtual Machine and an endpoint using a Network Watcher. + +--- + +# azurerm_connection_monitor + +Configures a Connection Monitor to monitor communication between a Virtual Machine and an endpoint using a Network Watcher. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "connection-monitor-rg" + location = "West US" +} + +resource "azurerm_network_watcher" "test" { + name = "network-watcher" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_virtual_network" "test" { + name = "production-network" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "internal" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "cmtest-nic" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_virtual_machine" "test" { + name = "cmtest-vm" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_F2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "osdisk" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + + os_profile { + computer_name = "cmtest-vm" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } +} + +resource "azurerm_virtual_machine_extension" "test" { + name = "cmtest-vm-network-watcher" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_machine_name = "${azurerm_virtual_machine.test.name}" + publisher = "Microsoft.Azure.NetworkWatcher" + type = "NetworkWatcherAgentLinux" + type_handler_version = "1.4" + auto_upgrade_minor_version = true +} + +resource "azurerm_connection_monitor" "test" { + name = "cmtest-connectionmonitor" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_watcher_name = "${azurerm_network_watcher.test.name}" + + source { + virtual_machine_id = "${azurerm_virtual_machine.test.id}" + } + + destination { + address = "terraform.io" + port = 80 + } + + depends_on = ["azurerm_virtual_machine_extension.test"] +} +``` + +~> **NOTE:** This Resource requires that [the Network Watcher Agent Virtual Machine Extension](https://docs.microsoft.com/en-us/azure/network-watcher/connection-monitor) is installed on the Virtual Machine before monitoring can be started. The extension can be installed via [the `azurerm_virtual_machine_extension` resource](virtual_machine_extension.html). + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Connection Monitor. Changing this forces a new resource to be created. + +* `network_watcher_name` - (Required) The name of the Network Watcher. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group in which to create the Connection Monitor. Changing this forces a new resource to be created. + +* `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. + +* `auto_start` - (Optional) Specifies whether the connection monitor will start automatically once created. Defaults to `true`. Changing this forces a new resource to be created. + +* `interval_in_seconds` - (Optional) Monitoring interval in seconds. Defaults to `60`. + +* `source` - (Required) A `source` block as defined below. + +* `destination` - (Required) A `destination` block as defined below. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +--- + +A `source` block contains: + +* `virtual_machine_id` - (Required) The ID of the Virtual Machine to monitor connectivity from. + +* `port` - (Optional) The port on the Virtual Machine to monitor connectivity from. Defaults to `0` (Dynamic Port Assignment). + +A `destination` block contains: + +* `virtual_machine_id` - (Optional) The ID of the Virtual Machine to monitor connectivity to. + +* `address` - (Optional) IP address or domain name to monitor connectivity to. + +* `port` - (Required) The port on the destination to monitor connectivity to. + +~> **NOTE:** One of `virtual_machine_id` or `address` must be specified. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Connection Monitor ID. + +## Import + +Connection Monitors can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_connection_monitor.monitor1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/networkWatchers/watcher1/connectionMonitors/monitor1 +``` From afdfdcd9cbef3e1d507bf76f53f1dae9ec5926fc Mon Sep 17 00:00:00 2001 From: Matthew Lewinski Date: Fri, 8 Feb 2019 16:16:03 -0600 Subject: [PATCH 5/6] Add strict import behavior to Connection Monitor --- azurerm/resource_arm_connection_monitor.go | 14 +++++ .../resource_arm_connection_monitor_test.go | 56 +++++++++++++++++++ azurerm/resource_arm_network_watcher_test.go | 1 + 3 files changed, 71 insertions(+) diff --git a/azurerm/resource_arm_connection_monitor.go b/azurerm/resource_arm_connection_monitor.go index d5455aeaf549..ec06143ddb82 100644 --- a/azurerm/resource_arm_connection_monitor.go +++ b/azurerm/resource_arm_connection_monitor.go @@ -7,6 +7,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -128,6 +129,19 @@ func resourceArmConnectionMonitorCreateUpdate(d *schema.ResourceData, meta inter return err } + if requireResourcesToBeImported && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, watcherName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Connection Monitor %q (Watcher %q / Resource Group %q): %s", name, watcherName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_connection_monitor", *existing.ID) + } + } + tags := d.Get("tags").(map[string]interface{}) properties := network.ConnectionMonitor{ diff --git a/azurerm/resource_arm_connection_monitor_test.go b/azurerm/resource_arm_connection_monitor_test.go index 5d112aec3a87..fb13ce8836a5 100644 --- a/azurerm/resource_arm_connection_monitor_test.go +++ b/azurerm/resource_arm_connection_monitor_test.go @@ -41,6 +41,37 @@ func testAccAzureRMConnectionMonitor_addressBasic(t *testing.T) { }) } +func testAccAzureRMConnectionMonitor_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_connection_monitor.test" + + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMConnectionMonitorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMConnectionMonitor_basicAddressConfig(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMConnectionMonitorExists(resourceName), + ), + }, + { + Config: testAccAzureRMConnectionMonitor_requiresImportConfig(ri, location), + ExpectError: testRequiresImportError("azurerm_connection_monitor"), + }, + }, + }) + +} + func testAccAzureRMConnectionMonitor_addressComplete(t *testing.T) { resourceName := "azurerm_connection_monitor.test" @@ -639,3 +670,28 @@ resource "azurerm_connection_monitor" "test" { } `, config, rInt) } + +func testAccAzureRMConnectionMonitor_requiresImportConfig(rInt int, location string) string { + config := testAccAzureRMConnectionMonitor_basicAddressConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_connection_monitor" "import" { + name = "${azurerm_connection_monitor.test.name}" + network_watcher_name = "${azurerm_connection_monitor.test.network_watcher_name}" + resource_group_name = "${azurerm_connection_monitor.test.resource_group_name}" + location = "${azurerm_connection_monitor.test.location}" + + source { + virtual_machine_id = "${azurerm_virtual_machine.src.id}" + } + + destination { + address = "terraform.io" + port = 80 + } + + depends_on = ["azurerm_virtual_machine_extension.src"] +} +`, config) +} diff --git a/azurerm/resource_arm_network_watcher_test.go b/azurerm/resource_arm_network_watcher_test.go index 6952a9198783..8905ed2363f7 100644 --- a/azurerm/resource_arm_network_watcher_test.go +++ b/azurerm/resource_arm_network_watcher_test.go @@ -36,6 +36,7 @@ func TestAccAzureRMNetworkWatcher(t *testing.T) { "destinationUpdate": testAccAzureRMConnectionMonitor_destinationUpdate, "missingDestinationInvalid": testAccAzureRMConnectionMonitor_missingDestination, "bothDestinationsInvalid": testAccAzureRMConnectionMonitor_conflictingDestinations, + "requiresImport": testAccAzureRMConnectionMonitor_requiresImport, }, "PacketCapture": { "localDisk": testAccAzureRMPacketCapture_localDisk, From 686d6f4f2b924e88f30fe6407a45ed636c5bf692 Mon Sep 17 00:00:00 2001 From: Matthew Lewinski Date: Tue, 12 Feb 2019 11:49:44 -0600 Subject: [PATCH 6/6] Incorporate validation improvements from PR feedback --- azurerm/data_source_network_watcher.go | 6 ++++-- azurerm/resource_arm_connection_monitor.go | 24 +++++++++------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/azurerm/data_source_network_watcher.go b/azurerm/data_source_network_watcher.go index 1aaa93d1d1e0..5cfb25b36686 100644 --- a/azurerm/data_source_network_watcher.go +++ b/azurerm/data_source_network_watcher.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -13,8 +14,9 @@ func dataSourceArmNetworkWatcher() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, }, "resource_group_name": resourceGroupNameForDataSourceSchema(), diff --git a/azurerm/resource_arm_connection_monitor.go b/azurerm/resource_arm_connection_monitor.go index ec06143ddb82..a3688ea83930 100644 --- a/azurerm/resource_arm_connection_monitor.go +++ b/azurerm/resource_arm_connection_monitor.go @@ -27,17 +27,19 @@ func resourceArmConnectionMonitor() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, }, "resource_group_name": resourceGroupNameSchema(), "network_watcher_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, }, "location": locationSchema(), @@ -71,7 +73,7 @@ func resourceArmConnectionMonitor() *schema.Resource { Type: schema.TypeInt, Optional: true, Default: 0, - ValidateFunc: validate.PortNumber, + ValidateFunc: validate.PortNumberOrZero, }, }, }, @@ -207,7 +209,7 @@ func resourceArmConnectionMonitorRead(d *schema.ResourceData, meta interface{}) if props := resp.ConnectionMonitorResultProperties; props != nil { d.Set("auto_start", props.AutoStart) - d.Set("interval_in_seconds", int(*props.MonitoringIntervalInSeconds)) + d.Set("interval_in_seconds", props.MonitoringIntervalInSeconds) source := flattenArmConnectionMonitorSource(props.Source) if err := d.Set("source", source); err != nil { @@ -270,9 +272,6 @@ func flattenArmConnectionMonitorSource(input *network.ConnectionMonitorSource) [ func expandArmConnectionMonitorSource(d *schema.ResourceData) (*network.ConnectionMonitorSource, error) { sources := d.Get("source").([]interface{}) - if len(sources) == 0 { - return nil, fmt.Errorf("Error expanding `source`: not found") - } source := sources[0].(map[string]interface{}) monitorSource := network.ConnectionMonitorSource{} @@ -311,9 +310,6 @@ func flattenArmConnectionMonitorDestination(input *network.ConnectionMonitorDest func expandArmConnectionMonitorDestination(d *schema.ResourceData) (*network.ConnectionMonitorDestination, error) { dests := d.Get("destination").([]interface{}) - if len(dests) == 0 { - return nil, fmt.Errorf("Error expanding `destination`: not found") - } dest := dests[0].(map[string]interface{}) monitorDest := network.ConnectionMonitorDestination{}