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 @@ > Monitor Resources - > - Azure Monitor for containers Resources + > + Log Analytics Resources diff --git a/website/docs/guides/migrating-between-renamed-resources.html.markdown b/website/docs/guides/migrating-between-renamed-resources.html.markdown new file mode 100644 index 000000000000..7b61aa02a81e --- /dev/null +++ b/website/docs/guides/migrating-between-renamed-resources.html.markdown @@ -0,0 +1,100 @@ +--- +layout: "azurerm" +page_title: "Azure Active Directory: Migrating to a renamed resource" +sidebar_current: "docs-azurerm-migrating-to-a-renamed-resource" +description: | - + This page documents how to migrate between two resources in the Azure Provider which have been renamed. + +--- + +# Azure Active Directory: Migrating to a renamed resource + +In v1.22 of the AzureRM Provider several resources have been deprecated in favour of a renamed version - this guide covers how to migrate from the old resource to the new one and is applicable for the following resources: + +| Old Name | New Name | +| ---------------------------------------------- | ------------------------------------ | +| azurerm_log_analytics_workspace_linked_service | azurerm_log_analytics_linked_service | +| azurerm_autoscale_setting | azurerm_monitor_autoscale_setting | +| azurerm_metric_alertrule | azurerm_monitor_metric_alertrule | + +As the Schema's for each resource are the same at this time - it's possible to migrate between the resources by updating your Terraform Configuration and updating the Statefile. + +In this guide we'll assume we're migrating from the `azurerm_autoscale_setting` resource to the new `azurerm_monitor_autoscale_setting` resource, but this should be applicable for any of the resources listed above. + +Assuming we have the following Terraform Configuration: + +```hcl +resource "azurerm_resource_group" "test" { + # ... +} + +resource "azurerm_virtual_machine_scale_set" "test" { + # ... +} + +resource "azurerm_autoscale_setting" "test" { + name = "myAutoscaleSetting" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + # ... +} +``` + +We can update the Terraform Configuration to use the new resource by updating the name from `azurerm_autoscale_setting` to `azurerm_monitor_autoscale_setting`: + +```hcl +resource "azurerm_resource_group" "test" { + # ... +} + +resource "azurerm_virtual_machine_scale_set" "test" { + # ... +} + +resource "azurerm_monitor_autoscale_setting" "test" { + name = "myAutoscaleSetting" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + target_resource_id = "${azurerm_virtual_machine_scale_set.test.id}" + + # ... +} +``` + +As the Terraform Configuration has been updated - we now need to update the State. We can view the items Terraform is tracking in it's Statefile using the `terraform state list` command, for example: + +```bash +$ terraform state list +azurerm_autoscale_setting.test +azurerm_resource_group.test +azurerm_virtual_machine.test +``` + +We can move each of the resources which has been renamed in the state using the `terraform state mv` command - for example: + +```shell +$ terraform state mv azurerm_autoscale_setting.test azurerm_monitor_autoscale_setting.test +Moved azurerm_autoscale_setting.test to azurerm_monitor_autoscale_setting.test +``` + +Once this has been done, running `terraform plan` should show no changes: + +```shell +$ terraform plan +Refreshing Terraform state in-memory prior to plan... +The refreshed state will be used to calculate this plan, but will not be +persisted to local or remote state storage. + + +------------------------------------------------------------------------ + +No changes. Infrastructure is up-to-date. + +This means that Terraform did not detect any differences between your +configuration and real physical resources that exist. As a result, no +actions need to be performed. +``` + +At this point you've switched over to using the newly renamed resources and should be able to continue using Terraform as normal. diff --git a/website/docs/r/application_gateway.html.markdown b/website/docs/r/application_gateway.html.markdown index f8fb46713552..d91c9f5fbeec 100644 --- a/website/docs/r/application_gateway.html.markdown +++ b/website/docs/r/application_gateway.html.markdown @@ -171,9 +171,13 @@ A `backend_address_pool` block supports the following: * `name` - (Required) The name of the Backend Address Pool. -* `fqdn_list` - (Optional) A list of FQDN's which should be part of the Backend Address Pool. +* `fqdns` - (Optional) A list of FQDN's which should be part of the Backend Address Pool. -* `ip_address_list` - (Optional) A list of IP Addresses which should be part of the Backend Address Pool. +* `fqdn_list` - (Optional **Deprecated**) A list of FQDN's which should be part of the Backend Address Pool. This field has been deprecated in favour of `fqdns` and will be removed in v2.0 of the AzureRM Provider. + +* `ip_addresses` - (Optional) A list of IP Addresses which should be part of the Backend Address Pool. + +* `ip_address_list` - (Optional **Deprecated**) A list of IP Addresses which should be part of the Backend Address Pool. This field has been deprecated in favour of `ip_addresses` and will be removed in v2.0 of the AzureRM Provider. --- diff --git a/website/docs/r/autoscale_setting.html.markdown b/website/docs/r/autoscale_setting.html.markdown index 43b22d01d55d..35f532de6366 100644 --- a/website/docs/r/autoscale_setting.html.markdown +++ b/website/docs/r/autoscale_setting.html.markdown @@ -10,6 +10,8 @@ description: |- Manages an AutoScale Setting which can be applied to Virtual Machine Scale Sets, App Services and other scalable resources. +~> **NOTE:** This resource has been deprecated in favour of the `azurerm_monitor_autoscale_setting` resource and will be removed in the next major version of the AzureRM Provider. The new resource shares the same fields as this one, and information on migrating across [can be found in this guide](../guides/migrating-between-renamed-resources.html). + ## Example Usage ```hcl diff --git a/website/docs/r/log_analytics_linked_service.html.markdown b/website/docs/r/log_analytics_linked_service.html.markdown new file mode 100644 index 000000000000..cebcc5dea237 --- /dev/null +++ b/website/docs/r/log_analytics_linked_service.html.markdown @@ -0,0 +1,86 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_log_analytics_linked_service" +sidebar_current: "docs-azurerm-log-analytics-linked-service" +description: |- + Manages a Log Analytics (formally Operational Insights) Linked Service. +--- + +# azurerm_log_analytics_linked_service + +Links a Log Analytics (formally Operational Insights) Workspace to another resource. The (currently) only linkable service is an Azure Automation Account. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "resourcegroup-01" + location = "West Europe" +} + +resource "azurerm_automation_account" "test" { + name = "automation-01" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + name = "Basic" + } + + tags { + environment = "development" + } +} + +resource "azurerm_log_analytics_workspace" "test" { + name = "workspace-01" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "PerGB2018" + retention_in_days = 30 +} + +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}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `resource_group_name` - (Required) The name of the resource group in which the Log Analytics Linked Service is created. Changing this forces a new resource to be created. + +* `workspace_name` - (Required) Name of the Log Analytics Workspace that will contain the linkedServices resource. Changing this forces a new resource to be created. + +* `linked_service_name` - (Optional) Name of the type of linkedServices resource to connect to the Log Analytics Workspace specified in `workspace_name`. Currently it defaults to and only supports `automation` as a value. Changing this forces a new resource to be created. + +* `resource_id` - (Optional) The ID of the Resource that will be linked to the workspace. + +* `linked_service_properties` - (Optional **Deprecated**) A `linked_service_properties` block as defined below. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +--- + +`linked_service_properties` supports the following: + +* `resource_id` - (Optional **Deprecated**) The resource id of the resource that will be linked to the workspace. This field has been deprecated in favour of the top-level `resource_id` field and will be removed in v2.0 of the AzureRM Provider. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Log Analytics Linked Service ID. + +* `name` - The automatically generated name of the Linked Service. This cannot be specified. The format is always `/` e.g. `workspace1/Automation` + +## Import + +Log Analytics Workspaces can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_log_analytics_linked_service.test /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.OperationalInsights/workspaces/workspace1/linkedServices/automation +``` diff --git a/website/docs/r/log_analytics_solution.html.markdown b/website/docs/r/log_analytics_solution.html.markdown index b6d744255439..cf65a4cae2db 100644 --- a/website/docs/r/log_analytics_solution.html.markdown +++ b/website/docs/r/log_analytics_solution.html.markdown @@ -1,7 +1,7 @@ --- layout: "azurerm" page_title: "Azure Resource Manager: azurerm_log_analytics_solution" -sidebar_current: "docs-azurerm-oms-log-analytics-solution" +sidebar_current: "docs-azurerm-log-analytics-solution" description: |- Manages a Log Analytics (formally Operational Insights) Solution. --- diff --git a/website/docs/r/log_analytics_workspace.html.markdown b/website/docs/r/log_analytics_workspace.html.markdown index 7d2001f48cda..9dd8a7884362 100644 --- a/website/docs/r/log_analytics_workspace.html.markdown +++ b/website/docs/r/log_analytics_workspace.html.markdown @@ -1,7 +1,7 @@ --- layout: "azurerm" page_title: "Azure Resource Manager: azurerm_log_analytics_workspace" -sidebar_current: "docs-azurerm-oms-log-analytics-workspace-x" +sidebar_current: "docs-azurerm-log-analytics-workspace-x" description: |- Manages a Log Analytics (formally Operational Insights) Workspace. --- diff --git a/website/docs/r/log_analytics_workspace_linked_service.html.markdown b/website/docs/r/log_analytics_workspace_linked_service.html.markdown index fe2717fdc186..e8b88e3407bf 100644 --- a/website/docs/r/log_analytics_workspace_linked_service.html.markdown +++ b/website/docs/r/log_analytics_workspace_linked_service.html.markdown @@ -1,7 +1,7 @@ --- layout: "azurerm" page_title: "Azure Resource Manager: azurerm_log_analytics_workspace_linked_service" -sidebar_current: "docs-azurerm-oms-log-analytics-workspace-linked-service" +sidebar_current: "docs-azurerm-log-analytics-workspace-linked-service" description: |- Manages a Log Analytics (formally Operational Insights) Linked Service. --- @@ -10,6 +10,9 @@ description: |- Links a Log Analytics (formally Operational Insights) Workspace to another resource. The (currently) only linkable service is an Azure Automation Account. +~> **NOTE:** This resource has been deprecated in favour of the `azurerm_log_analytics_linked_service` resource and will be removed in the next major version of the AzureRM Provider. The new resource shares the same fields as this one, and information on migrating across [can be found in this guide](../guides/migrating-between-renamed-resources.html). + + ## Example Usage ```hcl @@ -43,10 +46,7 @@ resource "azurerm_log_analytics_workspace" "test" { 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}" } ``` @@ -60,13 +60,17 @@ The following arguments are supported: * `linked_service_name` - (Optional) Name of the type of linkedServices resource to connect to the Log Analytics Workspace specified in `workspace_name`. Currently it defaults to and only supports `automation` as a value. Changing this forces a new resource to be created. -* `linked_service_properties` - (Required) A `linked_service_properties` block as defined below. +* `resource_id` - (Optional) The ID of the Resource that will be linked to the workspace. + +* `linked_service_properties` - (Optional **Deprecated**) A `linked_service_properties` block as defined below. * `tags` - (Optional) A mapping of tags to assign to the resource. +--- + `linked_service_properties` supports the following: -* `resource_id` - (Required) The resource id of the resource that will be linked to the workspace. +* `resource_id` - (Optional **Deprecated**) The resource id of the resource that will be linked to the workspace. This field has been deprecated in favour of the top-level `resource_id` field and will be removed in v2.0 of the AzureRM Provider. ## Attributes Reference diff --git a/website/docs/r/metric_alertrule.html.markdown b/website/docs/r/metric_alertrule.html.markdown index 83e370aba643..39ad24290b54 100644 --- a/website/docs/r/metric_alertrule.html.markdown +++ b/website/docs/r/metric_alertrule.html.markdown @@ -11,6 +11,8 @@ description: |- Manages a [metric-based alert rule](https://docs.microsoft.com/en-us/azure/monitoring-and-diagnostics/monitor-quick-resource-metric-alert-portal) in Azure Monitor. +~> **NOTE:** This resource has been deprecated in favour of the `azurerm_monitor_metric_alertrule` resource and will be removed in the next major version of the AzureRM Provider. The new resource shares the same fields as this one, and information on migrating across [can be found in this guide](../guides/migrating-between-renamed-resources.html). + ## Example Usage (CPU Percentage of a virtual machine) ```hcl diff --git a/website/docs/r/monitor_autoscale_setting.html.markdown b/website/docs/r/monitor_autoscale_setting.html.markdown new file mode 100644 index 000000000000..5990446ad4d9 --- /dev/null +++ b/website/docs/r/monitor_autoscale_setting.html.markdown @@ -0,0 +1,410 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_monitor_autoscale_setting" +sidebar_current: "docs-azurerm-resource-monitor-autoscale-setting" +description: |- + Manages an AutoScale Setting which can be applied to Virtual Machine Scale Sets, App Services and other scalable resources. +--- + +# azurerm_monitor_autoscale_setting + +Manages a AutoScale Setting which can be applied to Virtual Machine Scale Sets, App Services and other scalable resources. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "autoscalingTest" + location = "West US" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + # ... +} + +resource "azurerm_monitor_autoscale_setting" "test" { + name = "myAutoscaleSetting" + 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 = "defaultProfile" + + 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" + } + } + } + + notification { + email { + send_to_subscription_administrator = true + send_to_subscription_co_administrator = true + custom_emails = ["admin@contoso.com"] + } + } +} +``` + +## Example Usage (repeating on weekends) + +```hcl +resource "azurerm_resource_group" "test" { + name = "autoscalingTest" + location = "West US" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + # ... +} + +resource "azurerm_monitor_autoscale_setting" "test" { + name = "myAutoscaleSetting" + 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 = "Weekends" + + 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 = 90 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + 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 = 10 + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "2" + cooldown = "PT1M" + } + } + + recurrence { + frequency = "Week" + timezone = "Pacific Standard Time" + days = ["Saturday", "Sunday"] + hours = [12] + minutes = [0] + } + } + + notification { + email { + send_to_subscription_administrator = true + send_to_subscription_co_administrator = true + custom_emails = ["admin@contoso.com"] + } + } +} +``` + +## Example Usage (for fixed dates) + +```hcl +resource "azurerm_resource_group" "test" { + name = "autoscalingTest" + location = "West US" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + # ... +} + +resource "azurerm_monitor_autoscale_setting" "test" { + name = "myAutoscaleSetting" + enabled = true + 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 = "forJuly" + + 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 = 90 + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + 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 = 10 + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "2" + cooldown = "PT1M" + } + } + + fixed_date { + timezone = "Pacific Standard Time" + start = "2020-07-01T00:00:00Z" + end = "2020-07-31T23:59:59Z" + } + } + + notification { + email { + send_to_subscription_administrator = true + send_to_subscription_co_administrator = true + custom_emails = ["admin@contoso.com"] + } + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the AutoScale Setting. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the Resource Group in the AutoScale Setting should be created. Changing this forces a new resource to be created. + +* `location` - (Required) Specifies the supported Azure location where the AutoScale Setting should exist. Changing this forces a new resource to be created. + +* `profile` - (Required) Specifies one or more (up to 20) `profile` blocks as defined below. + +* `target_resource_id` - (Required) Specifies the resource ID of the resource that the autoscale setting should be added to. + +* `enabled` - (Optional) Specifies whether automatic scaling is enabled for the target resource. Defaults to `true`. + +* `notification` - (Optional) Specifies a `notification` block as defined below. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + +--- + +A `profile` block supports the following: + +* `name` - (Required) Specifies the name of the profile. + +* `capacity` - (Required) A `capacity` block as defined below. + +* `rule` - (Required) One or more (up to 10) `rule` blocks as defined below. + +* `fixed_date` - (Optional) A `fixed_date` block as defined below. This cannot be specified if a `recurrence` block is specified. + +* `recurrence` - (Optional) A `recurrence` block as defined below. This cannot be specified if a `fixed_date` block is specified. + +--- + +A `capacity` block supports the following: + +* `default` - (Required) The number of instances that are available for scaling if metrics are not available for evaluation. The default is only used if the current instance count is lower than the default. + +* `maximum` - (Required) The maximum number of instances for this resource. Valid values are between `1` and `40`. + +-> **NOTE:** The maximum number of instances is also limited by the amount of Cores available in the subscription. + +* `minimum` - (Required) The minimum number of instances for this resource. Valid values are between `1` and `40`. + +--- + +A `rule` block supports the following: + +* `metric_trigger` - (Required) A `metric_trigger` block as defined below. + +* `scale_action` - (Required) A `scale_action` block as defined below. + +--- + +A `metric_trigger` block supports the following: + +* `metric_name` - (Required) The name of the metric that defines what the rule monitors, such as `Percentage CPU` for `Virtual Machine Scale Sets` and `CpuPercentage` for `App Service Plan`. + +-> **NOTE:** The allowed value of `metric_name` highly depends on the targeting resource type, please visit [Supported metrics with Azure Monitor](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/metrics-supported) for more details. + +* `metric_resource_id` - (Required) The ID of the Resource which the Rule monitors. + +* `operator` - (Required) Specifies the operator used to compare the metric data and threshold. Possible values are: `Equals`, `NotEquals`, `GreaterThan`, `GreaterThanOrEqual`, `LessThan`, `LessThanOrEqual`. + +* `statistic` - (Required) Specifies how the metrics from multiple instances are combined. Possible values are `Average`, `Min` and `Max`. + +* `time_aggregation` - (Required) Specifies how the data that's collected should be combined over time. Possible values include `Average`, `Count`, `Maximum`, `Minimum`, `Last` and `Total`. Defaults to `Average`. + +* `time_grain` - (Required) Specifies the granularity of metrics that the rule monitors, which must be one of the pre-defined values returned from the metric definitions for the metric. This value must be between 1 minute and 12 hours an be formatted as an ISO 8601 string. + +* `time_window` - (Required) Specifies the time range for which data is collected, which must be greater than the delay in metric collection (which varies from resource to resource). This value must be between 5 minutes and 12 hours and be formatted as an ISO 8601 string. + +* `threshold` - (Required) Specifies the threshold of the metric that triggers the scale action. + +--- + +A `scale_action` block supports the following: + +* `cooldown` - (Required) The amount of time to wait since the last scaling action before this action occurs. Must be between 1 minute and 1 week and formatted as a ISO 8601 string. + +* `direction` - (Required) The scale direction. Possible values are `Increase` and `Decrease`. + +* `type` - (Required) The type of action that should occur. Possible values are `ChangeCount`, `ExactCount` and `PercentChangeCount`. + +* `value` - (Required) The number of instances involved in the scaling action. Defaults to `1`. + +--- + +A `fixed_date` block supports the following: + +* `end` - (Required) Specifies the end date for the profile, formatted as an RFC3339 date string. + +* `start` - (Required) Specifies the start date for the profile, formatted as an RFC3339 date string. + +* `timezone` (Optional) The Time Zone of the `start` and `end` times. A list of [possible values can be found here](https://msdn.microsoft.com/en-us/library/azure/dn931928.aspx). Defaults to `UTC`. + +--- + +A `recurrence` block supports the following: + +* `timezone` - (Required) The Time Zone used for the `hours` field. A list of [possible values can be found here](https://msdn.microsoft.com/en-us/library/azure/dn931928.aspx). Defaults to `UTC`. + +* `days` - (Required) A list of days that this profile takes effect on. Possible values include `Monday`, `Tuesday`, `Wednesday`, `Thursday`, `Friday`, `Saturday` and `Sunday`. + +* `hours` - (Required) A list containing a single item, which specifies the Hour interval at which this recurrence should be triggered (in 24-hour time). Possible values are from `0` to `23`. + +* `minutes` - (Required) A list containing a single item which specifies the Minute interval at which this recurrence should be triggered. + +--- + +A `notification` block supports the following: + +* `email` - (Required) A `email` block as defined below. + +* `webhook` - (Optional) One or more `webhook` blocks as defined below. + +--- + +A `email` block supports the following: + +* `send_to_subscription_administrator` - (Optional) Should email notifications be sent to the subscription administrator? Defaults to `false`. + +* `send_to_subscription_co_administrator` - (Optional) Should email notifications be sent to the subscription co-administrator? Defaults to `false`. + +* `custom_emails` - (Optional) Specifies a list of custom email addresses to which the email notifications will be sent. + +--- + +A `webhook` block supports the following: + +* `service_uri` - (Required) The HTTPS URI which should receive scale notifications. + +* `properties` - (Optional) A map of settings. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the AutoScale Setting. + +## Import + +AutoScale Setting can be imported using the `resource id`, e.g. + +``` +terraform import azurerm_monitor_autoscale_setting.test /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/microsoft.insights/autoscalesettings/setting1 +``` diff --git a/website/docs/r/postgresql_server.html.markdown b/website/docs/r/postgresql_server.html.markdown index 3e79d9e20621..a6661169bc7e 100644 --- a/website/docs/r/postgresql_server.html.markdown +++ b/website/docs/r/postgresql_server.html.markdown @@ -61,7 +61,7 @@ The following arguments are supported: * `administrator_login_password` - (Required) The Password associated with the `administrator_login` for the PostgreSQL Server. -* `version` - (Required) Specifies the version of PostgreSQL to use. Valid values are `9.5`, `9.6`, and `10.0`. Changing this forces a new resource to be created. +* `version` - (Required) Specifies the version of PostgreSQL to use. Valid values are `9.5`, `9.6`, `10`, `10.0`, and `10.2`. Changing this forces a new resource to be created. * `ssl_enforcement` - (Required) Specifies if SSL should be enforced on connections. Possible values are `Enabled` and `Disabled`. diff --git a/website/docs/r/servicebus_queue.html.markdown b/website/docs/r/servicebus_queue.html.markdown index 5537e78622ee..5ff231f8e405 100644 --- a/website/docs/r/servicebus_queue.html.markdown +++ b/website/docs/r/servicebus_queue.html.markdown @@ -48,7 +48,7 @@ The following arguments are supported: * `namespace_name` - (Required) The name of the ServiceBus Namespace to create this queue in. Changing this forces a new resource to be created. -* `location` - (Required) Specifies the supported Azure location where the resource exists. +* `location` - (Optional / **Deprecated**) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. * `resource_group_name` - (Required) The name of the resource group in which to