Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Active Directory Domain Services #10782

Merged
merged 9 commits into from
Jul 22, 2021
Merged
1 change: 1 addition & 0 deletions .teamcity/components/generated/services.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var services = mapOf(
"desktopvirtualization" to "Desktop Virtualization",
"devtestlabs" to "Dev Test",
"digitaltwins" to "Digital Twins",
"domainservices" to "DomainServices",
"eventgrid" to "EventGrid",
"eventhub" to "EventHub",
"firewall" to "Firewall",
Expand Down
19 changes: 19 additions & 0 deletions azurerm/helpers/validate/strings.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
package validate

import (
"encoding/base64"
"fmt"
"strings"
)

// Base64EncodedString validates that the string is base64 encoded
func Base64EncodedString(i interface{}, k string) ([]string, []error) {
manicminer marked this conversation as resolved.
Show resolved Hide resolved
v, ok := i.(string)
if !ok {
return nil, []error{fmt.Errorf("expected type of %q to be string", k)}
}

if strings.TrimSpace(v) == "" {
return nil, []error{fmt.Errorf("%q must not be empty", k)}
}

if _, err := base64.StdEncoding.DecodeString(v); err != nil {
return nil, []error{fmt.Errorf("%q must be a valid base64 encoded string", k)}
}

return nil, nil
}

// LowerCasedString validates that the string is lower-cased
func LowerCasedString(i interface{}, k string) ([]string, []error) {
v, ok := i.(string)
Expand Down
28 changes: 28 additions & 0 deletions azurerm/helpers/validate/strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@ import (
"testing"
)

func TestBase64EncodedString(t *testing.T) {
cases := []struct {
Input string
Errors int
}{
{
Input: "",
Errors: 1,
},
{
Input: "aGVsbG8td29ybGQ=",
Errors: 0,
},
{
Input: "hello-world",
Errors: 1,
},
}

for _, tc := range cases {
t.Run(tc.Input, func(t *testing.T) {
if _, errors := Base64EncodedString(tc.Input, "base64"); len(errors) != tc.Errors {
t.Fatalf("Expected Base64 string to have %d not %d errors for %q: %v", tc.Errors, len(errors), tc.Input, errors)
}
})
}
}

func TestLowerCasedStrings(t *testing.T) {
cases := []struct {
Value string
Expand Down
3 changes: 3 additions & 0 deletions azurerm/internal/clients/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
devtestlabs "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/devtestlabs/client"
digitaltwins "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/digitaltwins/client"
dns "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/dns/client"
domainservices "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/domainservices/client"
eventgrid "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventgrid/client"
eventhub "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventhub/client"
firewall "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/firewall/client"
Expand Down Expand Up @@ -142,6 +143,7 @@ type Client struct {
DevTestLabs *devtestlabs.Client
DigitalTwins *digitaltwins.Client
Dns *dns.Client
DomainServices *domainservices.Client
EventGrid *eventgrid.Client
Eventhub *eventhub.Client
Firewall *firewall.Client
Expand Down Expand Up @@ -246,6 +248,7 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error
client.DevTestLabs = devtestlabs.NewClient(o)
client.DigitalTwins = digitaltwins.NewClient(o)
client.Dns = dns.NewClient(o)
client.DomainServices = domainservices.NewClient(o)
client.EventGrid = eventgrid.NewClient(o)
client.Eventhub = eventhub.NewClient(o)
client.Firewall = firewall.NewClient(o)
Expand Down
2 changes: 2 additions & 0 deletions azurerm/internal/provider/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/devtestlabs"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/digitaltwins"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/dns"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/domainservices"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventgrid"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/eventhub"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/firewall"
Expand Down Expand Up @@ -146,6 +147,7 @@ func SupportedUntypedServices() []sdk.UntypedServiceRegistration {
devtestlabs.Registration{},
digitaltwins.Registration{},
dns.Registration{},
domainservices.Registration{},
eventgrid.Registration{},
eventhub.Registration{},
firewall.Registration{},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
package domainservices

import (
"fmt"
"time"

"github.com/Azure/azure-sdk-for-go/services/domainservices/mgmt/2020-01-01/aad"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func dataSourceActiveDirectoryDomainService() *pluginsdk.Resource {
return &pluginsdk.Resource{
Read: dataSourceActiveDirectoryDomainServiceRead,

Timeouts: &pluginsdk.ResourceTimeout{
Read: pluginsdk.DefaultTimeout(5 * time.Minute),
},

Schema: map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotWhiteSpace,
},

"resource_group_name": azure.SchemaResourceGroupNameForDataSource(),

"deployment_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"domain_configuration_type": {
Type: pluginsdk.TypeString,
Computed: true,
},

"domain_name": {
Type: pluginsdk.TypeString,
Computed: true,
},

"filtered_sync_enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"location": azure.SchemaLocationForDataSource(),

"notifications": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"additional_recipients": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},
"notify_dc_admins": {
Type: pluginsdk.TypeBool,
Computed: true,
},
"notify_global_admins": {
Type: pluginsdk.TypeBool,
Computed: true,
},
},
},
},

"replica_sets": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Resource{
Schema: dataSourceActiveDirectoryDomainServiceReplicaSetSchema(),
},
},

"secure_ldap": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"external_access_enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"certificate_expiry": {
Type: pluginsdk.TypeString,
Computed: true,
},

"certificate_thumbprint": {
Type: pluginsdk.TypeString,
Computed: true,
},

"public_certificate": {
Type: pluginsdk.TypeString,
Computed: true,
},
},
},
},

