diff --git a/azurerm/config.go b/azurerm/config.go index f3d36f56992e..c923c0394bb9 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -141,6 +141,7 @@ type ArmClient struct { mysqlDatabasesClient mysql.DatabasesClient mysqlFirewallRulesClient mysql.FirewallRulesClient mysqlServersClient mysql.ServersClient + mysqlVirtualNetworkRulesClient mysql.VirtualNetworkRulesClient postgresqlConfigurationsClient postgresql.ConfigurationsClient postgresqlDatabasesClient postgresql.DatabasesClient postgresqlFirewallRulesClient postgresql.FirewallRulesClient @@ -602,6 +603,10 @@ func (c *ArmClient) registerDatabases(endpoint, subscriptionId string, auth auto c.configureClient(&mysqlServersClient.Client, auth) c.mysqlServersClient = mysqlServersClient + mysqlVirtualNetworkRulesClient := mysql.NewVirtualNetworkRulesClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&mysqlVirtualNetworkRulesClient.Client, auth) + c.mysqlVirtualNetworkRulesClient = mysqlVirtualNetworkRulesClient + // PostgreSQL postgresqlConfigClient := postgresql.NewConfigurationsClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&postgresqlConfigClient.Client, auth) diff --git a/azurerm/provider.go b/azurerm/provider.go index 52647239fdf2..342310160f3c 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -204,6 +204,7 @@ func Provider() terraform.ResourceProvider { "azurerm_mysql_database": resourceArmMySqlDatabase(), "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), "azurerm_mysql_server": resourceArmMySqlServer(), + "azurerm_mysql_virtual_network_rule": resourceArmMySqlVirtualNetworkRule(), "azurerm_network_interface": resourceArmNetworkInterface(), "azurerm_network_security_group": resourceArmNetworkSecurityGroup(), "azurerm_network_security_rule": resourceArmNetworkSecurityRule(), diff --git a/azurerm/resource_arm_mysql_virtual_network_rule.go b/azurerm/resource_arm_mysql_virtual_network_rule.go new file mode 100644 index 000000000000..14482108b8d9 --- /dev/null +++ b/azurerm/resource_arm_mysql_virtual_network_rule.go @@ -0,0 +1,228 @@ +package azurerm + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + "github.com/Azure/azure-sdk-for-go/services/mysql/mgmt/2017-12-01/mysql" + "github.com/hashicorp/terraform/helper/resource" + "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 resourceArmMySqlVirtualNetworkRule() *schema.Resource { + return &schema.Resource{ + Create: resourceArmMySqlVirtualNetworkRuleCreateUpdate, + Read: resourceArmMySqlVirtualNetworkRuleRead, + Update: resourceArmMySqlVirtualNetworkRuleCreateUpdate, + Delete: resourceArmMySqlVirtualNetworkRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.VirtualNetworkRuleName, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + + "subnet_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + } +} + +func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + serverName := d.Get("server_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + subnetId := d.Get("subnet_id").(string) + + // due to a bug in the API we have to ensure the Subnet's configured correctly or the API call will timeout + // BUG: https://github.com/Azure/azure-rest-api-specs/issues/3719 + subnetsClient := meta.(*ArmClient).subnetClient + subnetParsedId, err := parseAzureResourceID(subnetId) + + subnetResourceGroup := subnetParsedId.ResourceGroup + virtualNetwork := subnetParsedId.Path["virtualNetworks"] + subnetName := subnetParsedId.Path["subnets"] + subnet, err := subnetsClient.Get(ctx, subnetResourceGroup, virtualNetwork, subnetName, "") + if err != nil { + if utils.ResponseWasNotFound(subnet.Response) { + return fmt.Errorf("Subnet with ID %q was not found: %+v", subnetId, err) + } + + return fmt.Errorf("Error obtaining Subnet %q (Virtual Network %q / Resource Group %q: %+v", subnetName, virtualNetwork, subnetResourceGroup, err) + } + + containsEndpoint := false + if props := subnet.SubnetPropertiesFormat; props != nil { + if endpoints := props.ServiceEndpoints; endpoints != nil { + for _, e := range *endpoints { + if e.Service == nil { + continue + } + + if strings.EqualFold(*e.Service, "Microsoft.Sql") { + containsEndpoint = true + break + } + } + } + } + + if !containsEndpoint { + return fmt.Errorf("Error creating MySQL Virtual Network Rule: Subnet %q (Virtual Network %q / Resource Group %q) must contain a Service Endpoint for `Microsoft.Sql`", subnetName, virtualNetwork, subnetResourceGroup) + } + + parameters := mysql.VirtualNetworkRule{ + VirtualNetworkRuleProperties: &mysql.VirtualNetworkRuleProperties{ + VirtualNetworkSubnetID: utils.String(subnetId), + IgnoreMissingVnetServiceEndpoint: utils.Bool(false), + }, + } + + _, err = client.CreateOrUpdate(ctx, resourceGroup, serverName, name, parameters) + if err != nil { + return fmt.Errorf("Error creating MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + //Wait for the provisioning state to become ready + log.Printf("[DEBUG] Waiting for MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) to become ready: %+v", name, serverName, resourceGroup, err) + stateConf := &resource.StateChangeConf{ + Pending: []string{"Initializing", "InProgress", "Unknown", "ResponseNotFound"}, + Target: []string{"Ready"}, + Refresh: mySQLVirtualNetworkStateStatusCodeRefreshFunc(ctx, client, resourceGroup, serverName, name), + Timeout: 30 * time.Minute, + MinTimeout: 1 * time.Minute, + ContinuousTargetOccurence: 5, + } + + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) to be created or updated: %+v", name, serverName, resourceGroup, err) + } + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + return fmt.Errorf("Error retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + d.SetId(*resp.ID) + + return resourceArmMySqlVirtualNetworkRuleRead(d, meta) +} + +func resourceArmMySqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["virtualNetworkRules"] + + resp, err := client.Get(ctx, resourceGroup, serverName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Error reading MySQL Virtual Network Rule %q - removing from state", d.Id()) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading MySQL Virtual Network Rule: %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + d.Set("name", resp.Name) + d.Set("resource_group_name", resourceGroup) + d.Set("server_name", serverName) + + if props := resp.VirtualNetworkRuleProperties; props != nil { + d.Set("subnet_id", props.VirtualNetworkSubnetID) + } + + return nil +} + +func resourceArmMySqlVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + name := id.Path["virtualNetworkRules"] + + future, err := client.Delete(ctx, resourceGroup, serverName, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("Error waiting for deletion of MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + } + + return nil +} + +func mySQLVirtualNetworkStateStatusCodeRefreshFunc(ctx context.Context, client mysql.VirtualNetworkRulesClient, resourceGroup string, serverName string, name string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := client.Get(ctx, resourceGroup, serverName, name) + + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) returned 404.", resourceGroup, serverName, name) + return nil, "ResponseNotFound", nil + } + + return nil, "", fmt.Errorf("Error polling for the state of the MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q): %+v", name, serverName, resourceGroup, err) + } + + if props := resp.VirtualNetworkRuleProperties; props != nil { + log.Printf("[DEBUG] Retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) returned Status %s", resourceGroup, serverName, name, props.State) + return resp, fmt.Sprintf("%s", props.State), nil + } + + //Valid response was returned but VirtualNetworkRuleProperties was nil. Basically the rule exists, but with no properties for some reason. Assume Unknown instead of returning error. + log.Printf("[DEBUG] Retrieving MySQL Virtual Network Rule %q (MySQL Server: %q, Resource Group: %q) returned empty VirtualNetworkRuleProperties", resourceGroup, serverName, name) + return resp, "Unknown", nil + } +} diff --git a/azurerm/resource_arm_mysql_virtual_network_rule_test.go b/azurerm/resource_arm_mysql_virtual_network_rule_test.go new file mode 100644 index 000000000000..a52836db2ae8 --- /dev/null +++ b/azurerm/resource_arm_mysql_virtual_network_rule_test.go @@ -0,0 +1,476 @@ +package azurerm + +import ( + "fmt" + "regexp" + "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/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMMySqlVirtualNetworkRule_basic(t *testing.T) { + resourceName := "azurerm_mysql_virtual_network_rule.test" + ri := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMySqlVirtualNetworkRule_basic(ri, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMMySqlVirtualNetworkRule_switchSubnets(t *testing.T) { + resourceName := "azurerm_mysql_virtual_network_rule.test" + ri := acctest.RandInt() + + preConfig := testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPre(ri, testLocation()) + postConfig := testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPost(ri, testLocation()) + + // Create regex strings that will ensure that one subnet name exists, but not the other + preConfigRegex := regexp.MustCompile(fmt.Sprintf("(subnet1%d)$|(subnet[^2]%d)$", ri, ri)) //subnet 1 but not 2 + postConfigRegex := regexp.MustCompile(fmt.Sprintf("(subnet2%d)$|(subnet[^1]%d)$", ri, ri)) //subnet 2 but not 1 + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "subnet_id", preConfigRegex), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + resource.TestMatchResourceAttr(resourceName, "subnet_id", postConfigRegex), + ), + }, + }, + }) +} + +func TestAccAzureRMMySqlVirtualNetworkRule_disappears(t *testing.T) { + resourceName := "azurerm_mysql_virtual_network_rule.test" + ri := acctest.RandInt() + config := testAccAzureRMMySqlVirtualNetworkRule_basic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName), + testCheckAzureRMMySqlVirtualNetworkRuleDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(t *testing.T) { + resourceName1 := "azurerm_mysql_virtual_network_rule.rule1" + resourceName2 := "azurerm_mysql_virtual_network_rule.rule2" + resourceName3 := "azurerm_mysql_virtual_network_rule.rule3" + ri := acctest.RandInt() + config := testAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName1), + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName2), + testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName3), + ), + }, + }, + }) +} + +func testCheckAzureRMMySqlVirtualNetworkRuleExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: MySql Virtual Network Rule %q (Server %q / Resource Group %q) was not found", ruleName, serverName, resourceGroup) + } + + return err + } + + return nil + } +} + +func testCheckAzureRMMySqlVirtualNetworkRuleDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_mysql_virtual_network_rule" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, ruleName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("Bad: MySql Firewall Rule %q (Server %q / Resource Group %q) still exists: %+v", ruleName, serverName, resourceGroup, resp) + } + + return nil +} + +func testCheckAzureRMMySqlVirtualNetworkRuleDisappears(name 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[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + ruleName := rs.Primary.Attributes["name"] + + client := testAccProvider.Meta().(*ArmClient).mysqlVirtualNetworkRulesClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + future, err := client.Delete(ctx, resourceGroup, serverName, ruleName) + if err != nil { + //If the error is that the resource we want to delete does not exist in the first + //place (404), then just return with no error. + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting MySql Virtual Network Rule: %+v", err) + } + + err = future.WaitForCompletionRef(ctx, client.Client) + if err != nil { + //Same deal as before. Just in case. + if response.WasNotFound(future.Response()) { + return nil + } + + return fmt.Errorf("Error deleting MySql Virtual Network Rule: %+v", err) + } + + return nil + } +} + +func testAccAzureRMMySqlVirtualNetworkRule_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvnet%d" + address_space = ["10.7.29.0/29"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "acctestmysqlvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.test.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPre(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvnet%d" + address_space = ["10.7.29.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test1" { + name = "subnet1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "test2" { + name = "subnet2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.128/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "acctestmysqlvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.test1.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPost(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvnet%d" + address_space = ["10.7.29.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test1" { + name = "subnet1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "test2" { + name = "subnet2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.128/25" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "acctestmysqlvnetrule%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.test2.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "vnet1" { + name = "acctestvnet1%d" + address_space = ["10.7.29.0/24"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_virtual_network" "vnet2" { + name = "acctestvnet2%d" + address_space = ["10.1.29.0/29"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "vnet1_subnet1" { + name = "acctestsubnet1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.vnet1.name}" + address_prefix = "10.7.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "vnet1_subnet2" { + name = "acctestsubnet2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.vnet1.name}" + address_prefix = "10.7.29.128/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_subnet" "vnet2_subnet1" { + name = "acctestsubnet3%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.vnet2.name}" + address_prefix = "10.1.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "acctestmysqlsvr-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.6" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "rule1" { + name = "acctestmysqlvnetrule1%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.vnet1_subnet1.id}" +} + +resource "azurerm_mysql_virtual_network_rule" "rule2" { + name = "acctestmysqlvnetrule2%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.vnet1_subnet2.id}" +} + +resource "azurerm_mysql_virtual_network_rule" "rule3" { + name = "acctestmysqlvnetrule3%d" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.vnet2_subnet1.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index 339fa4c1a12f..89adb3e207ce 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -417,6 +417,10 @@ azurerm_mysql_server + > + azurerm_mysql_virtual_network_rule + + > azurerm_postgresql_configuration diff --git a/website/docs/r/mysql_virtual_network_rule.html.markdown b/website/docs/r/mysql_virtual_network_rule.html.markdown new file mode 100644 index 000000000000..f1d6ea24e48b --- /dev/null +++ b/website/docs/r/mysql_virtual_network_rule.html.markdown @@ -0,0 +1,100 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_mysql_virtual_network_rule" +sidebar_current: "docs-azurerm-resource-database-mysql-virtual-network-rule" +description: |- + Manages a MySQL Virtual Network Rule. +--- + +# azurerm_mysql_virtual_network_rule + +Manages a MySQL Virtual Network Rule. + +-> **NOTE:** MySQL Virtual Network Rules [can only be used with SKU Tiers of `GeneralPurpose` or `MemoryOptimized`](https://docs.microsoft.com/en-us/azure/mysql/concepts-data-access-and-security-vnet) + +## Example Usage + +```hcl +resource "azurerm_resource_group" "test" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_virtual_network" "test" { + name = "example-vnet" + address_space = ["10.7.29.0/29"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "internal" { + name = "internal" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.7.29.0/29" + service_endpoints = ["Microsoft.Sql"] +} + +resource "azurerm_mysql_server" "test" { + name = "mysql-server-1" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + administrator_login = "mysqladminun" + administrator_login_password = "H@Sh1CoR3!" + version = "5.7" + ssl_enforcement = "Enabled" + + sku { + name = "GP_Gen5_2" + capacity = 2 + tier = "GeneralPurpose" + family = "Gen5" + } + + storage_profile { + storage_mb = 5120 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } +} + +resource "azurerm_mysql_virtual_network_rule" "test" { + name = "mysql-vnet-rule" + resource_group_name = "${azurerm_resource_group.test.name}" + server_name = "${azurerm_mysql_server.test.name}" + subnet_id = "${azurerm_subnet.internal.id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the MySQL Virtual Network Rule. Cannot be empty and must only contain alphanumeric characters and hyphens. Cannot start with a number, and cannot start or end with a hyphen. Changing this forces a new resource to be created. + +~> **NOTE:** `name` must be between 1-128 characters long and must satisfy all of the requirements below: +1. Contains only alphanumeric and hyphen characters +2. Cannot start with a number or hyphen +3. Cannot end with a hyphen + +* `resource_group_name` - (Required) The name of the resource group where the MySQL server resides. Changing this forces a new resource to be created. + +* `server_name` - (Required) The name of the SQL Server to which this MySQL virtual network rule will be applied to. Changing this forces a new resource to be created. + +* `subnet_id` - (Required) The ID of the subnet that the MySQL server will be connected to. + +~> **NOTE:** Due to [a bug in the Azure API](https://github.com/Azure/azure-rest-api-specs/issues/3719) this resource currently doesn't expose the `ignore_missing_vnet_service_endpoint` field and defaults this to `false`. Terraform will check during the provisioning of the Virtual Network Rule that the Subnet contains the Service Rule to verify that the Virtual Network Rule can be created. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the MySQL Virtual Network Rule. + +## Import + +MySQL Virtual Network Rules can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_mysql_virtual_network_rule.rule1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.DBforMySQL/servers/myserver/virtualNetworkRules/vnetrulename +``` diff --git a/website/docs/r/postgresql_virtual_network_rule.html.markdown b/website/docs/r/postgresql_virtual_network_rule.html.markdown index 79de06acdd9b..026e4f7af8d2 100644 --- a/website/docs/r/postgresql_virtual_network_rule.html.markdown +++ b/website/docs/r/postgresql_virtual_network_rule.html.markdown @@ -10,6 +10,8 @@ description: |- Manages a PostgreSQL Virtual Network Rule. +-> **NOTE:** PostgreSQL Virtual Network Rules [can only be used with SKU Tiers of `GeneralPurpose` or `MemoryOptimized`](https://docs.microsoft.com/en-us/azure/postgresql/concepts-data-access-and-security-vnet) + ## Example Usage ```hcl @@ -39,16 +41,16 @@ resource "azurerm_postgresql_server" "test" { resource_group_name = "${azurerm_resource_group.test.name}" sku { - name = "B_Gen4_2" + name = "GP_Gen5_2" capacity = 2 - tier = "Basic" - family = "Gen4" + tier = "GeneralPurpose" + family = "Gen5" } storage_profile { - storage_mb = 5120 + storage_mb = 5120 backup_retention_days = 7 - geo_redundant_backup = "Disabled" + geo_redundant_backup = "Disabled" } administrator_login = "psqladminun"