diff --git a/azurerm/internal/services/postgres/client/client.go b/azurerm/internal/services/postgres/client/client.go index 0dd71d9cc304..879ea03a557e 100644 --- a/azurerm/internal/services/postgres/client/client.go +++ b/azurerm/internal/services/postgres/client/client.go @@ -12,6 +12,7 @@ type Client struct { ServersClient *postgresql.ServersClient ServerSecurityAlertPoliciesClient *postgresql.ServerSecurityAlertPoliciesClient VirtualNetworkRulesClient *postgresql.VirtualNetworkRulesClient + ServerAdministratorsClient *postgresql.ServerAdministratorsClient } func NewClient(o *common.ClientOptions) *Client { @@ -33,6 +34,9 @@ func NewClient(o *common.ClientOptions) *Client { virtualNetworkRulesClient := postgresql.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&virtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer) + serverAdministratorsClient := postgresql.NewServerAdministratorsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&serverAdministratorsClient.Client, o.ResourceManagerAuthorizer) + return &Client{ ConfigurationsClient: &configurationsClient, DatabasesClient: &databasesClient, @@ -40,5 +44,6 @@ func NewClient(o *common.ClientOptions) *Client { ServersClient: &serversClient, ServerSecurityAlertPoliciesClient: &serverSecurityAlertPoliciesClient, VirtualNetworkRulesClient: &virtualNetworkRulesClient, + ServerAdministratorsClient: &serverAdministratorsClient, } } diff --git a/azurerm/internal/services/postgres/postgresql_aad_administrator_resource.go b/azurerm/internal/services/postgres/postgresql_aad_administrator_resource.go new file mode 100644 index 000000000000..84cef6159a4b --- /dev/null +++ b/azurerm/internal/services/postgres/postgresql_aad_administrator_resource.go @@ -0,0 +1,170 @@ +package postgres + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + uuid "github.com/satori/go.uuid" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmPostgreSQLAdministrator() *schema.Resource { + return &schema.Resource{ + Create: resourceArmPostgreSQLAdministratorCreateUpdate, + Read: resourceArmPostgreSQLAdministratorRead, + Update: resourceArmPostgreSQLAdministratorCreateUpdate, + Delete: resourceArmPostgreSQLAdministratorDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "server_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": azure.SchemaResourceGroupName(), + + "login": { + Type: schema.TypeString, + Required: true, + }, + + "object_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsUUID, + }, + + "tenant_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsUUID, + }, + }, + } +} + +func resourceArmPostgreSQLAdministratorCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Postgres.ServerAdministratorsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + serverName := d.Get("server_name").(string) + resGroup := d.Get("resource_group_name").(string) + login := d.Get("login").(string) + objectId := uuid.FromStringOrNil(d.Get("object_id").(string)) + tenantId := uuid.FromStringOrNil(d.Get("tenant_id").(string)) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resGroup, serverName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing PostgreSQL AD Administrator (Resource Group %q, Server %q): %+v", resGroup, serverName, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_postgresql_active_directory_administrator", *existing.ID) + } + } + + parameters := postgresql.ServerAdministratorResource{ + ServerAdministratorProperties: &postgresql.ServerAdministratorProperties{ + AdministratorType: utils.String("ActiveDirectory"), + Login: utils.String(login), + Sid: &objectId, + TenantID: &tenantId, + }, + } + + future, err := client.CreateOrUpdate(ctx, resGroup, serverName, parameters) + if err != nil { + return fmt.Errorf("Error issuing create/update request for PostgreSQL AD Administrator (Resource Group %q, Server %q): %+v", resGroup, serverName, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting on create/update future for PostgreSQL AD Administrator (Resource Group %q, Server %q): %+v", resGroup, serverName, err) + } + + resp, err := client.Get(ctx, resGroup, serverName) + if err != nil { + return fmt.Errorf("Error issuing get request for PostgreSQL AD Administrator (Resource Group %q, Server %q): %+v", resGroup, serverName, err) + } + + d.SetId(*resp.ID) + + return nil +} + +func resourceArmPostgreSQLAdministratorRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Postgres.ServerAdministratorsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + + resp, err := client.Get(ctx, resourceGroup, serverName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Error reading PostgreSQL AD administrator %q - removing from state", d.Id()) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading PostgreSQL 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 resourceArmPostgreSQLAdministratorDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Postgres.ServerAdministratorsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + serverName := id.Path["servers"] + + _, err = client.Delete(ctx, resourceGroup, serverName) + if err != nil { + return fmt.Errorf("Error deleting PostgreSQL AD Administrator: %+v", err) + } + + return nil +} diff --git a/azurerm/internal/services/postgres/registration.go b/azurerm/internal/services/postgres/registration.go index 043f32059ebb..6fbc5c410585 100644 --- a/azurerm/internal/services/postgres/registration.go +++ b/azurerm/internal/services/postgres/registration.go @@ -28,10 +28,11 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), - "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), - "azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(), - "azurerm_postgresql_server": resourceArmPostgreSQLServer(), - "azurerm_postgresql_virtual_network_rule": resourceArmPostgreSQLVirtualNetworkRule(), + "azurerm_postgresql_configuration": resourceArmPostgreSQLConfiguration(), + "azurerm_postgresql_database": resourceArmPostgreSQLDatabase(), + "azurerm_postgresql_firewall_rule": resourceArmPostgreSQLFirewallRule(), + "azurerm_postgresql_server": resourceArmPostgreSQLServer(), + "azurerm_postgresql_virtual_network_rule": resourceArmPostgreSQLVirtualNetworkRule(), + "azurerm_postgresql_active_directory_administrator": resourceArmPostgreSQLAdministrator(), } } diff --git a/azurerm/internal/services/postgres/tests/postgresql_administrator_resource_test.go b/azurerm/internal/services/postgres/tests/postgresql_administrator_resource_test.go new file mode 100644 index 000000000000..5b0d2a9699b4 --- /dev/null +++ b/azurerm/internal/services/postgres/tests/postgresql_administrator_resource_test.go @@ -0,0 +1,247 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzurePostgreSqlAdministrator_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_active_directory_administrator", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzurePostgreSqlAdministratorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzurePostgreSqlAdministrator_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzurePostgreSqlAdministratorExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "login", "sqladmin"), + ), + }, + data.ImportStep(), + { + Config: testAccAzurePostgreSqlAdministrator_withUpdates(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzurePostgreSqlAdministratorExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "login", "sqladmin2"), + ), + }, + }, + }) +} +func TestAccAzurePostgreSqlAdministrator_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_active_directory_administrator", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzurePostgreSqlAdministratorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzurePostgreSqlAdministrator_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzurePostgreSqlAdministratorExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "login", "sqladmin"), + ), + }, + { + Config: testAccAzurePostgreSqlAdministrator_requiresImport(data), + ExpectError: acceptance.RequiresImportError("azurerm_postgresql_active_directory_administrator"), + }, + }, + }) +} + +func TestAccAzurePostgreSqlAdministrator_disappears(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_active_directory_administrator", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzurePostgreSqlAdministratorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzurePostgreSqlAdministrator_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzurePostgreSqlAdministratorExists(data.ResourceName), + testCheckAzurePostgreSqlAdministratorDisappears(data.ResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testCheckAzurePostgreSqlAdministratorExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Postgres.ServerAdministratorsClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + + _, err := client.Get(ctx, resourceGroup, serverName) + return err + } +} + +func testCheckAzurePostgreSqlAdministratorDisappears(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Postgres.ServerAdministratorsClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + + if _, err := client.Delete(ctx, resourceGroup, serverName); err != nil { + return fmt.Errorf("Bad: Delete on postgresAdministratorClient: %+v", err) + } + + return nil + } +} + +func testCheckAzurePostgreSqlAdministratorDestroy(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).Postgres.ServerAdministratorsClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_postgresql_active_directory_administrator" { + continue + } + + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serverName := rs.Primary.Attributes["server_name"] + + resp, err := client.Get(ctx, resourceGroup, serverName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + return fmt.Errorf("PostgreSQL AD Administrator (server %q / resource group %q) still exists: %+v", serverName, resourceGroup, resp) + } + + return nil +} + +func testAccAzurePostgreSqlAdministrator_basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-psql-%d" + location = "%s" +} + +resource "azurerm_postgresql_server" "test" { + name = "acctest-psql-server-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku_name = "GP_Gen5_2" + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "9.6" + ssl_enforcement = "Enabled" +} + +resource "azurerm_postgresql_active_directory_administrator" "test" { + server_name = azurerm_postgresql_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 +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func testAccAzurePostgreSqlAdministrator_requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_postgresql_active_directory_administrator" "import" { + server_name = azurerm_postgresql_active_directory_administrator.test.server_name + resource_group_name = azurerm_postgresql_active_directory_administrator.test.resource_group_name + login = azurerm_postgresql_active_directory_administrator.test.login + tenant_id = azurerm_postgresql_active_directory_administrator.test.tenant_id + object_id = azurerm_postgresql_active_directory_administrator.test.object_id +} +`, testAccAzurePostgreSqlAdministrator_basic(data)) +} + +func testAccAzurePostgreSqlAdministrator_withUpdates(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-psql-%d" + location = "%s" +} + +resource "azurerm_postgresql_server" "test" { + name = "acctest-psql-server-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku_name = "GP_Gen5_2" + + storage_profile { + storage_mb = 51200 + backup_retention_days = 7 + geo_redundant_backup = "Disabled" + } + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "9.6" + ssl_enforcement = "Enabled" +} + +resource "azurerm_postgresql_active_directory_administrator" "test" { + server_name = azurerm_postgresql_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 +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index f4a86ad256e5..6d17486ebce1 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -1284,6 +1284,10 @@ azurerm_mysql_virtual_network_rule +
  • + azurerm_postgresql_active_directory_administrator +
  • +
  • azurerm_postgresql_configuration
  • diff --git a/website/docs/r/postgresql_active_directory_administrator.html.markdown b/website/docs/r/postgresql_active_directory_administrator.html.markdown new file mode 100644 index 000000000000..a8b4f5492f16 --- /dev/null +++ b/website/docs/r/postgresql_active_directory_administrator.html.markdown @@ -0,0 +1,76 @@ +--- +subcategory: "Database" +layout: "azurerm" +page_title: "Azure Resource manager: azurerm_postgresql_active_directory_administrator" +description: |- + Manages an Active Directory administrator on a PostgreSQL server +--- + +# azurerm_postgresql_active_directory_administrator + +Allows you to set a user or group as the AD administrator for an PostgreSQL server in Azure + +## Example Usage + +```hcl +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West US" +} + +resource "azurerm_postgresql_server" "example" { + name = "example-psqlserver" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + version = "9.6" + administrator_login = "4dm1n157r470r" + administrator_login_password = "4-v3ry-53cr37-p455w0rd" +} + +resource "azurerm_postgresql_active_directory_administrator" "example" { + server_name = azurerm_postgresql_server.example.name + resource_group_name = azurerm_resource_group.example.name + login = "sqladmin" + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `server_name` - (Required) The name of the PostgreSQL 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 PostgreSQL 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 ID of the PostgreSQL Active Directory Administrator. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the PostgreSQL Active Directory Administrator. +* `update` - (Defaults to 30 minutes) Used when updating the PostgreSQL Active Directory Administrator. +* `read` - (Defaults to 5 minutes) Used when retrieving the PostgreSQL Active Directory Administrator. +* `delete` - (Defaults to 30 minutes) Used when deleting the PostgreSQL Active Directory Administrator. + +## Import + +A PostgreSQL Active Directory Administrator can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_postgresql_active_directory_administrator.administrator /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.DBforPostgreSQL/servers/myserver/administrators/activeDirectory +```