diff --git a/azurerm/config.go b/azurerm/config.go index 69e84c52b2e6..82fa2a54fb62 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -112,18 +112,19 @@ type ArmClient struct { vmClient compute.VirtualMachinesClient // Databases - mysqlConfigurationsClient mysql.ConfigurationsClient - mysqlDatabasesClient mysql.DatabasesClient - mysqlFirewallRulesClient mysql.FirewallRulesClient - mysqlServersClient mysql.ServersClient - postgresqlConfigurationsClient postgresql.ConfigurationsClient - postgresqlDatabasesClient postgresql.DatabasesClient - postgresqlFirewallRulesClient postgresql.FirewallRulesClient - postgresqlServersClient postgresql.ServersClient - sqlDatabasesClient sql.DatabasesClient - sqlElasticPoolsClient sql.ElasticPoolsClient - sqlFirewallRulesClient sql.FirewallRulesClient - sqlServersClient sql.ServersClient + mysqlConfigurationsClient mysql.ConfigurationsClient + mysqlDatabasesClient mysql.DatabasesClient + mysqlFirewallRulesClient mysql.FirewallRulesClient + mysqlServersClient mysql.ServersClient + postgresqlConfigurationsClient postgresql.ConfigurationsClient + postgresqlDatabasesClient postgresql.DatabasesClient + postgresqlFirewallRulesClient postgresql.FirewallRulesClient + postgresqlServersClient postgresql.ServersClient + sqlDatabasesClient sql.DatabasesClient + sqlElasticPoolsClient sql.ElasticPoolsClient + sqlFirewallRulesClient sql.FirewallRulesClient + sqlServersClient sql.ServersClient + sqlServerAzureADAdministratorsClient sql.ServerAzureADAdministratorsClient // KeyVault keyVaultClient keyvault.VaultsClient @@ -586,6 +587,13 @@ func (c *ArmClient) registerDatabases(endpoint, subscriptionId string, auth auto sqlSrvClient.Sender = sender sqlSrvClient.SkipResourceProviderRegistration = c.skipProviderRegistration c.sqlServersClient = sqlSrvClient + + sqlADClient := sql.NewServerAzureADAdministratorsClientWithBaseURI(endpoint, subscriptionId) + setUserAgent(&sqlADClient.Client) + sqlADClient.Authorizer = auth + sqlADClient.Sender = sender + sqlADClient.SkipResourceProviderRegistration = c.skipProviderRegistration + c.sqlServerAzureADAdministratorsClient = sqlADClient } func (c *ArmClient) registerDNSClients(endpoint, subscriptionId string, auth autorest.Authorizer, sender autorest.Sender) { diff --git a/azurerm/import_arm_sql_administrator_test.go b/azurerm/import_arm_sql_administrator_test.go new file mode 100644 index 000000000000..ede826f63788 --- /dev/null +++ b/azurerm/import_arm_sql_administrator_test.go @@ -0,0 +1,31 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMSqlAdministrator_importBasic(t *testing.T) { + resourceName := "azurerm_sql_active_directory_administrator.test" + + ri := acctest.RandInt() + config := testAccAzureRMSqlAdministrator_basic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSqlAdministratorDestroy, + Steps: []resource.TestStep{ + { + Config: config, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 9bc8e0c49480..f4424deaa787 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -181,6 +181,7 @@ func Provider() terraform.ResourceProvider { "azurerm_sql_database": resourceArmSqlDatabase(), "azurerm_sql_elasticpool": resourceArmSqlElasticPool(), "azurerm_sql_firewall_rule": resourceArmSqlFirewallRule(), + "azurerm_sql_active_directory_administrator": resourceArmSqlAdministrator(), "azurerm_sql_server": resourceArmSqlServer(), "azurerm_storage_account": resourceArmStorageAccount(), "azurerm_storage_blob": resourceArmStorageBlob(), diff --git a/azurerm/resource_arm_sql_administrator.go b/azurerm/resource_arm_sql_administrator.go new file mode 100644 index 000000000000..923ce5d9fe2a --- /dev/null +++ b/azurerm/resource_arm_sql_administrator.go @@ -0,0 +1,143 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/Azure/azure-sdk-for-go/services/sql/mgmt/2015-05-01-preview/sql" + "github.com/hashicorp/terraform/helper/schema" + uuid "github.com/satori/go.uuid" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmSqlAdministrator() *schema.Resource { + return &schema.Resource{ + Create: resourceArmSqlActiveDirectoryAdministratorCreateUpdate, + Read: resourceArmSqlActiveDirectoryAdministratorRead, + Update: resourceArmSqlActiveDirectoryAdministratorCreateUpdate, + Delete: resourceArmSqlActiveDirectoryAdministratorDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "login": { + Type: schema.TypeString, + Required: true, + }, + + "object_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateUUID, + }, + + "tenant_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateUUID, + }, + }, + } +} + +func resourceArmSqlActiveDirectoryAdministratorCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).sqlServerAzureADAdministratorsClient + ctx := meta.(*ArmClient).StopContext + + serverName := d.Get("server_name").(string) + resGroup := d.Get("resource_group_name").(string) + administratorName := "activeDirectory" + login := d.Get("login").(string) + objectId := uuid.FromStringOrNil(d.Get("object_id").(string)) + tenantId := uuid.FromStringOrNil(d.Get("tenant_id").(string)) + parameters := sql.ServerAzureADAdministrator{ + ServerAdministratorProperties: &sql.ServerAdministratorProperties{ + AdministratorType: utils.String("ActiveDirectory"), + Login: utils.String(login), + Sid: &objectId, + TenantID: &tenantId, + }, + } + + future, err := client.CreateOrUpdate(ctx, resGroup, serverName, administratorName, parameters) + if err != nil { + return err + } + + err = future.WaitForCompletion(ctx, client.Client) + if err != nil { + return err + } + + resp, err := client.Get(ctx, resGroup, serverName, administratorName) + if err != nil { + return err + } + + d.SetId(*resp.ID) + + return nil +} + +func resourceArmSqlActiveDirectoryAdministratorRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).sqlServerAzureADAdministratorsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + administratorName := id.Path["administrators"] + + resp, err := client.Get(ctx, resourceGroup, serverName, administratorName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Error reading SQL AD administrator %q - removing from state", d.Id()) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading SQL AD administrator: %+v", err) + } + + d.Set("resource_group_name", resourceGroup) + d.Set("server_name", serverName) + d.Set("login", resp.Login) + d.Set("object_id", resp.Sid.String()) + d.Set("tenant_id", resp.TenantID.String()) + + return nil +} + +func resourceArmSqlActiveDirectoryAdministratorDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).sqlServerAzureADAdministratorsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + administratorName := id.Path["administrators"] + + _, err = client.Delete(ctx, resourceGroup, serverName, administratorName) + if err != nil { + return fmt.Errorf("Error deleting SQL AD administrator: %+v", err) + } + + return nil +} diff --git a/azurerm/resource_arm_sql_administrator_test.go b/azurerm/resource_arm_sql_administrator_test.go new file mode 100644 index 000000000000..f4e67d32b5e7 --- /dev/null +++ b/azurerm/resource_arm_sql_administrator_test.go @@ -0,0 +1,189 @@ +package azurerm + +import ( + "fmt" + "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/utils" +) + +func TestAccAzureRMSqlAdministrator_basic(t *testing.T) { + resourceName := "azurerm_sql_active_directory_administrator.test" + ri := acctest.RandInt() + preConfig := testAccAzureRMSqlAdministrator_basic(ri, testLocation()) + postConfig := testAccAzureRMSqlAdministrator_withUpdates(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSqlAdministratorDestroy, + Steps: []resource.TestStep{ + { + Config: preConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlAdministratorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "login", "sqladmin"), + ), + }, + { + Config: postConfig, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlAdministratorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "login", "sqladmin2"), + ), + }, + }, + }) +} + +func TestAccAzureRMSqlAdministrator_disappears(t *testing.T) { + resourceName := "azurerm_sql_active_directory_administrator.test" + ri := acctest.RandInt() + config := testAccAzureRMSqlAdministrator_basic(ri, testLocation()) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMSqlAdministratorDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSqlAdministratorExists(resourceName), + testCheckAzureRMSqlAdministratorDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testCheckAzureRMSqlAdministratorExists(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"] + + client := testAccProvider.Meta().(*ArmClient).sqlServerAzureADAdministratorsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + _, err := client.Get(ctx, resourceGroup, serverName, "activeDirectory") + if err != nil { + return err + } + + return nil + } +} + +func testCheckAzureRMSqlAdministratorDisappears(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"] + + client := testAccProvider.Meta().(*ArmClient).sqlServerAzureADAdministratorsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + _, err := client.Delete(ctx, resourceGroup, serverName, "activeDirectory") + if err != nil { + return fmt.Errorf("Bad: Delete on sqlAdministratorClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMSqlAdministratorDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_sql_active_directory_administrator" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + + client := testAccProvider.Meta().(*ArmClient).sqlServerAzureADAdministratorsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, serverName, "activeDirectory") + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("SQL Administrator (server %q / resource group %q) still exists: %+v", serverName, resourceGroup, resp) + } + + return nil +} + +func testAccAzureRMSqlAdministrator_basic(rInt int, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG_%d" + location = "%s" +} + +resource "azurerm_sql_server" "test" { + name = "acctestsqlserver%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_active_directory_administrator" "test" { + server_name = "${azurerm_sql_server.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + login = "sqladmin" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.client_id}" +} +`, rInt, location, rInt) +} + +func testAccAzureRMSqlAdministrator_withUpdates(rInt int, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG_%d" + location = "%s" +} + +resource "azurerm_sql_server" "test" { + name = "acctestsqlserver%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_active_directory_administrator" "test" { + server_name = "${azurerm_sql_server.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + login = "sqladmin2" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.client_id}" +} +`, rInt, location, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index cdb56ffd271f..d66949c30f3e 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -313,6 +313,10 @@ azurerm_sql_database + > + azurerm_sql_active_directory_administrator + + > azurerm_sql_elasticpool diff --git a/website/docs/r/sql_active_directory_administrator.markdown b/website/docs/r/sql_active_directory_administrator.markdown new file mode 100644 index 000000000000..426e1506c9c7 --- /dev/null +++ b/website/docs/r/sql_active_directory_administrator.markdown @@ -0,0 +1,67 @@ +--- +layout: "azurerm" +page_title: "Azure Resource manager: azurerm_sql_active_directory_administrator" +sidebar_current: "docs-azurerm-resource-database-sql-administrator" +description: |- + Manages an Active Directory administrator on a SQL server +--- + +# azurerm_sql_active_directory_administrator + +Allows you to set a user or group as the AD administrator for an Azure SQL server + +## Example Usage + +```hcl +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acceptanceTestResourceGroup1" + location = "West US" +} + +resource "azurerm_sql_server" "test" { + name = "mysqlserver" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + version = "12.0" + administrator_login = "4dm1n157r470r" + administrator_login_password = "4-v3ry-53cr37-p455w0rd" +} + +resource "azurerm_sql_active_directory_administrator" "test" { + server_name = "${azurerm_sql_server.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + login = "sqladmin" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `server_name` - (Required) The name of the SQL Server on which to set the administrator. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the resource group for the SQL server. Changing this forces a new resource to be created. + +* `login` - (Required) The login name of the principal to set as the server administrator + +* `object_id` - (Required) The ID of the principal to set as the server administrator + +* `tenant_id` - (Required) The Azure Tenant ID + +## Attributes Reference + +The following attributes are exported: + +* `id` - The SQL Active Directory Administrator ID. + +## Import + +A SQL Active Directory Administrator can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_sql_active_directory_administrator.administrator /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Sql/servers/myserver/administrators/activeDirectory +```