diff --git a/azurerm/config.go b/azurerm/config.go index 4688ebc5911b..e31fbf1fb47f 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -113,6 +113,7 @@ type ArmClient struct { keyVaultClient keyvault.VaultsClient sqlElasticPoolsClient sql.ElasticPoolsClient + sqlServersClient sql.ServersClient appInsightsClient appinsights.ComponentsClient @@ -526,6 +527,12 @@ func (c *Config) getArmClient() (*ArmClient, error) { sqlepc.Sender = autorest.CreateSender(withRequestLogging()) client.sqlElasticPoolsClient = sqlepc + sqlsrv := sql.NewServersClientWithBaseURI(endpoint, c.SubscriptionID) + setUserAgent(&sqlsrv.Client) + sqlsrv.Authorizer = auth + sqlsrv.Sender = autorest.CreateSender(withRequestLogging()) + client.sqlServersClient = sqlsrv + ai := appinsights.NewComponentsClientWithBaseURI(endpoint, c.SubscriptionID) setUserAgent(&ai.Client) ai.Authorizer = auth diff --git a/azurerm/resource_arm_sql_server.go b/azurerm/resource_arm_sql_server.go index 8bf4aba0ff82..a7d18d46fcb2 100644 --- a/azurerm/resource_arm_sql_server.go +++ b/azurerm/resource_arm_sql_server.go @@ -2,25 +2,24 @@ package azurerm import ( "fmt" - "log" + "github.com/Azure/azure-sdk-for-go/arm/sql" "github.com/hashicorp/terraform/helper/schema" - "github.com/jen20/riviera/azure" - "github.com/jen20/riviera/sql" + "github.com/hashicorp/terraform/helper/validation" ) func resourceArmSqlServer() *schema.Resource { return &schema.Resource{ - Create: resourceArmSqlServerCreate, + Create: resourceArmSqlServerCreateUpdate, Read: resourceArmSqlServerRead, - Update: resourceArmSqlServerCreate, + Update: resourceArmSqlServerCreateUpdate, Delete: resourceArmSqlServerDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ + "name": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -28,29 +27,35 @@ func resourceArmSqlServer() *schema.Resource { "location": locationSchema(), - "resource_group_name": &schema.Schema{ + "resource_group_name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "version": &schema.Schema{ + "version": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(sql.TwoFullStopZero), + string(sql.OneTwoFullStopZero), + }, true), + // TODO: is this ForceNew? }, - "administrator_login": &schema.Schema{ + "administrator_login": { Type: schema.TypeString, Required: true, + ForceNew: true, }, - "administrator_login_password": &schema.Schema{ + "administrator_login_password": { Type: schema.TypeString, Required: true, Sensitive: true, }, - "fully_qualified_domain_name": &schema.Schema{ + "fully_qualified_domain_name": { Type: schema.TypeString, Computed: true, }, @@ -60,101 +65,101 @@ func resourceArmSqlServer() *schema.Resource { } } -func resourceArmSqlServerCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient) - rivieraClient := client.rivieraClient +func resourceArmSqlServerCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).sqlServersClient + + name := d.Get("name").(string) + resGroup := d.Get("resource_group_name").(string) + location := d.Get("location").(string) + adminUsername := d.Get("administrator_login").(string) + adminPassword := d.Get("administrator_login_password").(string) + version := d.Get("version").(string) tags := d.Get("tags").(map[string]interface{}) - expandedTags := expandTags(tags) - - createRequest := rivieraClient.NewRequest() - createRequest.Command = &sql.CreateOrUpdateServer{ - Name: d.Get("name").(string), - Location: d.Get("location").(string), - ResourceGroupName: d.Get("resource_group_name").(string), - AdministratorLogin: azure.String(d.Get("administrator_login").(string)), - AdministratorLoginPassword: azure.String(d.Get("administrator_login_password").(string)), - Version: azure.String(d.Get("version").(string)), - Tags: *expandedTags, + metadata := expandTags(tags) + + parameters := sql.Server{ + Location: &location, + Tags: metadata, + ServerProperties: &sql.ServerProperties{ + Version: sql.ServerVersion(version), + AdministratorLogin: &adminUsername, + AdministratorLoginPassword: &adminPassword, + }, } - createResponse, err := createRequest.Execute() + response, err := client.CreateOrUpdate(resGroup, name, parameters) if err != nil { - return fmt.Errorf("Error creating SQL Server: %s", err) - } - if !createResponse.IsSuccessful() { - return fmt.Errorf("Error creating SQL Server: %s", createResponse.Error) - } + // if the name is in-use, Azure returns a 409 "Unknown Service Error" which is a bad UX + if responseWasConflict(response.Response) { + return fmt.Errorf("SQL Server names need to be globally unique and '%s' is already in use.", name) + } - readRequest := rivieraClient.NewRequest() - readRequest.Command = &sql.GetServer{ - Name: d.Get("name").(string), - ResourceGroupName: d.Get("resource_group_name").(string), + return err } - readResponse, err := readRequest.Execute() - if err != nil { - return fmt.Errorf("Error reading SQL Server: %s", err) - } - if !readResponse.IsSuccessful() { - return fmt.Errorf("Error reading SQL Server: %s", readResponse.Error) + if response.ID == nil { + return fmt.Errorf("Cannot create SQL Server %s (resource group %s) ID", name, resGroup) } - resp := readResponse.Parsed.(*sql.GetServerResponse) - d.SetId(*resp.ID) + d.SetId(*response.ID) return resourceArmSqlServerRead(d, meta) } func resourceArmSqlServerRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient) - rivieraClient := client.rivieraClient + client := meta.(*ArmClient).sqlServersClient id, err := parseAzureResourceID(d.Id()) if err != nil { return err } - readRequest := rivieraClient.NewRequestForURI(d.Id()) - readRequest.Command = &sql.GetServer{} + resGroup := id.ResourceGroup + name := id.Path["servers"] - readResponse, err := readRequest.Execute() + result, err := client.Get(resGroup, name) if err != nil { - return fmt.Errorf("Error reading SQL Server: %s", err) - } - if !readResponse.IsSuccessful() { - log.Printf("[INFO] Error reading SQL Server %q - removing from state", d.Id()) - d.SetId("") - return fmt.Errorf("Error reading SQL Server: %s", readResponse.Error) + if responseWasNotFound(result.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error reading SQL Server %s: %v", name, err) } - resp := readResponse.Parsed.(*sql.GetServerResponse) + d.Set("name", name) + d.Set("resource_group_name", resGroup) + d.Set("location", azureRMNormalizeLocation(*result.Location)) - d.Set("name", id.Path["servers"]) - d.Set("resource_group_name", id.ResourceGroup) - d.Set("location", azureRMNormalizeLocation(*resp.Location)) - d.Set("fully_qualified_domain_name", resp.FullyQualifiedDomainName) - d.Set("administrator_login", resp.AdministratorLogin) - d.Set("version", resp.Version) + if serverProperties := result.ServerProperties; serverProperties != nil { + d.Set("version", string(serverProperties.Version)) + d.Set("administrator_login", serverProperties.AdministratorLogin) + d.Set("fully_qualified_domain_name", serverProperties.FullyQualifiedDomainName) + } - flattenAndSetTags(d, resp.Tags) + flattenAndSetTags(d, result.Tags) return nil } func resourceArmSqlServerDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient) - rivieraClient := client.rivieraClient - - deleteRequest := rivieraClient.NewRequestForURI(d.Id()) - deleteRequest.Command = &sql.DeleteServer{} + client := meta.(*ArmClient).sqlServersClient - deleteResponse, err := deleteRequest.Execute() + id, err := parseAzureResourceID(d.Id()) if err != nil { - return fmt.Errorf("Error deleting SQL Server: %s", err) + return err } - if !deleteResponse.IsSuccessful() { - return fmt.Errorf("Error deleting SQL Server: %s", deleteResponse.Error) + + resGroup := id.ResourceGroup + name := id.Path["servers"] + + response, err := client.Delete(resGroup, name) + if err != nil { + if responseWasNotFound(response) { + return nil + } + + return fmt.Errorf("Error deleting SQL Server %s: %+v", name, err) } return nil diff --git a/azurerm/resource_arm_sql_server_test.go b/azurerm/resource_arm_sql_server_test.go index 02b78d6b1b8a..eb681fff739b 100644 --- a/azurerm/resource_arm_sql_server_test.go +++ b/azurerm/resource_arm_sql_server_test.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" - "github.com/jen20/riviera/sql" ) func TestAccAzureRMSqlServer_basic(t *testing.T) { @@ -61,23 +60,25 @@ func TestAccAzureRMSqlServer_withTags(t *testing.T) { func testCheckAzureRMSqlServerExists(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) } - conn := testAccProvider.Meta().(*ArmClient).rivieraClient - - readRequest := conn.NewRequestForURI(rs.Primary.ID) - readRequest.Command = &sql.GetServer{} + sqlServerName := 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 SQL Server: %s", sqlServerName) + } - readResponse, err := readRequest.Execute() + conn := testAccProvider.Meta().(*ArmClient).sqlServersClient + resp, err := conn.Get(resourceGroup, sqlServerName) if err != nil { - return fmt.Errorf("Bad: GetServer: %+v", err) - } - if !readResponse.IsSuccessful() { - return fmt.Errorf("Bad: GetServer: %+v", readResponse.Error) + if responseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: SQL Server %s (resource group: %s) does not exist", sqlServerName, resourceGroup) + } + return fmt.Errorf("Bad: Get SQL Server: %v", err) } return nil @@ -85,24 +86,28 @@ func testCheckAzureRMSqlServerExists(name string) resource.TestCheckFunc { } func testCheckAzureRMSqlServerDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).rivieraClient + conn := testAccProvider.Meta().(*ArmClient).sqlServersClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_sql_server" { continue } - readRequest := conn.NewRequestForURI(rs.Primary.ID) - readRequest.Command = &sql.GetServer{} + sqlServerName := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := conn.Get(resourceGroup, sqlServerName) - readResponse, err := readRequest.Execute() if err != nil { - return fmt.Errorf("Bad: GetServer: % +v", err) - } + if responseWasNotFound(resp.Response) { + return nil + } - if readResponse.IsSuccessful() { - return fmt.Errorf("Bad: SQL Server still exists: %+v", readResponse.Error) + return fmt.Errorf("Bad: Get Server: %+v", err) } + + return fmt.Errorf("SQL Server %s still exists", sqlServerName) + } return nil diff --git a/azurerm/response.go b/azurerm/response.go index e3b01100b50b..bd0cc5da6ae3 100644 --- a/azurerm/response.go +++ b/azurerm/response.go @@ -6,9 +6,17 @@ import ( "github.com/Azure/go-autorest/autorest" ) +func responseWasConflict(resp autorest.Response) bool { + return responseWasStatusCode(resp, http.StatusConflict) +} + func responseWasNotFound(resp autorest.Response) bool { + return responseWasStatusCode(resp, http.StatusNotFound) +} + +func responseWasStatusCode(resp autorest.Response, statusCode int) bool { if r := resp.Response; r != nil { - if r.StatusCode == http.StatusNotFound { + if r.StatusCode == statusCode { return true } } diff --git a/website/docs/r/sql_server.html.markdown b/website/docs/r/sql_server.html.markdown index 8af90f9dc67b..b1d791a52035 100644 --- a/website/docs/r/sql_server.html.markdown +++ b/website/docs/r/sql_server.html.markdown @@ -3,12 +3,13 @@ layout: "azurerm" page_title: "Azure Resource Manager: azurerm_sql_server" sidebar_current: "docs-azurerm-resource-sql-server" description: |- - Create a SQL Server. + Manages a SQL Azure Database Server. + --- # azurerm\_sql\_server -Allows you to manage an Azure SQL Database Server +Manages a SQL Azure Database Server. ~> **Note:** All arguments including the administrator login and password will be stored in the raw state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). @@ -17,14 +18,14 @@ Allows you to manage an Azure SQL Database Server ```hcl resource "azurerm_resource_group" "test" { - name = "acceptanceTestResourceGroup1" + name = "database-rg" location = "West US" } resource "azurerm_sql_server" "test" { name = "mysqlserver" resource_group_name = "${azurerm_resource_group.test.name}" - location = "West US" + location = "${azurerm_resource_group.test.location}" version = "12.0" administrator_login = "mradministrator" administrator_login_password = "thisIsDog11" @@ -38,18 +39,17 @@ resource "azurerm_sql_server" "test" { The following arguments are supported: -* `name` - (Required) The name of the SQL Server. +* `name` - (Required) The name of the SQL Server. This needs to be globally unique within Azure. -* `resource_group_name` - (Required) The name of the resource group in which to - create the sql server. +* `resource_group_name` - (Required) The name of the resource group in which to create the SQL Server. * `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. * `version` - (Required) The version for the new server. Valid values are: 2.0 (for v11 server) and 12.0 (for v12 server). -* `administrator_login` - (Required) The administrator login name for the new server. +* `administrator_login` - (Required) The administrator login name for the new server. Changing this forces a new resource to be created. -* `administrator_login_password` - (Required) The password for the new AdministratorLogin. Please following Azures [Password Policy](https://msdn.microsoft.com/library/ms161959.aspx) +* `administrator_login_password` - (Required) The password associated with the `administrator_login` user. Needs to comply with Azure's [Password Policy](https://msdn.microsoft.com/library/ms161959.aspx) * `tags` - (Optional) A mapping of tags to assign to the resource.