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/data_source_network_watcher.go b/azurerm/data_source_network_watcher.go new file mode 100644 index 000000000000..5cfb25b36686 --- /dev/null +++ b/azurerm/data_source_network_watcher.go @@ -0,0 +1,54 @@ +package azurerm + +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" +) + +func dataSourceArmNetworkWatcher() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmNetworkWatcherRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "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..e27409b8f090 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(), @@ -188,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..a3688ea83930 --- /dev/null +++ b/azurerm/resource_arm_connection_monitor.go @@ -0,0 +1,332 @@ +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/tf" + "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, + ValidateFunc: validate.NoEmptyStrings, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "network_watcher_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "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.PortNumberOrZero, + }, + }, + }, + }, + + "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 + } + + 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{ + 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", 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{}) + 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{}) + 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..fb13ce8836a5 --- /dev/null +++ b/azurerm/resource_arm_connection_monitor_test.go @@ -0,0 +1,697 @@ +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_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" + + 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) +} + +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 144d714f72ee..8905ed2363f7 100644 --- a/azurerm/resource_arm_network_watcher_test.go +++ b/azurerm/resource_arm_network_watcher_test.go @@ -23,6 +23,21 @@ func TestAccAzureRMNetworkWatcher(t *testing.T) { "update": testAccAzureRMNetworkWatcher_update, "disappears": testAccAzureRMNetworkWatcher_disappears, }, + "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, + "requiresImport": testAccAzureRMConnectionMonitor_requiresImport, + }, "PacketCapture": { "localDisk": testAccAzureRMPacketCapture_localDisk, "storageAccount": testAccAzureRMPacketCapture_storageAccount, diff --git a/website/azurerm.erb b/website/azurerm.erb index 3d1150a68e3c..d6526aed81fa 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -199,6 +199,10 @@ azurerm_network_security_group +