"security": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"ntlm_v1_enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"sync_kerberos_passwords": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"sync_ntlm_passwords": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"sync_on_prem_passwords": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"tls_v1_enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},
},
},
},

"sku": {
Type: pluginsdk.TypeString,
Computed: true,
},

"sync_owner": {
Type: pluginsdk.TypeString,
Computed: true,
},

"tags": tags.SchemaDataSource(),

"tenant_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"version": {
Type: pluginsdk.TypeInt,
Computed: true,
},
},
}
}

func dataSourceActiveDirectoryDomainServiceReplicaSetSchema() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
// TODO: add health-related attributes

"domain_controller_ip_addresses": {
Type: pluginsdk.TypeList,
Computed: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"external_access_ip_address": {
Type: pluginsdk.TypeString,
Computed: true,
},

"id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"location": azure.SchemaLocationForDataSource(),

"service_status": {
Type: pluginsdk.TypeString,
Computed: true,
},

"subnet_id": {
Type: pluginsdk.TypeString,
Computed: true,
},
}
}

func dataSourceActiveDirectoryDomainServiceRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).DomainServices.DomainServicesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

name := d.Get("name").(string)
resourceGroup := d.Get("resource_group_name").(string)

resp, err := client.Get(ctx, resourceGroup, name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return nil
}
return err
}

if resp.ID == nil {
return fmt.Errorf("reading Domain Service: ID was returned nil")
}
d.SetId(*resp.ID)

d.Set("name", name)
d.Set("resource_group_name", resourceGroup)

if resp.Location == nil {
return fmt.Errorf("reading Domain Service %q: location was returned nil", d.Id())
}
d.Set("location", azure.NormalizeLocation(*resp.Location))

if props := resp.DomainServiceProperties; props != nil {
d.Set("deployment_id", props.DeploymentID)

domainConfigType := ""
if v := props.DomainConfigurationType; v != nil {
domainConfigType = *v
}
d.Set("domain_configuration_type", domainConfigType)

d.Set("domain_name", props.DomainName)

d.Set("filtered_sync_enabled", false)
if props.FilteredSync == aad.FilteredSyncEnabled {
d.Set("filtered_sync_enabled", true)
}

d.Set("sku", props.Sku)
d.Set("sync_owner", props.SyncOwner)
d.Set("tenant_id", props.TenantID)
d.Set("version", props.Version)

if err := d.Set("notifications", flattenDomainServiceNotifications(props.NotificationSettings)); err != nil {
return fmt.Errorf("setting `notifications`: %+v", err)
}

if err := d.Set("secure_ldap", flattenDomainServiceLdaps(d, props.LdapsSettings, true)); 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)
}

replicaSets := flattenDomainServiceReplicaSets(props.ReplicaSets)
if err := d.Set("replica_sets", replicaSets); err != nil {
return fmt.Errorf("setting `replica_sets`: %+v", err)
}
}

return tags.FlattenAndSet(d, resp.Tags)
}
Loading