diff --git a/azurerm/provider.go b/azurerm/provider.go index d6b9d8109d4a..29acc1ae168d 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -243,6 +243,7 @@ func Provider() terraform.ResourceProvider { "azurerm_lb": resourceArmLoadBalancer(), "azurerm_local_network_gateway": resourceArmLocalNetworkGateway(), "azurerm_log_analytics_solution": resourceArmLogAnalyticsSolution(), + "azurerm_log_analytics_linked_service": resourceArmLogAnalyticsLinkedService(), "azurerm_log_analytics_workspace_linked_service": resourceArmLogAnalyticsWorkspaceLinkedService(), "azurerm_log_analytics_workspace": resourceArmLogAnalyticsWorkspace(), "azurerm_logic_app_action_custom": resourceArmLogicAppActionCustom(), @@ -257,6 +258,7 @@ func Provider() terraform.ResourceProvider { "azurerm_mariadb_database": resourceArmMariaDbDatabase(), "azurerm_mariadb_server": resourceArmMariaDbServer(), "azurerm_metric_alertrule": resourceArmMetricAlertRule(), + "azurerm_monitor_autoscale_setting": resourceArmMonitorAutoScaleSetting(), "azurerm_monitor_action_group": resourceArmMonitorActionGroup(), "azurerm_monitor_activity_log_alert": resourceArmMonitorActivityLogAlert(), "azurerm_monitor_diagnostic_setting": resourceArmMonitorDiagnosticSetting(), diff --git a/azurerm/resource_arm_application_gateway.go b/azurerm/resource_arm_application_gateway.go index 8a85b3323d07..cc429cd17587 100644 --- a/azurerm/resource_arm_application_gateway.go +++ b/azurerm/resource_arm_application_gateway.go @@ -50,20 +50,20 @@ func resourceArmApplicationGateway() *schema.Resource { Required: true, }, - // TODO: ditch the suffix `_list` in the future - "fqdn_list": { + "fqdns": { Type: schema.TypeList, Optional: true, + Computed: true, MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, }, }, - // TODO: ditch the suffix `_list` in the future - "ip_address_list": { + "ip_addresses": { Type: schema.TypeList, Optional: true, + Computed: true, MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, @@ -71,6 +71,31 @@ func resourceArmApplicationGateway() *schema.Resource { }, }, + // TODO: remove in 2.0 + "fqdn_list": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "`fqdn_list` has been deprecated in favour of the `fqdns` field", + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + // TODO: remove in 2.0 + "ip_address_list": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Deprecated: "`ip_address_list` has been deprecated in favour of the `ip_addresses` field", + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.IPv4Address, + }, + }, + "id": { Type: schema.TypeString, Computed: true, @@ -1050,12 +1075,24 @@ func expandApplicationGatewayBackendAddressPools(d *schema.ResourceData) *[]netw v := raw.(map[string]interface{}) backendAddresses := make([]network.ApplicationGatewayBackendAddress, 0) - for _, ip := range v["ip_address_list"].([]interface{}) { + for _, ip := range v["fqdns"].([]interface{}) { + backendAddresses = append(backendAddresses, network.ApplicationGatewayBackendAddress{ + Fqdn: utils.String(ip.(string)), + }) + } + for _, ip := range v["ip_addresses"].([]interface{}) { backendAddresses = append(backendAddresses, network.ApplicationGatewayBackendAddress{ IPAddress: utils.String(ip.(string)), }) } + // TODO: remove in 2.0 + for _, ip := range v["ip_address_list"].([]interface{}) { + backendAddresses = append(backendAddresses, network.ApplicationGatewayBackendAddress{ + IPAddress: utils.String(ip.(string)), + }) + } + // TODO: remove in 2.0 for _, ip := range v["fqdn_list"].([]interface{}) { backendAddresses = append(backendAddresses, network.ApplicationGatewayBackendAddress{ Fqdn: utils.String(ip.(string)), @@ -1099,6 +1136,10 @@ func flattenApplicationGatewayBackendAddressPools(input *[]network.ApplicationGa } output := map[string]interface{}{ + "fqdns": fqdnList, + "ip_addresses": ipAddressList, + + // TODO: deprecated - remove in 2.0 "ip_address_list": ipAddressList, "fqdn_list": fqdnList, } diff --git a/azurerm/resource_arm_autoscale_setting.go b/azurerm/resource_arm_autoscale_setting.go index 80876ba0f5a0..3ae1933f8c9b 100644 --- a/azurerm/resource_arm_autoscale_setting.go +++ b/azurerm/resource_arm_autoscale_setting.go @@ -18,6 +18,13 @@ import ( func resourceArmAutoScaleSetting() *schema.Resource { return &schema.Resource{ + DeprecationMessage: `The 'azurerm_autoscale_setting' resource is deprecated in favour of the renamed version 'azurerm_monitor_autoscale_setting'. + +Information on migrating to the renamed resource can be found here: https://terraform.io/docs/providers/azurerm/guides/migrating-between-renamed-resources.html + +As such the existing 'azurerm_autoscale_setting' resource is deprecated and will be removed in the next major version of the AzureRM Provider (2.0). +`, + Create: resourceArmAutoScaleSettingCreateUpdate, Read: resourceArmAutoScaleSettingRead, Update: resourceArmAutoScaleSettingCreateUpdate, diff --git a/azurerm/resource_arm_cdn_profile.go b/azurerm/resource_arm_cdn_profile.go index 7885fe26ec50..811a4cda9d8b 100644 --- a/azurerm/resource_arm_cdn_profile.go +++ b/azurerm/resource_arm_cdn_profile.go @@ -41,8 +41,7 @@ func resourceArmCdnProfile() *schema.Resource { string(cdn.StandardAkamai), string(cdn.StandardChinaCdn), string(cdn.StandardVerizon), - // TODO: replace this with an SDK constant once available - "Standard_Microsoft", + string(cdn.StandardMicrosoft), string(cdn.PremiumVerizon), }, true), DiffSuppressFunc: ignoreCaseDiffSuppressFunc, diff --git a/azurerm/resource_arm_log_analytics_linked_service.go b/azurerm/resource_arm_log_analytics_linked_service.go new file mode 100644 index 000000000000..33ab8baf709e --- /dev/null +++ b/azurerm/resource_arm_log_analytics_linked_service.go @@ -0,0 +1,232 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/preview/operationalinsights/mgmt/2015-11-01-preview/operationalinsights" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +/* +TODO: refactor this: + + * resource_group_name/workspace_name can become case-sensitive +*/ +func resourceArmLogAnalyticsLinkedService() *schema.Resource { + return &schema.Resource{ + Create: resourceArmLogAnalyticsLinkedServiceCreateUpdate, + Read: resourceArmLogAnalyticsLinkedServiceRead, + Update: resourceArmLogAnalyticsLinkedServiceCreateUpdate, + Delete: resourceArmLogAnalyticsLinkedServiceDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "resource_group_name": resourceGroupNameDiffSuppressSchema(), + + "workspace_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validateAzureRmLogAnalyticsWorkspaceName, + }, + + "linked_service_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "automation", + ValidateFunc: validation.StringInSlice([]string{ + "automation", + }, false), + }, + + "resource_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "linked_service_properties": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "resource_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + ConflictsWith: []string{ + // this is the top-level field, not this one + "resource_id", + }, + }, + }, + }, + }, + + // Exported properties + "name": { + Type: schema.TypeString, + Computed: true, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmLogAnalyticsLinkedServiceCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).linkedServicesClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for AzureRM Log Analytics Linked Services creation.") + + resGroup := d.Get("resource_group_name").(string) + workspaceName := d.Get("workspace_name").(string) + lsName := d.Get("linked_service_name").(string) + + if requireResourcesToBeImported && d.IsNewResource() { + existing, err := client.Get(ctx, resGroup, workspaceName, lsName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Linked Service %q (Workspace %q / Resource Group %q): %s", lsName, workspaceName, resGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_log_analytics_linked_service", *existing.ID) + } + } + + resourceId := d.Get("resource_id").(string) + if resourceId == "" { + props := d.Get("linked_service_properties").(map[string]interface{}) + resourceId = props["resource_id"].(string) + if resourceId == "" { + return fmt.Errorf("A `resource_id` must be specified either using the `resource_id` field at the top level or within the `linked_service_properties` block") + } + } + tags := d.Get("tags").(map[string]interface{}) + + parameters := operationalinsights.LinkedService{ + LinkedServiceProperties: &operationalinsights.LinkedServiceProperties{ + ResourceID: utils.String(resourceId), + }, + Tags: expandTags(tags), + } + + if _, err := client.CreateOrUpdate(ctx, resGroup, workspaceName, lsName, parameters); err != nil { + return fmt.Errorf("Error creating Linked Service %q (Workspace %q / Resource Group %q): %+v", lsName, workspaceName, resGroup, err) + } + + read, err := client.Get(ctx, resGroup, workspaceName, lsName) + if err != nil { + return fmt.Errorf("Error retrieving Linked Service %q (Worksppce %q / Resource Group %q): %+v", lsName, workspaceName, resGroup, err) + } + if read.ID == nil { + return fmt.Errorf("Cannot read Linked Service %q (Workspace %q / Resource Group %q) ID", lsName, workspaceName, resGroup) + } + + d.SetId(*read.ID) + + return resourceArmLogAnalyticsLinkedServiceRead(d, meta) +} + +func resourceArmLogAnalyticsLinkedServiceRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).linkedServicesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + workspaceName := id.Path["workspaces"] + lsName := id.Path["linkedServices"] + + resp, err := client.Get(ctx, resGroup, workspaceName, lsName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on AzureRM Log Analytics Linked Service '%s': %+v", lsName, err) + } + if resp.ID == nil { + d.SetId("") + return nil + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resGroup) + d.Set("workspace_name", workspaceName) + d.Set("linked_service_name", lsName) + + if props := resp.LinkedServiceProperties; props != nil { + d.Set("resource_id", props.ResourceID) + } + + linkedServiceProperties := flattenLogAnalyticsLinkedServiceProperties(resp.LinkedServiceProperties) + if err := d.Set("linked_service_properties", linkedServiceProperties); err != nil { + return fmt.Errorf("Error setting `linked_service_properties`: %+v", err) + } + + flattenAndSetTags(d, resp.Tags) + return nil +} + +func resourceArmLogAnalyticsLinkedServiceDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).linkedServicesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resGroup := id.ResourceGroup + workspaceName := id.Path["workspaces"] + lsName := id.Path["linkedServices"] + + resp, err := client.Delete(ctx, resGroup, workspaceName, lsName) + if err != nil { + if utils.ResponseWasNotFound(resp) { + return nil + } + + return fmt.Errorf("Error deleting Linked Service %q (Workspace %q / Resource Group %q): %+v", lsName, workspaceName, resGroup, err) + } + + return nil +} + +func flattenLogAnalyticsLinkedServiceProperties(input *operationalinsights.LinkedServiceProperties) interface{} { + if input == nil { + return []interface{}{} + } + + properties := make(map[string]interface{}) + + // resource id linked service + if resourceID := input.ResourceID; resourceID != nil { + properties["resource_id"] = interface{}(*resourceID) + } + + return interface{}(properties) +} diff --git a/azurerm/resource_arm_log_analytics_linked_service_test.go b/azurerm/resource_arm_log_analytics_linked_service_test.go new file mode 100644 index 000000000000..eda4a56e5be7 --- /dev/null +++ b/azurerm/resource_arm_log_analytics_linked_service_test.go @@ -0,0 +1,227 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMLogAnalyticsLinkedService_basic(t *testing.T) { + resourceName := "azurerm_log_analytics_linked_service.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLogAnalyticsLinkedServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLogAnalyticsLinkedService_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLogAnalyticsLinkedServiceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestlaw-%d/Automation", ri)), + resource.TestCheckResourceAttr(resourceName, "workspace_name", fmt.Sprintf("acctestlaw-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "linked_service_name", "automation"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMLogAnalyticsLinkedService_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_log_analytics_linked_service.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLogAnalyticsLinkedServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLogAnalyticsLinkedService_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLogAnalyticsLinkedServiceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("acctestlaw-%d/Automation", ri)), + resource.TestCheckResourceAttr(resourceName, "workspace_name", fmt.Sprintf("acctestlaw-%d", ri)), + resource.TestCheckResourceAttr(resourceName, "linked_service_name", "automation"), + ), + }, + { + Config: testAccAzureRMLogAnalyticsLinkedService_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_log_analytics_linked_service"), + }, + }, + }) +} + +func TestAccAzureRMLogAnalyticsLinkedService_complete(t *testing.T) { + resourceName := "azurerm_log_analytics_linked_service.test" + ri := tf.AccRandTimeInt() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMLogAnalyticsLinkedServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMLogAnalyticsLinkedService_complete(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMLogAnalyticsLinkedServiceExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "linked_service_name", "automation"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMLogAnalyticsLinkedServiceDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).linkedServicesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_log_analytics_linked_service" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + workspaceName := rs.Primary.Attributes["workspace_name"] + lsName := rs.Primary.Attributes["linked_service_name"] + + resp, err := conn.Get(ctx, resourceGroup, workspaceName, lsName) + if err != nil { + return nil + } + if resp.ID == nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Log Analytics Linked Service still exists:\n%#v", resp) + } + } + + return nil +} + +func testCheckAzureRMLogAnalyticsLinkedServiceExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + workspaceName := rs.Primary.Attributes["workspace_name"] + lsName := rs.Primary.Attributes["linked_service_name"] + name := rs.Primary.Attributes["name"] + + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Log Analytics Linked Service: '%s'", name) + } + + conn := testAccProvider.Meta().(*ArmClient).linkedServicesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := conn.Get(ctx, resourceGroup, workspaceName, lsName) + if err != nil { + return fmt.Errorf("Bad: Get on Log Analytics Linked Service Client: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Log Analytics Linked Service '%s' (resource group: '%s') does not exist", name, resourceGroup) + } + + return nil + } +} + +func testAccAzureRMLogAnalyticsLinkedService_basic(rInt int, location string) string { + template := testAccAzureRMLogAnalyticsLinkedService_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_log_analytics_linked_service" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + workspace_name = "${azurerm_log_analytics_workspace.test.name}" + resource_id = "${azurerm_automation_account.test.id}"} +`, template) +} + +func testAccAzureRMLogAnalyticsLinkedService_requiresImport(rInt int, location string) string { + template := testAccAzureRMLogAnalyticsLinkedService_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_log_analytics_linked_service" "import" { + resource_group_name = "${azurerm_log_analytics_linked_service.test.resource_group_name}" + workspace_name = "${azurerm_log_analytics_linked_service.test.workspace_name}" + resource_id = "${azurerm_log_analytics_linked_service.test.resource_id}" +} +`, template) +} + +func testAccAzureRMLogAnalyticsLinkedService_complete(rInt int, location string) string { + template := testAccAzureRMLogAnalyticsLinkedService_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_log_analytics_linked_service" "test" { + resource_group_name = "${azurerm_resource_group.test.name}" + workspace_name = "${azurerm_log_analytics_workspace.test.name}" + linked_service_name = "automation" + resource_id = "${azurerm_automation_account.test.id}"} +} +`, template) +} + +func testAccAzureRMLogAnalyticsLinkedService_template(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_automation_account" "test" { + name = "acctestAutomation-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "Basic" + } + + tags { + Environment = "Test" + } +} + +resource "azurerm_log_analytics_workspace" "test" { + name = "acctestLAW-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "PerGB2018" + retention_in_days = 30 +} +`, rInt, location, rInt, rInt) +} diff --git a/azurerm/resource_arm_log_analytics_workspace_linked_service.go b/azurerm/resource_arm_log_analytics_workspace_linked_service.go index 544a708516a3..3424d0082fc4 100644 --- a/azurerm/resource_arm_log_analytics_workspace_linked_service.go +++ b/azurerm/resource_arm_log_analytics_workspace_linked_service.go @@ -22,6 +22,13 @@ TODO: refactor this: */ func resourceArmLogAnalyticsWorkspaceLinkedService() *schema.Resource { return &schema.Resource{ + DeprecationMessage: `The 'azurerm_log_analytics_workspace_linked_service' resource is deprecated in favour of the renamed version 'azurerm_log_analytics_linked_service'. + +Information on migrating to the renamed resource can be found here: https://terraform.io/docs/providers/azurerm/guides/migrating-between-renamed-resources.html + +As such the existing 'azurerm_log_analytics_workspace_linked_service' resource is deprecated and will be removed in the next major version of the AzureRM Provider (2.0). +`, + Create: resourceArmLogAnalyticsWorkspaceLinkedServiceCreateUpdate, Read: resourceArmLogAnalyticsWorkspaceLinkedServiceRead, Update: resourceArmLogAnalyticsWorkspaceLinkedServiceCreateUpdate, @@ -52,9 +59,18 @@ func resourceArmLogAnalyticsWorkspaceLinkedService() *schema.Resource { }, false), }, + "resource_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + "linked_service_properties": { Type: schema.TypeMap, - Required: true, + Optional: true, + Computed: true, ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -63,6 +79,10 @@ func resourceArmLogAnalyticsWorkspaceLinkedService() *schema.Resource { Required: true, ForceNew: true, ValidateFunc: azure.ValidateResourceID, + ConflictsWith: []string{ + // this is the top-level field, not this one + "resource_id", + }, }, }, }, @@ -102,16 +122,21 @@ func resourceArmLogAnalyticsWorkspaceLinkedServiceCreateUpdate(d *schema.Resourc } } - props := d.Get("linked_service_properties").(map[string]interface{}) - resourceID := props["resource_id"].(string) - + resourceId := d.Get("resource_id").(string) + if resourceId == "" { + props := d.Get("linked_service_properties").(map[string]interface{}) + resourceId = props["resource_id"].(string) + if resourceId == "" { + return fmt.Errorf("A `resource_id` must be specified either using the `resource_id` field at the top level or within the `linked_service_properties` block") + } + } tags := d.Get("tags").(map[string]interface{}) parameters := operationalinsights.LinkedService{ - Tags: expandTags(tags), LinkedServiceProperties: &operationalinsights.LinkedServiceProperties{ - ResourceID: &resourceID, + ResourceID: utils.String(resourceId), }, + Tags: expandTags(tags), } if _, err := client.CreateOrUpdate(ctx, resGroup, workspaceName, lsName, parameters); err != nil { @@ -162,6 +187,10 @@ func resourceArmLogAnalyticsWorkspaceLinkedServiceRead(d *schema.ResourceData, m d.Set("workspace_name", workspaceName) d.Set("linked_service_name", lsName) + if props := resp.LinkedServiceProperties; props != nil { + d.Set("resource_id", props.ResourceID) + } + linkedServiceProperties := flattenLogAnalyticsWorkspaceLinkedServiceProperties(resp.LinkedServiceProperties) if err := d.Set("linked_service_properties", linkedServiceProperties); err != nil { return fmt.Errorf("Error setting `linked_service_properties`: %+v", err) diff --git a/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go b/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go index 2787d4720898..3c2470b69498 100644 --- a/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go +++ b/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go @@ -164,11 +164,7 @@ func testAccAzureRMLogAnalyticsWorkspaceLinkedService_basic(rInt int, location s resource "azurerm_log_analytics_workspace_linked_service" "test" { resource_group_name = "${azurerm_resource_group.test.name}" workspace_name = "${azurerm_log_analytics_workspace.test.name}" - - linked_service_properties { - resource_id = "${azurerm_automation_account.test.id}" - } -} + resource_id = "${azurerm_automation_account.test.id}"} `, template) } @@ -180,10 +176,7 @@ func testAccAzureRMLogAnalyticsWorkspaceLinkedService_requiresImport(rInt int, l resource "azurerm_log_analytics_workspace_linked_service" "import" { resource_group_name = "${azurerm_log_analytics_workspace_linked_service.test.resource_group_name}" workspace_name = "${azurerm_log_analytics_workspace_linked_service.test.workspace_name}" - - linked_service_properties { - resource_id = "${azurerm_automation_account.test.id}" - } + resource_id = "${azurerm_log_analytics_workspace_linked_service.test.resource_id}" } `, template) } @@ -197,10 +190,7 @@ resource "azurerm_log_analytics_workspace_linked_service" "test" { resource_group_name = "${azurerm_resource_group.test.name}" workspace_name = "${azurerm_log_analytics_workspace.test.name}" linked_service_name = "automation" - - linked_service_properties { - resource_id = "${azurerm_automation_account.test.id}" - } + resource_id = "${azurerm_automation_account.test.id}"} } `, template) } diff --git a/azurerm/resource_arm_metric_alertrule.go b/azurerm/resource_arm_metric_alertrule.go index ca70f8ae9c84..884022d624b4 100644 --- a/azurerm/resource_arm_metric_alertrule.go +++ b/azurerm/resource_arm_metric_alertrule.go @@ -22,10 +22,11 @@ func resourceArmMetricAlertRule() *schema.Resource { Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, + DeprecationMessage: `The 'azurerm_metric_alertrule' resource is deprecated in favour of the renamed version 'azurerm_monitor_metric_alertrule'. - DeprecationMessage: `This resource has been renamed to 'azurerm_monitor_metric_alertrule' to match the other monitor resources and will be removed in v2.0. +Information on migrating to the renamed resource can be found here: https://terraform.io/docs/providers/azurerm/guides/migrating-between-renamed-resources.html -Functionally these resources are the same and to upgrade all that will need to be done is replace 'azurerm_metric_alertrule' with 'azurerm_monitor_metric_alertrule' +As such the existing 'azurerm_metric_alertrule' resource is deprecated and will be removed in the next major version of the AzureRM Provider (2.0). `, Schema: map[string]*schema.Schema{ @@ -443,3 +444,14 @@ func validateMetricAlertRuleTags(v interface{}, f string) (warnings []string, er return warnings, errors } + +func resourceGroupAndAlertRuleNameFromId(alertRuleId string) (string, string, error) { + id, err := parseAzureResourceID(alertRuleId) + if err != nil { + return "", "", err + } + name := id.Path["alertrules"] + resourceGroup := id.ResourceGroup + + return resourceGroup, name, nil +} diff --git a/azurerm/resource_arm_monitor_autoscale_setting.go b/azurerm/resource_arm_monitor_autoscale_setting.go new file mode 100644 index 000000000000..03d35eb023ac --- /dev/null +++ b/azurerm/resource_arm_monitor_autoscale_setting.go @@ -0,0 +1,1050 @@ +package azurerm + +import ( + "fmt" + "log" + "strconv" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2018-03-01/insights" + "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "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" +) + +func resourceArmMonitorAutoScaleSetting() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMonitorAutoScaleSettingCreateUpdate, + Read: resourceArmMonitorAutoScaleSettingRead, + Update: resourceArmMonitorAutoScaleSettingCreateUpdate, + Delete: resourceArmMonitorAutoScaleSettingDelete, + 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(), + + "location": locationSchema(), + + "target_resource_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "profile": { + Type: schema.TypeList, + Required: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "capacity": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "minimum": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 40), + }, + "maximum": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 40), + }, + "default": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 40), + }, + }, + }, + }, + "rule": { + Type: schema.TypeList, + Optional: true, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "metric_trigger": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "metric_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "metric_resource_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "time_grain": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateIso8601Duration(), + }, + "statistic": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.MetricStatisticTypeAverage), + string(insights.MetricStatisticTypeMax), + string(insights.MetricStatisticTypeMin), + string(insights.MetricStatisticTypeSum), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + "time_window": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateIso8601Duration(), + }, + "time_aggregation": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.TimeAggregationTypeAverage), + string(insights.TimeAggregationTypeCount), + string(insights.TimeAggregationTypeMaximum), + string(insights.TimeAggregationTypeMinimum), + string(insights.TimeAggregationTypeTotal), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + "operator": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.Equals), + string(insights.GreaterThan), + string(insights.GreaterThanOrEqual), + string(insights.LessThan), + string(insights.LessThanOrEqual), + string(insights.NotEquals), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + "threshold": { + Type: schema.TypeFloat, + Required: true, + }, + }, + }, + }, + "scale_action": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "direction": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.ScaleDirectionDecrease), + string(insights.ScaleDirectionIncrease), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(insights.ChangeCount), + string(insights.ExactCount), + string(insights.PercentChangeCount), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + "value": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "cooldown": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateIso8601Duration(), + }, + }, + }, + }, + }, + }, + }, + "fixed_date": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "timezone": { + Type: schema.TypeString, + Optional: true, + Default: "UTC", + ValidateFunc: validateMonitorAutoScaleSettingsTimeZone(), + }, + "start": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRFC3339Date, + }, + "end": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateRFC3339Date, + }, + }, + }, + }, + "recurrence": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "timezone": { + Type: schema.TypeString, + Optional: true, + Default: "UTC", + ValidateFunc: validateMonitorAutoScaleSettingsTimeZone(), + }, + "days": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, + }, + }, + "hours": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 23), + }, + }, + "minutes": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeInt, + ValidateFunc: validation.IntBetween(0, 59), + }, + }, + }, + }, + }, + }, + }, + }, + + "notification": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "email": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "send_to_subscription_administrator": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "send_to_subscription_co_administrator": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "custom_emails": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "webhook": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "service_uri": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + "properties": { + Type: schema.TypeMap, + Optional: true, + }, + }, + }, + }, + }, + }, + }, + + "tags": tagsSchema(), + }, + } +} + +func resourceArmMonitorAutoScaleSettingCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).autoscaleSettingsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + location := azureRMNormalizeLocation(d.Get("location").(string)) + enabled := d.Get("enabled").(bool) + targetResourceId := d.Get("target_resource_id").(string) + + notificationsRaw := d.Get("notification").([]interface{}) + notifications := expandAzureRmMonitorAutoScaleSettingNotifications(notificationsRaw) + + profilesRaw := d.Get("profile").([]interface{}) + profiles, err := expandAzureRmMonitorAutoScaleSettingProfile(profilesRaw) + if err != nil { + return fmt.Errorf("Error expanding `profile`: %+v", err) + } + + tags := d.Get("tags").(map[string]interface{}) + expandedTags := expandTags(tags) + + parameters := insights.AutoscaleSettingResource{ + Location: utils.String(location), + AutoscaleSetting: &insights.AutoscaleSetting{ + Enabled: &enabled, + Profiles: profiles, + Notifications: notifications, + TargetResourceURI: &targetResourceId, + }, + Tags: expandedTags, + } + + if _, err = client.CreateOrUpdate(ctx, resourceGroup, name, parameters); err != nil { + return fmt.Errorf("Error creating AutoScale Setting %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, name) + if err != nil { + return fmt.Errorf("Error retrieving AutoScale Setting %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if read.ID == nil { + return fmt.Errorf("AutoScale Setting %q (Resource Group %q) has no ID", name, resourceGroup) + } + + d.SetId(*read.ID) + + return resourceArmMonitorAutoScaleSettingRead(d, meta) +} + +func resourceArmMonitorAutoScaleSettingRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).autoscaleSettingsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["autoscalesettings"] + + resp, err := client.Get(ctx, resourceGroup, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] AutoScale Setting %q (Resource Group %q) was not found - removing from state!", name, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading AutoScale Setting %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + if location := resp.Location; location != nil { + d.Set("location", azureRMNormalizeLocation(*location)) + } + + d.Set("enabled", resp.Enabled) + d.Set("target_resource_id", resp.TargetResourceURI) + + profile, err := flattenAzureRmMonitorAutoScaleSettingProfile(resp.Profiles) + if err != nil { + return fmt.Errorf("Error flattening `profile` of Autoscale Setting %q (Resource Group %q): %+v", name, resourceGroup, err) + } + if err = d.Set("profile", profile); err != nil { + return fmt.Errorf("Error setting `profile` of Autoscale Setting %q (Resource Group %q): %+v", name, resourceGroup, err) + } + + notifications := flattenAzureRmMonitorAutoScaleSettingNotification(resp.Notifications) + if err = d.Set("notification", notifications); err != nil { + return fmt.Errorf("Error setting `notification` of Autoscale Setting %q (resource group %q): %+v", name, resourceGroup, err) + } + + // Return a new tag map filtered by the specified tag names. + tagMap := filterTags(resp.Tags, "$type") + flattenAndSetTags(d, tagMap) + + return nil +} + +func resourceArmMonitorAutoScaleSettingDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).autoscaleSettingsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + name := id.Path["autoscalesettings"] + + resp, err := client.Delete(ctx, resourceGroup, name) + if err != nil { + if !response.WasNotFound(resp.Response) { + return fmt.Errorf("Error deleting AutoScale Setting %q (Resource Group %q): %+v", name, resourceGroup, err) + } + } + + return nil +} + +func expandAzureRmMonitorAutoScaleSettingProfile(input []interface{}) (*[]insights.AutoscaleProfile, error) { + results := make([]insights.AutoscaleProfile, 0) + + for _, v := range input { + raw := v.(map[string]interface{}) + + name := raw["name"].(string) + + // this is Required, so we don't need to check for optionals here + capacitiesRaw := raw["capacity"].([]interface{}) + capacityRaw := capacitiesRaw[0].(map[string]interface{}) + capacity := insights.ScaleCapacity{ + Minimum: utils.String(strconv.Itoa(capacityRaw["minimum"].(int))), + Maximum: utils.String(strconv.Itoa(capacityRaw["maximum"].(int))), + Default: utils.String(strconv.Itoa(capacityRaw["default"].(int))), + } + + recurrencesRaw := raw["recurrence"].([]interface{}) + recurrence := expandAzureRmMonitorAutoScaleSettingRecurrence(recurrencesRaw) + + rulesRaw := raw["rule"].([]interface{}) + rules := expandAzureRmMonitorAutoScaleSettingRule(rulesRaw) + + fixedDatesRaw := raw["fixed_date"].([]interface{}) + fixedDate, err := expandAzureRmMonitorAutoScaleSettingFixedDate(fixedDatesRaw) + if err != nil { + return nil, fmt.Errorf("Error expanding `fixed_date`: %+v", err) + } + + result := insights.AutoscaleProfile{ + Name: utils.String(name), + Capacity: &capacity, + FixedDate: fixedDate, + Recurrence: recurrence, + Rules: rules, + } + results = append(results, result) + } + + return &results, nil +} + +func expandAzureRmMonitorAutoScaleSettingRule(input []interface{}) *[]insights.ScaleRule { + rules := make([]insights.ScaleRule, 0) + + for _, v := range input { + ruleRaw := v.(map[string]interface{}) + + triggersRaw := ruleRaw["metric_trigger"].([]interface{}) + triggerRaw := triggersRaw[0].(map[string]interface{}) + metricTrigger := insights.MetricTrigger{ + MetricName: utils.String(triggerRaw["metric_name"].(string)), + MetricResourceURI: utils.String(triggerRaw["metric_resource_id"].(string)), + TimeGrain: utils.String(triggerRaw["time_grain"].(string)), + Statistic: insights.MetricStatisticType(triggerRaw["statistic"].(string)), + TimeWindow: utils.String(triggerRaw["time_window"].(string)), + TimeAggregation: insights.TimeAggregationType(triggerRaw["time_aggregation"].(string)), + Operator: insights.ComparisonOperationType(triggerRaw["operator"].(string)), + Threshold: utils.Float(triggerRaw["threshold"].(float64)), + } + + actionsRaw := ruleRaw["scale_action"].([]interface{}) + actionRaw := actionsRaw[0].(map[string]interface{}) + scaleAction := insights.ScaleAction{ + Direction: insights.ScaleDirection(actionRaw["direction"].(string)), + Type: insights.ScaleType(actionRaw["type"].(string)), + Value: utils.String(strconv.Itoa(actionRaw["value"].(int))), + Cooldown: utils.String(actionRaw["cooldown"].(string)), + } + + rule := insights.ScaleRule{ + MetricTrigger: &metricTrigger, + ScaleAction: &scaleAction, + } + + rules = append(rules, rule) + } + + return &rules +} + +func expandAzureRmMonitorAutoScaleSettingFixedDate(input []interface{}) (*insights.TimeWindow, error) { + if len(input) == 0 { + return nil, nil + } + + raw := input[0].(map[string]interface{}) + + startString := raw["start"].(string) + startTime, err := date.ParseTime(time.RFC3339, startString) + if err != nil { + return nil, fmt.Errorf("Failed to parse `start` time %q as an RFC3339 date: %+v", startString, err) + } + endString := raw["end"].(string) + endTime, err := date.ParseTime(time.RFC3339, endString) + if err != nil { + return nil, fmt.Errorf("Failed to parse `end` time %q as an RFC3339 date: %+v", endString, err) + } + + timeZone := raw["timezone"].(string) + timeWindow := insights.TimeWindow{ + TimeZone: utils.String(timeZone), + Start: &date.Time{ + Time: startTime, + }, + End: &date.Time{ + Time: endTime, + }, + } + return &timeWindow, nil +} + +func expandAzureRmMonitorAutoScaleSettingRecurrence(input []interface{}) *insights.Recurrence { + if len(input) == 0 { + return nil + } + + recurrenceRaw := input[0].(map[string]interface{}) + + timeZone := recurrenceRaw["timezone"].(string) + days := make([]string, 0) + for _, dayItem := range recurrenceRaw["days"].([]interface{}) { + days = append(days, dayItem.(string)) + } + + hours := make([]int32, 0) + for _, hourItem := range recurrenceRaw["hours"].([]interface{}) { + hours = append(hours, int32(hourItem.(int))) + } + + minutes := make([]int32, 0) + for _, minuteItem := range recurrenceRaw["minutes"].([]interface{}) { + minutes = append(minutes, int32(minuteItem.(int))) + } + + return &insights.Recurrence{ + // API docs say this has to be `Week`. + Frequency: insights.RecurrenceFrequencyWeek, + Schedule: &insights.RecurrentSchedule{ + TimeZone: utils.String(timeZone), + Days: &days, + Hours: &hours, + Minutes: &minutes, + }, + } +} + +func expandAzureRmMonitorAutoScaleSettingNotifications(input []interface{}) *[]insights.AutoscaleNotification { + notifications := make([]insights.AutoscaleNotification, 0) + + for _, v := range input { + notificationRaw := v.(map[string]interface{}) + + emailsRaw := notificationRaw["email"].([]interface{}) + emailRaw := emailsRaw[0].(map[string]interface{}) + email := expandAzureRmMonitorAutoScaleSettingNotificationEmail(emailRaw) + + configsRaw := notificationRaw["webhook"].([]interface{}) + webhooks := expandAzureRmMonitorAutoScaleSettingNotificationWebhook(configsRaw) + + notification := insights.AutoscaleNotification{ + Email: email, + Operation: utils.String("scale"), + Webhooks: webhooks, + } + notifications = append(notifications, notification) + } + + return ¬ifications +} + +func expandAzureRmMonitorAutoScaleSettingNotificationEmail(input map[string]interface{}) *insights.EmailNotification { + customEmails := make([]string, 0) + if v, ok := input["custom_emails"]; ok { + for _, item := range v.([]interface{}) { + customEmails = append(customEmails, item.(string)) + } + } + + email := insights.EmailNotification{ + CustomEmails: &customEmails, + SendToSubscriptionAdministrator: utils.Bool(input["send_to_subscription_administrator"].(bool)), + SendToSubscriptionCoAdministrators: utils.Bool(input["send_to_subscription_co_administrator"].(bool)), + } + + return &email +} + +func expandAzureRmMonitorAutoScaleSettingNotificationWebhook(input []interface{}) *[]insights.WebhookNotification { + webhooks := make([]insights.WebhookNotification, 0) + + for _, v := range input { + webhookRaw := v.(map[string]interface{}) + + webhook := insights.WebhookNotification{ + ServiceURI: utils.String(webhookRaw["service_uri"].(string)), + } + + if props, ok := webhookRaw["properties"]; ok { + properties := make(map[string]*string) + for key, value := range props.(map[string]interface{}) { + properties[key] = utils.String(value.(string)) + } + + webhook.Properties = properties + } + + webhooks = append(webhooks, webhook) + } + + return &webhooks +} + +func flattenAzureRmMonitorAutoScaleSettingProfile(profiles *[]insights.AutoscaleProfile) ([]interface{}, error) { + if profiles == nil { + return []interface{}{}, nil + } + + results := make([]interface{}, 0) + for _, profile := range *profiles { + result := make(map[string]interface{}) + + if name := profile.Name; name != nil { + result["name"] = *name + } + + capacity, err := flattenAzureRmMonitorAutoScaleSettingCapacity(profile.Capacity) + if err != nil { + return nil, fmt.Errorf("Error flattening `capacity`: %+v", err) + } + result["capacity"] = capacity + + result["fixed_date"] = flattenAzureRmMonitorAutoScaleSettingFixedDate(profile.FixedDate) + result["recurrence"] = flattenAzureRmMonitorAutoScaleSettingRecurrence(profile.Recurrence) + + rule, err := flattenAzureRmMonitorAutoScaleSettingRules(profile.Rules) + if err != nil { + return nil, fmt.Errorf("Error flattening Rule: %s", err) + } + result["rule"] = rule + + results = append(results, result) + } + return results, nil +} + +func flattenAzureRmMonitorAutoScaleSettingCapacity(input *insights.ScaleCapacity) ([]interface{}, error) { + if input == nil { + return []interface{}{}, nil + } + + result := make(map[string]interface{}) + + if minStr := input.Minimum; minStr != nil { + min, err := strconv.Atoi(*minStr) + if err != nil { + return nil, fmt.Errorf("Error converting Minimum Scale Capacity %q to an int: %+v", *minStr, err) + } + result["minimum"] = min + } + + if maxStr := input.Maximum; maxStr != nil { + max, err := strconv.Atoi(*maxStr) + if err != nil { + return nil, fmt.Errorf("Error converting Maximum Scale Capacity %q to an int: %+v", *maxStr, err) + } + result["maximum"] = max + } + + if defaultCapacityStr := input.Default; defaultCapacityStr != nil { + defaultCapacity, err := strconv.Atoi(*defaultCapacityStr) + if err != nil { + return nil, fmt.Errorf("Error converting Default Scale Capacity %q to an int: %+v", *defaultCapacityStr, err) + } + result["default"] = defaultCapacity + } + + return []interface{}{result}, nil +} + +func flattenAzureRmMonitorAutoScaleSettingRules(input *[]insights.ScaleRule) ([]interface{}, error) { + if input == nil { + return []interface{}{}, nil + } + + results := make([]interface{}, 0) + for _, rule := range *input { + result := make(map[string]interface{}) + + metricTriggers := make([]interface{}, 0) + if trigger := rule.MetricTrigger; trigger != nil { + output := make(map[string]interface{}) + + output["operator"] = string(trigger.Operator) + output["statistic"] = string(trigger.Statistic) + output["time_aggregation"] = string(trigger.TimeAggregation) + + if trigger.MetricName != nil { + output["metric_name"] = *trigger.MetricName + } + + if trigger.MetricResourceURI != nil { + output["metric_resource_id"] = *trigger.MetricResourceURI + } + + if trigger.TimeGrain != nil { + output["time_grain"] = *trigger.TimeGrain + } + + if trigger.TimeWindow != nil { + output["time_window"] = *trigger.TimeWindow + } + + if trigger.Threshold != nil { + output["threshold"] = *trigger.Threshold + } + + metricTriggers = append(metricTriggers, output) + } + + result["metric_trigger"] = metricTriggers + + scaleActions := make([]interface{}, 0) + if v := rule.ScaleAction; v != nil { + action := make(map[string]interface{}) + + action["direction"] = string(v.Direction) + action["type"] = string(v.Type) + + if v.Cooldown != nil { + action["cooldown"] = *v.Cooldown + } + + if val := v.Value; val != nil && *val != "" { + i, err := strconv.Atoi(*val) + if err != nil { + return nil, fmt.Errorf("`value` %q was not convertable to an int: %s", *val, err) + } + action["value"] = i + } + + scaleActions = append(scaleActions, action) + } + + result["scale_action"] = scaleActions + + results = append(results, result) + } + + return results, nil +} + +func flattenAzureRmMonitorAutoScaleSettingFixedDate(input *insights.TimeWindow) []interface{} { + if input == nil { + return []interface{}{} + } + + result := make(map[string]interface{}) + + if timezone := input.TimeZone; timezone != nil { + result["timezone"] = *timezone + } + + if start := input.Start; start != nil { + result["start"] = start.String() + } + + if end := input.End; end != nil { + result["end"] = end.String() + } + + return []interface{}{result} +} + +func flattenAzureRmMonitorAutoScaleSettingRecurrence(input *insights.Recurrence) []interface{} { + if input == nil { + return []interface{}{} + } + + result := make(map[string]interface{}) + + if schedule := input.Schedule; schedule != nil { + + if timezone := schedule.TimeZone; timezone != nil { + result["timezone"] = *timezone + } + + days := make([]string, 0) + if s := schedule.Days; s != nil { + days = *s + } + result["days"] = days + + hours := make([]int, 0) + if schedule.Hours != nil { + for _, v := range *schedule.Hours { + hours = append(hours, int(v)) + } + } + result["hours"] = hours + + minutes := make([]int, 0) + if schedule.Minutes != nil { + for _, v := range *schedule.Minutes { + minutes = append(minutes, int(v)) + } + } + result["minutes"] = minutes + } + + return []interface{}{result} +} + +func flattenAzureRmMonitorAutoScaleSettingNotification(notifications *[]insights.AutoscaleNotification) []interface{} { + results := make([]interface{}, 0) + + if notifications == nil { + return results + } + + for _, notification := range *notifications { + result := make(map[string]interface{}) + + emails := make([]interface{}, 0) + if email := notification.Email; email != nil { + block := make(map[string]interface{}) + + if send := email.SendToSubscriptionAdministrator; send != nil { + block["send_to_subscription_administrator"] = *send + } + + if send := email.SendToSubscriptionCoAdministrators; send != nil { + block["send_to_subscription_co_administrator"] = *send + } + + customEmails := make([]interface{}, 0) + if custom := email.CustomEmails; custom != nil { + for _, v := range *custom { + customEmails = append(customEmails, v) + } + } + block["custom_emails"] = customEmails + + emails = append(emails, block) + } + result["email"] = emails + + webhooks := make([]interface{}, 0) + if hooks := notification.Webhooks; hooks != nil { + for _, v := range *hooks { + hook := make(map[string]interface{}) + + if v.ServiceURI != nil { + hook["service_uri"] = *v.ServiceURI + } + + props := make(map[string]string) + for key, value := range v.Properties { + if value != nil { + props[key] = *value + } + } + hook["properties"] = props + webhooks = append(webhooks, hook) + } + } + + result["webhook"] = webhooks + + results = append(results, result) + } + return results +} + +func validateMonitorAutoScaleSettingsTimeZone() schema.SchemaValidateFunc { + // from https://docs.microsoft.com/en-us/rest/api/monitor/autoscalesettings/createorupdate#timewindow + timeZones := []string{ + "Dateline Standard Time", + "UTC-11", + "Hawaiian Standard Time", + "Alaskan Standard Time", + "Pacific Standard Time (Mexico)", + "Pacific Standard Time", + "US Mountain Standard Time", + "Mountain Standard Time (Mexico)", + "Mountain Standard Time", + "Central America Standard Time", + "Central Standard Time", + "Central Standard Time (Mexico)", + "Canada Central Standard Time", + "SA Pacific Standard Time", + "Eastern Standard Time", + "US Eastern Standard Time", + "Venezuela Standard Time", + "Paraguay Standard Time", + "Atlantic Standard Time", + "Central Brazilian Standard Time", + "SA Western Standard Time", + "Pacific SA Standard Time", + "Newfoundland Standard Time", + "E. South America Standard Time", + "Argentina Standard Time", + "SA Eastern Standard Time", + "Greenland Standard Time", + "Montevideo Standard Time", + "Bahia Standard Time", + "UTC-02", + "Mid-Atlantic Standard Time", + "Azores Standard Time", + "Cape Verde Standard Time", + "Morocco Standard Time", + "UTC", + "GMT Standard Time", + "Greenwich Standard Time", + "W. Europe Standard Time", + "Central Europe Standard Time", + "Romance Standard Time", + "Central European Standard Time", + "W. Central Africa Standard Time", + "Namibia Standard Time", + "Jordan Standard Time", + "GTB Standard Time", + "Middle East Standard Time", + "Egypt Standard Time", + "Syria Standard Time", + "E. Europe Standard Time", + "South Africa Standard Time", + "FLE Standard Time", + "Turkey Standard Time", + "Israel Standard Time", + "Kaliningrad Standard Time", + "Libya Standard Time", + "Arabic Standard Time", + "Arab Standard Time", + "Belarus Standard Time", + "Russian Standard Time", + "E. Africa Standard Time", + "Iran Standard Time", + "Arabian Standard Time", + "Azerbaijan Standard Time", + "Russia Time Zone 3", + "Mauritius Standard Time", + "Georgian Standard Time", + "Caucasus Standard Time", + "Afghanistan Standard Time", + "West Asia Standard Time", + "Ekaterinburg Standard Time", + "Pakistan Standard Time", + "India Standard Time", + "Sri Lanka Standard Time", + "Nepal Standard Time", + "Central Asia Standard Time", + "Bangladesh Standard Time", + "N. Central Asia Standard Time", + "Myanmar Standard Time", + "SE Asia Standard Time", + "North Asia Standard Time", + "China Standard Time", + "North Asia East Standard Time", + "Singapore Standard Time", + "W. Australia Standard Time", + "Taipei Standard Time", + "Ulaanbaatar Standard Time", + "Tokyo Standard Time", + "Korea Standard Time", + "Yakutsk Standard Time", + "Cen. Australia Standard Time", + "AUS Central Standard Time", + "E. Australia Standard Time", + "AUS Eastern Standard Time", + "West Pacific Standard Time", + "Tasmania Standard Time", + "Magadan Standard Time", + "Vladivostok Standard Time", + "Russia Time Zone 10", + "Central Pacific Standard Time", + "Russia Time Zone 11", + "New Zealand Standard Time", + "UTC+12", + "Fiji Standard Time", + "Kamchatka Standard Time", + "Tonga Standard Time", + "Samoa Standard Time", + "Line Islands Standard Time", + } + return validation.StringInSlice(timeZones, false) +} diff --git a/azurerm/resource_arm_monitor_autoscale_setting_test.go b/azurerm/resource_arm_monitor_autoscale_setting_test.go new file mode 100644 index 000000000000..a93aef8e7446 --- /dev/null +++ b/azurerm/resource_arm_monitor_autoscale_setting_test.go @@ -0,0 +1,811 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMMonitorAutoScaleSetting_basic(t *testing.T) { + resourceName := "azurerm_autoscale_setting.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + location := testLocation() + config := testAccAzureRMMonitorAutoScaleSetting_basic(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorAutoScaleSettingDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "profile.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.name", "metricRules"), + resource.TestCheckResourceAttr(resourceName, "profile.0.rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.#", "0"), + resource.TestCheckNoResourceAttr(resourceName, "tags.$type"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMMonitorAutoScaleSetting_multipleProfiles(t *testing.T) { + resourceName := "azurerm_autoscale_setting.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + location := testLocation() + config := testAccAzureRMMonitorAutoScaleSetting_multipleProfiles(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorAutoScaleSettingDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "profile.#", "2"), + resource.TestCheckResourceAttr(resourceName, "profile.0.name", "primary"), + resource.TestCheckResourceAttr(resourceName, "profile.1.name", "secondary"), + ), + }, + }, + }) +} + +func TestAccAzureRMMonitorAutoScaleSetting_multipleRules(t *testing.T) { + resourceName := "azurerm_autoscale_setting.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorAutoScaleSettingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMonitorAutoScaleSetting_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "profile.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.name", "metricRules"), + resource.TestCheckResourceAttr(resourceName, "profile.0.rule.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.rule.0.scale_action.0.direction", "Increase"), + resource.TestCheckResourceAttr(resourceName, "notification.#", "0"), + ), + }, + { + Config: testAccAzureRMMonitorAutoScaleSetting_multipleRules(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "profile.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.name", "metricRules"), + resource.TestCheckResourceAttr(resourceName, "profile.0.rule.#", "2"), + resource.TestCheckResourceAttr(resourceName, "profile.0.rule.0.scale_action.0.direction", "Increase"), + resource.TestCheckResourceAttr(resourceName, "profile.0.rule.1.scale_action.0.direction", "Decrease"), + resource.TestCheckResourceAttr(resourceName, "notification.#", "0"), + ), + }, + }, + }) +} + +func TestAccAzureRMMonitorAutoScaleSetting_customEmails(t *testing.T) { + resourceName := "azurerm_autoscale_setting.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorAutoScaleSettingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMonitorAutoScaleSetting_email(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "notification.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.0.email.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.0.email.0.custom_emails.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.0.email.0.custom_emails.0", fmt.Sprintf("acctest1-%d@example.com", ri)), + ), + }, + { + Config: testAccAzureRMMonitorAutoScaleSetting_emailUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "notification.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.0.email.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.0.email.0.custom_emails.#", "2"), + resource.TestCheckResourceAttr(resourceName, "notification.0.email.0.custom_emails.0", fmt.Sprintf("acctest1-%d@example.com", ri)), + resource.TestCheckResourceAttr(resourceName, "notification.0.email.0.custom_emails.1", fmt.Sprintf("acctest2-%d@example.com", ri)), + ), + }, + }, + }) +} + +func TestAccAzureRMMonitorAutoScaleSetting_recurrence(t *testing.T) { + resourceName := "azurerm_autoscale_setting.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + location := testLocation() + config := testAccAzureRMMonitorAutoScaleSetting_recurrence(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorAutoScaleSettingDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "profile.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.name", "recurrence"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMMonitorAutoScaleSetting_recurrenceUpdate(t *testing.T) { + resourceName := "azurerm_autoscale_setting.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorAutoScaleSettingDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMonitorAutoScaleSetting_recurrence(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "notification.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.#", "3"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.0", "Monday"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.1", "Wednesday"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.2", "Friday"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.hours.0", "18"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.minutes.0", "0"), + ), + }, + { + Config: testAccAzureRMMonitorAutoScaleSetting_recurrenceUpdated(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.#", "3"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.0", "Monday"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.1", "Tuesday"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.days.2", "Wednesday"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.hours.0", "20"), + resource.TestCheckResourceAttr(resourceName, "profile.0.recurrence.0.minutes.0", "15"), + ), + }, + }, + }) +} + +func TestAccAzureRMMonitorAutoScaleSetting_fixedDate(t *testing.T) { + resourceName := "azurerm_autoscale_setting.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(6) + location := testLocation() + config := testAccAzureRMMonitorAutoScaleSetting_fixedDate(ri, rs, location) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMonitorAutoScaleSettingDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMonitorAutoScaleSettingExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "profile.#", "1"), + resource.TestCheckResourceAttr(resourceName, "profile.0.name", "fixedDate"), + resource.TestCheckResourceAttr(resourceName, "profile.0.fixed_date.#", "1"), + resource.TestCheckResourceAttr(resourceName, "notification.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMMonitorAutoScaleSettingExists(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) + } + + autoscaleSettingName := rs.Primary.Attributes["name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Monitor AutoScale Setting: %s", autoscaleSettingName) + } + + conn := testAccProvider.Meta().(*ArmClient).autoscaleSettingsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := conn.Get(ctx, resourceGroup, autoscaleSettingName) + if err != nil { + return fmt.Errorf("Bad: Get on Monitor AutoScale Setting: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: AutoScale Setting %q (Resource Group: %q) does not exist", autoscaleSettingName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMMonitorAutoScaleSettingDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).autoscaleSettingsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_autoscale_setting" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(ctx, resourceGroup, name) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("AutoScale Setting still exists:\n%#v", resp) + } + } + + return nil +} + +func testAccAzureRMMonitorAutoScaleSetting_basic(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + profile { + name = "metricRules" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 75 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = 1 + cooldown = "PT1M" + } + } + } +} +`, template, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_multipleProfiles(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + profile { + name = "primary" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 75 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = 1 + cooldown = "PT1M" + } + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 75 + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = 1 + cooldown = "PT1M" + } + } + } + + profile { + name = "secondary" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + recurrence { + timezone = "Pacific Standard Time" + + days = [ + "Monday", + "Wednesday", + "Friday", + ] + + hours = [18] + minutes = [0] + } + } +} +`, template, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_multipleRules(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + enabled = true + + profile { + name = "metricRules" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 75 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = 1 + cooldown = "PT1M" + } + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 25 + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = 1 + cooldown = "PT1M" + } + } + } +} +`, template, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_email(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + profile { + name = "metricRules" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 75 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = 1 + cooldown = "PT1M" + } + } + } + + notification { + email { + send_to_subscription_administrator = false + send_to_subscription_co_administrator = false + custom_emails = ["acctest1-%d@example.com"] + } + } +} +`, template, rInt, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_emailUpdated(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + profile { + name = "metricRules" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + rule { + metric_trigger { + metric_name = "Percentage CPU" + metric_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 75 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = 1 + cooldown = "PT1M" + } + } + } + + notification { + email { + send_to_subscription_administrator = false + send_to_subscription_co_administrator = false + custom_emails = ["acctest1-%d@example.com", "acctest2-%d@example.com"] + } + } +} +`, template, rInt, rInt, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_recurrence(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + profile { + name = "recurrence" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + recurrence { + timezone = "Pacific Standard Time" + + days = [ + "Monday", + "Wednesday", + "Friday", + ] + + hours = [18] + minutes = [0] + } + } + + notification { + email { + send_to_subscription_administrator = false + send_to_subscription_co_administrator = false + } + } +} +`, template, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_recurrenceUpdated(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + profile { + name = "recurrence" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + recurrence { + timezone = "Pacific Standard Time" + + days = [ + "Monday", + "Tuesday", + "Wednesday", + ] + + hours = [20] + minutes = [15] + } + } + + notification { + email { + send_to_subscription_administrator = false + send_to_subscription_co_administrator = false + } + } +} +`, template, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_fixedDate(rInt int, rString string, location string) string { + template := testAccAzureRMMonitorAutoScaleSetting_template(rInt, rString, location) + return fmt.Sprintf(` +%s + +resource "azurerm_autoscale_setting" "test" { + name = "acctestautoscale-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + profile { + name = "fixedDate" + + capacity { + default = 1 + minimum = 1 + maximum = 10 + } + + fixed_date { + timezone = "Pacific Standard Time" + start = "2020-06-18T00:00:00Z" + end = "2020-06-18T23:59:59Z" + } + } +} +`, template, rInt) +} + +func testAccAzureRMMonitorAutoScaleSetting_template(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +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_storage_account" "test" { + name = "accsa%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + name = "acctvmss-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + upgrade_policy_mode = "Manual" + + sku { + name = "Standard_F2" + tier = "Standard" + capacity = 2 + } + + os_profile { + computer_name_prefix = "testvm-%d" + admin_username = "myadmin" + admin_password = "Passwword1234" + } + + network_profile { + name = "TestNetworkProfile-%d" + primary = true + + ip_configuration { + name = "TestIPConfiguration" + subnet_id = "${azurerm_subnet.test.id}" + primary = true + } + } + + storage_profile_os_disk { + name = "osDiskProfile" + caching = "ReadWrite" + create_option = "FromImage" + vhd_containers = ["${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}"] + } + + storage_profile_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } +} +`, rInt, location, rInt, rString, rInt, rInt, rInt) +} diff --git a/azurerm/resource_arm_monitor_metric_alertrule.go b/azurerm/resource_arm_monitor_metric_alertrule.go index 1b8cc8c89733..f0d4ce9b7ded 100644 --- a/azurerm/resource_arm_monitor_metric_alertrule.go +++ b/azurerm/resource_arm_monitor_metric_alertrule.go @@ -208,10 +208,12 @@ func resourceArmMonitorMetricAlertRuleRead(d *schema.ResourceData, meta interfac client := meta.(*ArmClient).monitorAlertRulesClient ctx := meta.(*ArmClient).StopContext - resourceGroup, name, err := resourceGroupAndAlertRuleNameFromId(d.Id()) + id, err := parseAzureResourceID(d.Id()) if err != nil { return err } + name := id.Path["alertrules"] + resourceGroup := id.ResourceGroup resp, err := client.Get(ctx, resourceGroup, name) if err != nil { @@ -307,10 +309,12 @@ func resourceArmMonitorMetricAlertRuleDelete(d *schema.ResourceData, meta interf client := meta.(*ArmClient).monitorAlertRulesClient ctx := meta.(*ArmClient).StopContext - resourceGroup, name, err := resourceGroupAndAlertRuleNameFromId(d.Id()) + id, err := parseAzureResourceID(d.Id()) if err != nil { return err } + name := id.Path["alertrules"] + resourceGroup := id.ResourceGroup resp, err := client.Delete(ctx, resourceGroup, name) if err != nil { @@ -423,17 +427,6 @@ func expandAzureRmMonitorMetricThresholdAlertRule(d *schema.ResourceData) (*insi return &alertRule, nil } -func resourceGroupAndAlertRuleNameFromId(alertRuleId string) (string, string, error) { - id, err := parseAzureResourceID(alertRuleId) - if err != nil { - return "", "", err - } - name := id.Path["alertrules"] - resourceGroup := id.ResourceGroup - - return resourceGroup, name, nil -} - func validateMonitorMetricAlertRuleTags(v interface{}, f string) (warnings []string, errors []error) { // Normal validation required by any AzureRM resource. warnings, errors = validateAzureRMTags(v, f) diff --git a/azurerm/resource_arm_network_interface.go b/azurerm/resource_arm_network_interface.go index d25e822136fa..5ceb269e01da 100644 --- a/azurerm/resource_arm_network_interface.go +++ b/azurerm/resource_arm_network_interface.go @@ -146,6 +146,7 @@ func resourceArmNetworkInterface() *schema.Resource { Set: schema.HashString, }, + // TODO: does this want deprecating & a virtual resource for 2.0? "application_security_group_ids": { Type: schema.TypeSet, Optional: true, diff --git a/azurerm/resource_arm_postgresql_server.go b/azurerm/resource_arm_postgresql_server.go index 56ac58d89f45..5713ca6bb596 100644 --- a/azurerm/resource_arm_postgresql_server.go +++ b/azurerm/resource_arm_postgresql_server.go @@ -127,8 +127,9 @@ func resourceArmPostgreSQLServer() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ string(postgresql.NineFullStopFive), string(postgresql.NineFullStopSix), - // TODO: Swap for the azure go api enum once supported. - "10.0", + string(postgresql.OneZero), + string(postgresql.OneZeroFullStopZero), + string(postgresql.OneZeroFullStopTwo), }, true), DiffSuppressFunc: suppress.CaseDifference, }, diff --git a/azurerm/resource_arm_servicebus_queue.go b/azurerm/resource_arm_servicebus_queue.go index e9f7e0e5ff62..3d0e3d67c4a4 100644 --- a/azurerm/resource_arm_servicebus_queue.go +++ b/azurerm/resource_arm_servicebus_queue.go @@ -106,13 +106,14 @@ func resourceArmServiceBusQueue() *schema.Resource { Optional: true, }, - // TODO: remove these in the next major release + // TODO: remove this in 2.0 "enable_batched_operations": { Type: schema.TypeBool, Optional: true, Deprecated: "This field has been removed by Azure.", }, + // TODO: remove this in 2.0 "support_ordering": { Type: schema.TypeBool, Optional: true, diff --git a/azurerm/resource_arm_traffic_manager_endpoint.go b/azurerm/resource_arm_traffic_manager_endpoint.go index 3aaf78642100..e35d0356085f 100644 --- a/azurerm/resource_arm_traffic_manager_endpoint.go +++ b/azurerm/resource_arm_traffic_manager_endpoint.go @@ -235,7 +235,7 @@ func getArmTrafficManagerEndpointProperties(d *schema.ResourceData) *trafficmana if resourceId := d.Get("target_resource_id").(string); resourceId != "" { endpointProps.TargetResourceID = utils.String(resourceId) - //TODO? Workaround for upstream behavior: if the target is blank instead of nil, the REST API will throw a 500 error. Remove if/when no longer necessary + //NOTE: Workaround for upstream behavior: if the target is blank instead of nil, the REST API will throw a 500 error if target == "" { endpointProps.Target = nil } diff --git a/website/azurerm.erb b/website/azurerm.erb index 3d8c1c894859..ddfe7e548696 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -899,7 +899,7 @@