diff --git a/azurerm/internal/services/domainservices/active_directory_domain_service_data_source.go b/azurerm/internal/services/domainservices/active_directory_domain_service_data_source.go index 3846aa92a916..6612289218ce 100644 --- a/azurerm/internal/services/domainservices/active_directory_domain_service_data_source.go +++ b/azurerm/internal/services/domainservices/active_directory_domain_service_data_source.go @@ -18,6 +18,14 @@ func dataSourceArmActiveDirectoryDomainService() *schema.Resource { Read: dataSourceArmActiveDirectoryDomainServiceRead, Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotWhiteSpace, + }, + + "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + "domain_configuration_type": { Type: schema.TypeString, Computed: true, @@ -33,47 +41,8 @@ func dataSourceArmActiveDirectoryDomainService() *schema.Resource { Computed: true, }, - "ldaps": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enabled": { - Type: schema.TypeBool, - Computed: true, - }, - - "external_access_enabled": { - Type: schema.TypeBool, - Computed: true, - }, - - "external_access_ip_address": { - Type: schema.TypeString, - Computed: true, - }, - - "pfx_certificate": { - Type: schema.TypeString, - Computed: true, - }, - - "pfx_certificate_password": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - "location": azure.SchemaLocationForDataSource(), - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotWhiteSpace, - }, - "notifications": { Type: schema.TypeList, Computed: true, @@ -118,12 +87,12 @@ func dataSourceArmActiveDirectoryDomainService() *schema.Resource { Computed: true, }, - "location": { + "id": { Type: schema.TypeString, Computed: true, }, - "replica_set_id": { + "location": { Type: schema.TypeString, Computed: true, }, @@ -137,11 +106,6 @@ func dataSourceArmActiveDirectoryDomainService() *schema.Resource { Type: schema.TypeString, Computed: true, }, - - "vnet_site_id": { - Type: schema.TypeString, - Computed: true, - }, }, }, }, @@ -195,7 +159,38 @@ func dataSourceArmActiveDirectoryDomainService() *schema.Resource { }, }, - "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + "secure_ldap": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + + "external_access_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + + "external_access_ip_address": { + Type: schema.TypeString, + Computed: true, + }, + + "pfx_certificate": { + Type: schema.TypeString, + Computed: true, + }, + + "pfx_certificate_password": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, "security": { Type: schema.TypeList, @@ -280,10 +275,6 @@ func dataSourceArmActiveDirectoryDomainServiceRead(d *schema.ResourceData, meta d.Set("sku", props.Sku) - if err := d.Set("ldaps", flattenDomainServiceLdaps(props.LdapsSettings)); err != nil { - return fmt.Errorf("setting `ldaps`: %+v", err) - } - if err := d.Set("notifications", flattenDomainServiceNotifications(props.NotificationSettings)); err != nil { return fmt.Errorf("setting `notifications`: %+v", err) } @@ -296,6 +287,10 @@ func dataSourceArmActiveDirectoryDomainServiceRead(d *schema.ResourceData, meta return fmt.Errorf("setting `resource_forest`: %+v", err) } + if err := d.Set("secure_ldap", flattenDomainServiceLdaps(props.LdapsSettings)); err != nil { + return fmt.Errorf("setting `secure_ldap`: %+v", err) + } + if err := d.Set("security", flattenDomainServiceSecurity(props.DomainSecuritySettings)); err != nil { return fmt.Errorf("setting `security`: %+v", err) } diff --git a/azurerm/internal/services/domainservices/active_directory_domain_service_resource.go b/azurerm/internal/services/domainservices/active_directory_domain_service_resource.go index 21aaf2b79862..618f51968686 100644 --- a/azurerm/internal/services/domainservices/active_directory_domain_service_resource.go +++ b/azurerm/internal/services/domainservices/active_directory_domain_service_resource.go @@ -7,9 +7,8 @@ import ( "strings" "time" - "github.com/hashicorp/go-azure-helpers/response" - "github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad" + "github.com/hashicorp/go-azure-helpers/response" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" @@ -44,9 +43,32 @@ func resourceActiveDirectoryDomainService() *schema.Resource { }, Schema: map[string]*schema.Schema{ // TODO: add computed attributes: deployment_id, sync_owner - "deployment_id": { + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, // TODO: proper validation + }, + + "location": azure.SchemaLocation(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, // TODO: proper validation, first prefix must be 15 chars or less + }, + + "sku": { Type: schema.TypeString, - Computed: true, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "Standard", + "Enterprise", + "Premium", + }, false), }, "domain_configuration_type": { @@ -60,67 +82,12 @@ func resourceActiveDirectoryDomainService() *schema.Resource { }, false), }, - "domain_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, // TODO: proper validation, first prefix must be 15 chars or less - }, - "filtered_sync_enabled": { Type: schema.TypeBool, Optional: true, Default: false, }, - "ldaps": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enabled": { - Type: schema.TypeBool, - Required: true, - }, - - "external_access_enabled": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - - "external_access_ip_address": { - Type: schema.TypeString, - Computed: true, - }, - - "pfx_certificate": { - Type: schema.TypeString, - Required: true, - Sensitive: true, - ValidateFunc: azValidate.Base64EncodedString, - }, - - "pfx_certificate_password": { - Type: schema.TypeString, - Required: true, - Sensitive: true, - }, - }, - }, - }, - - "location": azure.SchemaLocation(), - - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, // TODO: proper validation - }, - "notifications": { Type: schema.TypeList, Optional: true, @@ -173,6 +140,11 @@ func resourceActiveDirectoryDomainService() *schema.Resource { Computed: true, }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "location": { Type: schema.TypeString, Required: true, @@ -182,11 +154,6 @@ func resourceActiveDirectoryDomainService() *schema.Resource { DiffSuppressFunc: location.DiffSuppressFunc, }, - "replica_set_id": { - Type: schema.TypeString, - Computed: true, - }, - "service_status": { Type: schema.TypeString, Computed: true, @@ -198,11 +165,6 @@ func resourceActiveDirectoryDomainService() *schema.Resource { ForceNew: true, // TODO: figure out if this is needed ValidateFunc: azure.ValidateResourceID, }, - - "vnet_site_id": { - Type: schema.TypeString, - Computed: true, - }, }, }, }, @@ -265,7 +227,44 @@ func resourceActiveDirectoryDomainService() *schema.Resource { }, }, - "resource_group_name": azure.SchemaResourceGroupName(), + "secure_ldap": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + + "external_access_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "external_access_ip_address": { + Type: schema.TypeString, + Computed: true, + }, + + "pfx_certificate": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: azValidate.Base64EncodedString, + }, + + "pfx_certificate_password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + }, + }, + }, "security": { Type: schema.TypeList, @@ -307,24 +306,19 @@ func resourceActiveDirectoryDomainService() *schema.Resource { }, }, - "sku": { + "tags": tags.Schema(), + + "deployment_id": { Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "Standard", - "Enterprise", - "Premium", - }, false), + Computed: true, }, - - "tags": tags.Schema(), }, } } func resourceActiveDirectoryDomainServiceCreateUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*clients.Client).DomainServices.DomainServicesClient - ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() name := d.Get("name").(string) @@ -355,7 +349,7 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *schema.ResourceData, me DomainName: utils.String(d.Get("domain_name").(string)), DomainSecuritySettings: expandDomainServiceSecurity(d.Get("security").([]interface{})), FilteredSync: filteredSync, - LdapsSettings: expandDomainServiceLdaps(d.Get("ldaps").([]interface{})), + LdapsSettings: expandDomainServiceLdaps(d.Get("secure_ldap").([]interface{})), NotificationSettings: expandDomainServiceNotifications(d.Get("notifications").([]interface{})), ResourceForestSettings: expandDomainServiceResourceForest(d.Get("resource_forest").([]interface{})), Sku: utils.String(d.Get("sku").(string)), @@ -387,13 +381,14 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *schema.ResourceData, me // A fully deployed domain service has 2 domain controllers per replica set, but the create operation completes early before the DCs are online. // The domain service is still provisioning and further operations are blocked until both controllers are up. + timeout, _ := ctx.Deadline() stateConf := &resource.StateChangeConf{ Pending: []string{"pending"}, Target: []string{"available"}, Refresh: domainServiceControllerRefreshFunc(ctx, client, id), - Delay: 30 * time.Second, - PollInterval: 10 * time.Second, - Timeout: 1 * time.Hour, + Delay: 1 * time.Minute, + PollInterval: 1 * time.Minute, + Timeout: time.Until(timeout), } if _, err := stateConf.WaitForState(); err != nil { @@ -421,13 +416,14 @@ func resourceActiveDirectoryDomainServiceCreateUpdate(d *schema.ResourceData, me } // Wait for the additional replica sets to become fully available + timeout, _ = ctx.Deadline() stateConf := &resource.StateChangeConf{ Pending: []string{"pending"}, Target: []string{"available"}, Refresh: domainServiceControllerRefreshFunc(ctx, client, id), - Delay: 30 * time.Second, - PollInterval: 10 * time.Second, - Timeout: 1 * time.Hour, + Delay: 1 * time.Minute, + PollInterval: 1 * time.Minute, + Timeout: time.Until(timeout), } if _, err := stateConf.WaitForState(); err != nil { @@ -482,10 +478,6 @@ func resourceActiveDirectoryDomainServiceRead(d *schema.ResourceData, meta inter d.Set("sku", props.Sku) - if err := d.Set("ldaps", flattenDomainServiceLdaps(props.LdapsSettings)); err != nil { - return fmt.Errorf("setting `ldaps`: %+v", err) - } - if err := d.Set("notifications", flattenDomainServiceNotifications(props.NotificationSettings)); err != nil { return fmt.Errorf("setting `notifications`: %+v", err) } @@ -498,6 +490,10 @@ func resourceActiveDirectoryDomainServiceRead(d *schema.ResourceData, meta inter return fmt.Errorf("setting `resource_forest`: %+v", err) } + if err := d.Set("secure_ldap", flattenDomainServiceLdaps(props.LdapsSettings)); err != nil { + return fmt.Errorf("setting `secure_ldap`: %+v", err) + } + if err := d.Set("security", flattenDomainServiceSecurity(props.DomainSecuritySettings)); err != nil { return fmt.Errorf("setting `security`: %+v", err) } @@ -540,15 +536,16 @@ func domainServiceControllerRefreshFunc(ctx context.Context, client *aad.DomainS if err != nil { return nil, "error", err } - if resp.ReplicaSets != nil { - for _, repl := range *resp.ReplicaSets { - if repl.ServiceStatus == nil || !strings.EqualFold(*repl.ServiceStatus, "Running") { - return resp, "pending", nil - } - // when a domain controller is online, its IP address will be returned - if repl.DomainControllerIPAddress == nil || len(*repl.DomainControllerIPAddress) < 2 { - return resp, "pending", nil - } + if resp.DomainServiceProperties == nil || resp.DomainServiceProperties.ReplicaSets == nil || len(*resp.DomainServiceProperties.ReplicaSets) == 0 { + return nil, "error", fmt.Errorf("API error: `replicaSets` was not returned") + } + for _, repl := range *resp.DomainServiceProperties.ReplicaSets { + if repl.ServiceStatus == nil || !strings.EqualFold(*repl.ServiceStatus, "Running") { + return resp, "pending", nil + } + // when a domain controller is online, its IP address will be returned + if repl.DomainControllerIPAddress == nil || len(*repl.DomainControllerIPAddress) < 2 { + return resp, "pending", nil } } return resp, "available", nil @@ -755,10 +752,9 @@ func flattenDomainServiceReplicaSets(input *[]aad.ReplicaSet) (ret []interface{} "domain_controller_ip_addresses": "", "external_access_ip_address": "", "location": "", - "replica_set_id": "", + "id": "", "service_status": "", "subnet_id": "", - "vnet_site_id": "", } if in.DomainControllerIPAddress != nil { repl["domain_controller_ip_addresses"] = *in.DomainControllerIPAddress @@ -770,7 +766,7 @@ func flattenDomainServiceReplicaSets(input *[]aad.ReplicaSet) (ret []interface{} repl["location"] = azure.NormalizeLocation(*in.Location) } if in.ReplicaSetID != nil { - repl["replica_set_id"] = in.ReplicaSetID + repl["id"] = in.ReplicaSetID } if in.ServiceStatus != nil { repl["service_status"] = in.ServiceStatus @@ -778,9 +774,6 @@ func flattenDomainServiceReplicaSets(input *[]aad.ReplicaSet) (ret []interface{} if in.SubnetID != nil { repl["subnet_id"] = in.SubnetID } - if in.VnetSiteID != nil { - repl["vnet_site_id"] = in.VnetSiteID - } ret = append(ret, repl) } diff --git a/azurerm/internal/services/domainservices/active_directory_domain_service_test.go b/azurerm/internal/services/domainservices/active_directory_domain_service_test.go index 038c1739348d..b787e8b1c197 100644 --- a/azurerm/internal/services/domainservices/active_directory_domain_service_test.go +++ b/azurerm/internal/services/domainservices/active_directory_domain_service_test.go @@ -37,15 +37,15 @@ func TestAccActiveDirectoryDomainService_complete(t *testing.T) { resource.TestCheckResourceAttr(data.ResourceName, "replica_set.1.domain_controller_ip_addresses.#", "2"), ), }, - data.ImportStep("ldaps.0.pfx_certificate", "ldaps.0.pfx_certificate_password"), + data.ImportStep("secure_ldap.0.pfx_certificate", "secure_ldap.0.pfx_certificate_password"), { Config: r.dataSource(data), Check: resource.ComposeTestCheckFunc( check.That(dataSourceName).ExistsInAzure(r), check.That(dataSourceName).Key("filtered_sync_enabled").HasValue("false"), - check.That(dataSourceName).Key("ldaps.#").HasValue("1"), - check.That(dataSourceName).Key("ldaps.0.enabled").HasValue("false"), - check.That(dataSourceName).Key("ldaps.0.external_access_enabled").HasValue("false"), + check.That(dataSourceName).Key("secure_ldap.#").HasValue("1"), + check.That(dataSourceName).Key("secure_ldap.0.enabled").HasValue("false"), + check.That(dataSourceName).Key("secure_ldap.0.external_access_enabled").HasValue("false"), check.That(dataSourceName).Key("location").HasValue(azure.NormalizeLocation(data.Locations.Primary)), check.That(dataSourceName).Key("notifications.#").HasValue("1"), check.That(dataSourceName).Key("notifications.0.additional_recipients.#").HasValue("2"), @@ -54,12 +54,12 @@ func TestAccActiveDirectoryDomainService_complete(t *testing.T) { check.That(dataSourceName).Key("replica_sets.#").HasValue("2"), check.That(dataSourceName).Key("replica_sets.0.domain_controller_ip_addresses.#").HasValue("2"), check.That(dataSourceName).Key("replica_sets.0.location").HasValue(azure.NormalizeLocation(data.Locations.Primary)), - check.That(dataSourceName).Key("replica_sets.0.replica_set_id").Exists(), + check.That(dataSourceName).Key("replica_sets.0.id").Exists(), check.That(dataSourceName).Key("replica_sets.0.service_status").Exists(), check.That(dataSourceName).Key("replica_sets.0.subnet_id").Exists(), check.That(dataSourceName).Key("replica_sets.1.domain_controller_ip_addresses.#").HasValue("2"), check.That(dataSourceName).Key("replica_sets.1.location").HasValue(azure.NormalizeLocation(data.Locations.Secondary)), - check.That(dataSourceName).Key("replica_sets.1.replica_set_id").Exists(), + check.That(dataSourceName).Key("replica_sets.1.id").Exists(), check.That(dataSourceName).Key("replica_sets.1.service_status").Exists(), check.That(dataSourceName).Key("replica_sets.1.subnet_id").Exists(), check.That(dataSourceName).Key("resource_forest.#").HasValue("0"), @@ -249,13 +249,6 @@ resource "azurerm_active_directory_domain_service" "test" { sku = "Enterprise" filtered_sync_enabled = false - //ldaps { - // enabled = true - // external_access = true - // pfx_certificate = "TODO Generate a dummy pfx key+cert (https://docs.microsoft.com/en-us/azure/active-directory-domain-services/tutorial-configure-ldaps)" - // pfx_certificate_password = "test" - //} - notifications { additional_recipients = ["notifyA@example.net", "notifyB@example.org"] notify_dc_admins = true @@ -272,6 +265,13 @@ resource "azurerm_active_directory_domain_service" "test" { subnet_id = azurerm_subnet.aadds_two.id } + //secure_ldap { + // enabled = true + // external_access = true + // pfx_certificate = "TODO Generate a dummy pfx key+cert (https://docs.microsoft.com/en-us/azure/active-directory-domain-services/tutorial-configure-ldaps)" + // pfx_certificate_password = "test" + //} + security { ntlm_v1_enabled = true sync_kerberos_passwords = true