From bc9c304d760b2122e6aeed7e79a315784c668750 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Tue, 18 Aug 2020 21:14:27 +0000 Subject: [PATCH] Add trust to ad domain 5731 (#3798) * AD Domain resource added * fmt issue fixed * validation regex and input flag added * Trust resouce attributes added to api.yaml * ad domain trust resource added * ad domain trust resource added * post_create attribute added * nested_query attribute added - work in progress * work in progress - peer review * encoder and decoder added - wip * custom encoders and decoders added for update and delete resource * comment removed * comment added * comment syntax corrected * comments added as PR review comments Signed-off-by: Modular Magician --- .changelog/3798.txt | 3 + google/provider.go | 5 +- ..._active_directory_domain_generated_test.go | 2 +- .../resource_active_directory_domain_trust.go | 580 ++++++++++++++++++ ...urce_active_directory_domain_trust_test.go | 101 +++ .../r/active_directory_domain.html.markdown | 2 +- ...ctive_directory_domain_trust.html.markdown | 127 ++++ website/google.erb | 4 + 8 files changed, 820 insertions(+), 4 deletions(-) create mode 100644 .changelog/3798.txt create mode 100644 google/resource_active_directory_domain_trust.go create mode 100644 google/resource_active_directory_domain_trust_test.go create mode 100644 website/docs/r/active_directory_domain_trust.html.markdown diff --git a/.changelog/3798.txt b/.changelog/3798.txt new file mode 100644 index 00000000000..79a801aff7b --- /dev/null +++ b/.changelog/3798.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +AD Domain - added new resource `google_active_directory_domain_trust` +``` diff --git a/google/provider.go b/google/provider.go index 267d03d9fc5..c7ae525862c 100644 --- a/google/provider.go +++ b/google/provider.go @@ -615,9 +615,9 @@ func Provider() terraform.ResourceProvider { return provider } -// Generated resources: 154 +// Generated resources: 155 // Generated IAM resources: 60 -// Total generated resources: 214 +// Total generated resources: 215 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -636,6 +636,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_access_context_manager_service_perimeters": resourceAccessContextManagerServicePerimeters(), "google_access_context_manager_service_perimeter_resource": resourceAccessContextManagerServicePerimeterResource(), "google_active_directory_domain": resourceActiveDirectoryDomain(), + "google_active_directory_domain_trust": resourceActiveDirectoryDomainTrust(), "google_app_engine_domain_mapping": resourceAppEngineDomainMapping(), "google_app_engine_firewall_rule": resourceAppEngineFirewallRule(), "google_app_engine_standard_app_version": resourceAppEngineStandardAppVersion(), diff --git a/google/resource_active_directory_domain_generated_test.go b/google/resource_active_directory_domain_generated_test.go index 8331dfa3f31..fede35281ce 100644 --- a/google/resource_active_directory_domain_generated_test.go +++ b/google/resource_active_directory_domain_generated_test.go @@ -51,7 +51,7 @@ func TestAccActiveDirectoryDomain_activeDirectoryDomainBasicExample(t *testing.T func testAccActiveDirectoryDomain_activeDirectoryDomainBasicExample(context map[string]interface{}) string { return Nprintf(` resource "google_active_directory_domain" "ad-domain" { - domain_name = "mydomain%{random_suffix}.org.com" + domain_name = ".org.com" locations = ["us-central1"] reserved_ip_range = "192.168.255.0/24" } diff --git a/google/resource_active_directory_domain_trust.go b/google/resource_active_directory_domain_trust.go new file mode 100644 index 00000000000..9590cbf36f9 --- /dev/null +++ b/google/resource_active_directory_domain_trust.go @@ -0,0 +1,580 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "log" + "reflect" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceActiveDirectoryDomainTrust() *schema.Resource { + return &schema.Resource{ + Create: resourceActiveDirectoryDomainTrustCreate, + Read: resourceActiveDirectoryDomainTrustRead, + Update: resourceActiveDirectoryDomainTrustUpdate, + Delete: resourceActiveDirectoryDomainTrustDelete, + + Importer: &schema.ResourceImporter{ + State: resourceActiveDirectoryDomainTrustImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The fully qualified domain name. e.g. mydomain.myorganization.com, with the restrictions, +https://cloud.google.com/managed-microsoft-ad/reference/rest/v1/projects.locations.global.domains.`, + }, + "target_dns_ip_addresses": { + Type: schema.TypeSet, + Required: true, + Description: `The target DNS server IP addresses which can resolve the remote domain involved in the trust.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Set: schema.HashString, + }, + "target_domain_name": { + Type: schema.TypeString, + Required: true, + Description: `The fully qualified target domain name which will be in trust with the current domain.`, + }, + "trust_direction": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"INBOUND", "OUTBOUND", "BIDIRECTIONAL"}, false), + Description: `The trust direction, which decides if the current domain is trusted, trusting, or both. Possible values: ["INBOUND", "OUTBOUND", "BIDIRECTIONAL"]`, + }, + "trust_handshake_secret": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The trust secret used for the handshake with the target domain. This will not be stored.`, + Sensitive: true, + }, + "trust_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"FOREST", "EXTERNAL"}, false), + Description: `The type of trust represented by the trust resource. Possible values: ["FOREST", "EXTERNAL"]`, + }, + "selective_authentication": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Description: `Whether the trusted side has forest/domain wide access or selective access to an approved set of resources.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + } +} + +func resourceActiveDirectoryDomainTrustCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := make(map[string]interface{}) + targetDomainNameProp, err := expandNestedActiveDirectoryDomainTrustTargetDomainName(d.Get("target_domain_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("target_domain_name"); !isEmptyValue(reflect.ValueOf(targetDomainNameProp)) && (ok || !reflect.DeepEqual(v, targetDomainNameProp)) { + obj["targetDomainName"] = targetDomainNameProp + } + trustTypeProp, err := expandNestedActiveDirectoryDomainTrustTrustType(d.Get("trust_type"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_type"); !isEmptyValue(reflect.ValueOf(trustTypeProp)) && (ok || !reflect.DeepEqual(v, trustTypeProp)) { + obj["trustType"] = trustTypeProp + } + trustDirectionProp, err := expandNestedActiveDirectoryDomainTrustTrustDirection(d.Get("trust_direction"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_direction"); !isEmptyValue(reflect.ValueOf(trustDirectionProp)) && (ok || !reflect.DeepEqual(v, trustDirectionProp)) { + obj["trustDirection"] = trustDirectionProp + } + selectiveAuthenticationProp, err := expandNestedActiveDirectoryDomainTrustSelectiveAuthentication(d.Get("selective_authentication"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("selective_authentication"); !isEmptyValue(reflect.ValueOf(selectiveAuthenticationProp)) && (ok || !reflect.DeepEqual(v, selectiveAuthenticationProp)) { + obj["selectiveAuthentication"] = selectiveAuthenticationProp + } + targetDnsIpAddressesProp, err := expandNestedActiveDirectoryDomainTrustTargetDnsIpAddresses(d.Get("target_dns_ip_addresses"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("target_dns_ip_addresses"); !isEmptyValue(reflect.ValueOf(targetDnsIpAddressesProp)) && (ok || !reflect.DeepEqual(v, targetDnsIpAddressesProp)) { + obj["targetDnsIpAddresses"] = targetDnsIpAddressesProp + } + trustHandshakeSecretProp, err := expandNestedActiveDirectoryDomainTrustTrustHandshakeSecret(d.Get("trust_handshake_secret"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_handshake_secret"); !isEmptyValue(reflect.ValueOf(trustHandshakeSecretProp)) && (ok || !reflect.DeepEqual(v, trustHandshakeSecretProp)) { + obj["trustHandshakeSecret"] = trustHandshakeSecretProp + } + + obj, err = resourceActiveDirectoryDomainTrustEncoder(d, meta, obj) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{ActiveDirectoryBasePath}}projects/{{project}}/locations/global/domains/{{domain}}:attachTrust") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new DomainTrust: %#v", obj) + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating DomainTrust: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "projects/{{project}}/locations/global/domains/{{domain}}/{{target_domain_name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Use the resource in the operation response to populate + // identity fields and d.Id() before read + var opRes map[string]interface{} + err = activeDirectoryOperationWaitTimeWithResponse( + config, res, &opRes, project, "Creating DomainTrust", + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create DomainTrust: %s", err) + } + + opRes, err = resourceActiveDirectoryDomainTrustDecoder(d, meta, opRes) + if err != nil { + return fmt.Errorf("Error decoding response from operation: %s", err) + } + if opRes == nil { + return fmt.Errorf("Error decoding response from operation, could not find object") + } + + if _, ok := opRes["trusts"]; ok { + opRes, err = flattenNestedActiveDirectoryDomainTrust(d, meta, opRes) + if err != nil { + return fmt.Errorf("Error getting nested object from operation response: %s", err) + } + if opRes == nil { + // Object isn't there any more - remove it from the state. + return fmt.Errorf("Error decoding response from operation, could not find nested object") + } + } + if err := d.Set("target_domain_name", flattenNestedActiveDirectoryDomainTrustTargetDomainName(opRes["targetDomainName"], d, config)); err != nil { + return err + } + + // This may have caused the ID to update - update it if so. + id, err = replaceVars(d, config, "projects/{{project}}/locations/global/domains/{{domain}}/{{target_domain_name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating DomainTrust %q: %#v", d.Id(), res) + + return resourceActiveDirectoryDomainTrustRead(d, meta) +} + +func resourceActiveDirectoryDomainTrustRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + url, err := replaceVars(d, config, "{{ActiveDirectoryBasePath}}projects/{{project}}/locations/global/domains/{{domain}}") + if err != nil { + return err + } + + project, err := getProject(d, config) + if err != nil { + return err + } + res, err := sendRequest(config, "GET", project, url, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("ActiveDirectoryDomainTrust %q", d.Id())) + } + + res, err = flattenNestedActiveDirectoryDomainTrust(d, meta, res) + if err != nil { + return err + } + + if res == nil { + // Object isn't there any more - remove it from the state. + log.Printf("[DEBUG] Removing ActiveDirectoryDomainTrust because it couldn't be matched.") + d.SetId("") + return nil + } + + res, err = resourceActiveDirectoryDomainTrustDecoder(d, meta, res) + if err != nil { + return err + } + + if res == nil { + // Decoding the object has resulted in it being gone. It may be marked deleted + log.Printf("[DEBUG] Removing ActiveDirectoryDomainTrust because it no longer exists.") + d.SetId("") + return nil + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading DomainTrust: %s", err) + } + + if err := d.Set("target_domain_name", flattenNestedActiveDirectoryDomainTrustTargetDomainName(res["targetDomainName"], d, config)); err != nil { + return fmt.Errorf("Error reading DomainTrust: %s", err) + } + if err := d.Set("trust_type", flattenNestedActiveDirectoryDomainTrustTrustType(res["trustType"], d, config)); err != nil { + return fmt.Errorf("Error reading DomainTrust: %s", err) + } + if err := d.Set("trust_direction", flattenNestedActiveDirectoryDomainTrustTrustDirection(res["trustDirection"], d, config)); err != nil { + return fmt.Errorf("Error reading DomainTrust: %s", err) + } + if err := d.Set("selective_authentication", flattenNestedActiveDirectoryDomainTrustSelectiveAuthentication(res["selectiveAuthentication"], d, config)); err != nil { + return fmt.Errorf("Error reading DomainTrust: %s", err) + } + if err := d.Set("target_dns_ip_addresses", flattenNestedActiveDirectoryDomainTrustTargetDnsIpAddresses(res["targetDnsIpAddresses"], d, config)); err != nil { + return fmt.Errorf("Error reading DomainTrust: %s", err) + } + + return nil +} + +func resourceActiveDirectoryDomainTrustUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + targetDomainNameProp, err := expandNestedActiveDirectoryDomainTrustTargetDomainName(d.Get("target_domain_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("target_domain_name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, targetDomainNameProp)) { + obj["targetDomainName"] = targetDomainNameProp + } + trustTypeProp, err := expandNestedActiveDirectoryDomainTrustTrustType(d.Get("trust_type"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_type"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, trustTypeProp)) { + obj["trustType"] = trustTypeProp + } + trustDirectionProp, err := expandNestedActiveDirectoryDomainTrustTrustDirection(d.Get("trust_direction"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_direction"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, trustDirectionProp)) { + obj["trustDirection"] = trustDirectionProp + } + selectiveAuthenticationProp, err := expandNestedActiveDirectoryDomainTrustSelectiveAuthentication(d.Get("selective_authentication"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("selective_authentication"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, selectiveAuthenticationProp)) { + obj["selectiveAuthentication"] = selectiveAuthenticationProp + } + targetDnsIpAddressesProp, err := expandNestedActiveDirectoryDomainTrustTargetDnsIpAddresses(d.Get("target_dns_ip_addresses"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("target_dns_ip_addresses"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, targetDnsIpAddressesProp)) { + obj["targetDnsIpAddresses"] = targetDnsIpAddressesProp + } + trustHandshakeSecretProp, err := expandNestedActiveDirectoryDomainTrustTrustHandshakeSecret(d.Get("trust_handshake_secret"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_handshake_secret"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, trustHandshakeSecretProp)) { + obj["trustHandshakeSecret"] = trustHandshakeSecretProp + } + + obj, err = resourceActiveDirectoryDomainTrustUpdateEncoder(d, meta, obj) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{ActiveDirectoryBasePath}}projects/{{project}}/locations/global/domains/{{domain}}:reconfigureTrust") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating DomainTrust %q: %#v", d.Id(), obj) + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return fmt.Errorf("Error updating DomainTrust %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating DomainTrust %q: %#v", d.Id(), res) + } + + err = activeDirectoryOperationWaitTime( + config, res, project, "Updating DomainTrust", + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceActiveDirectoryDomainTrustRead(d, meta) +} + +func resourceActiveDirectoryDomainTrustDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + url, err := replaceVars(d, config, "{{ActiveDirectoryBasePath}}projects/{{project}}/locations/global/domains/{{domain}}:detachTrust") + if err != nil { + return err + } + + obj := make(map[string]interface{}) + targetDomainNameProp, err := expandNestedActiveDirectoryDomainTrustTargetDomainName(d.Get("target_domain_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("target_domain_name"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, targetDomainNameProp)) { + obj["targetDomainName"] = targetDomainNameProp + } + trustTypeProp, err := expandNestedActiveDirectoryDomainTrustTrustType(d.Get("trust_type"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_type"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, trustTypeProp)) { + obj["trustType"] = trustTypeProp + } + trustDirectionProp, err := expandNestedActiveDirectoryDomainTrustTrustDirection(d.Get("trust_direction"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_direction"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, trustDirectionProp)) { + obj["trustDirection"] = trustDirectionProp + } + selectiveAuthenticationProp, err := expandNestedActiveDirectoryDomainTrustSelectiveAuthentication(d.Get("selective_authentication"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("selective_authentication"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, selectiveAuthenticationProp)) { + obj["selectiveAuthentication"] = selectiveAuthenticationProp + } + targetDnsIpAddressesProp, err := expandNestedActiveDirectoryDomainTrustTargetDnsIpAddresses(d.Get("target_dns_ip_addresses"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("target_dns_ip_addresses"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, targetDnsIpAddressesProp)) { + obj["targetDnsIpAddresses"] = targetDnsIpAddressesProp + } + trustHandshakeSecretProp, err := expandNestedActiveDirectoryDomainTrustTrustHandshakeSecret(d.Get("trust_handshake_secret"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("trust_handshake_secret"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, trustHandshakeSecretProp)) { + obj["trustHandshakeSecret"] = trustHandshakeSecretProp + } + + obj, err = resourceActiveDirectoryDomainTrustEncoder(d, meta, obj) + if err != nil { + return err + } + + log.Printf("[DEBUG] Deleting DomainTrust %q", d.Id()) + + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "DomainTrust") + } + + err = activeDirectoryOperationWaitTime( + config, res, project, "Deleting DomainTrust", + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting DomainTrust %q: %#v", d.Id(), res) + return nil +} + +func resourceActiveDirectoryDomainTrustImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/global/domains/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVars(d, config, "projects/{{project}}/locations/global/domains/{{domain}}/{{target_domain_name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenNestedActiveDirectoryDomainTrustTargetDomainName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNestedActiveDirectoryDomainTrustTrustType(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNestedActiveDirectoryDomainTrustTrustDirection(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNestedActiveDirectoryDomainTrustSelectiveAuthentication(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenNestedActiveDirectoryDomainTrustTargetDnsIpAddresses(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + return schema.NewSet(schema.HashString, v.([]interface{})) +} + +func expandNestedActiveDirectoryDomainTrustTargetDomainName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNestedActiveDirectoryDomainTrustTrustType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNestedActiveDirectoryDomainTrustTrustDirection(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNestedActiveDirectoryDomainTrustSelectiveAuthentication(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandNestedActiveDirectoryDomainTrustTargetDnsIpAddresses(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + v = v.(*schema.Set).List() + return v, nil +} + +func expandNestedActiveDirectoryDomainTrustTrustHandshakeSecret(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func resourceActiveDirectoryDomainTrustEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + + wrappedReq := map[string]interface{}{ + "trust": obj, + } + return wrappedReq, nil +} + +func resourceActiveDirectoryDomainTrustUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + wrappedReq := map[string]interface{}{ + "targetDomainName": obj["targetDomainName"], + "targetDnsIpAddresses": obj["targetDnsIpAddresses"], + } + return wrappedReq, nil +} + +func flattenNestedActiveDirectoryDomainTrust(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { + var v interface{} + var ok bool + + v, ok = res["trusts"] + if !ok || v == nil { + return nil, nil + } + + switch v.(type) { + case []interface{}: + break + case map[string]interface{}: + // Construct list out of single nested resource + v = []interface{}{v} + default: + return nil, fmt.Errorf("expected list or map for value trusts. Actual value: %v", v) + } + + _, item, err := resourceActiveDirectoryDomainTrustFindNestedObjectInList(d, meta, v.([]interface{})) + if err != nil { + return nil, err + } + return item, nil +} + +func resourceActiveDirectoryDomainTrustFindNestedObjectInList(d *schema.ResourceData, meta interface{}, items []interface{}) (index int, item map[string]interface{}, err error) { + expectedTargetDomainName, err := expandNestedActiveDirectoryDomainTrustTargetDomainName(d.Get("target_domain_name"), d, meta.(*Config)) + if err != nil { + return -1, nil, err + } + expectedFlattenedTargetDomainName := flattenNestedActiveDirectoryDomainTrustTargetDomainName(expectedTargetDomainName, d, meta.(*Config)) + + // Search list for this resource. + for idx, itemRaw := range items { + if itemRaw == nil { + continue + } + item := itemRaw.(map[string]interface{}) + + // Decode list item before comparing. + item, err := resourceActiveDirectoryDomainTrustDecoder(d, meta, item) + if err != nil { + return -1, nil, err + } + + itemTargetDomainName := flattenNestedActiveDirectoryDomainTrustTargetDomainName(item["targetDomainName"], d, meta.(*Config)) + // isEmptyValue check so that if one is nil and the other is "", that's considered a match + if !(isEmptyValue(reflect.ValueOf(itemTargetDomainName)) && isEmptyValue(reflect.ValueOf(expectedFlattenedTargetDomainName))) && !reflect.DeepEqual(itemTargetDomainName, expectedFlattenedTargetDomainName) { + log.Printf("[DEBUG] Skipping item with targetDomainName= %#v, looking for %#v)", itemTargetDomainName, expectedFlattenedTargetDomainName) + continue + } + log.Printf("[DEBUG] Found item for resource %q: %#v)", d.Id(), item) + return idx, item, nil + } + return -1, nil, nil +} +func resourceActiveDirectoryDomainTrustDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { + v, ok := res["domainTrust"] + if !ok || v == nil { + return res, nil + } + + return v.(map[string]interface{}), nil +} diff --git a/google/resource_active_directory_domain_trust_test.go b/google/resource_active_directory_domain_trust_test.go new file mode 100644 index 00000000000..6554d6a84c7 --- /dev/null +++ b/google/resource_active_directory_domain_trust_test.go @@ -0,0 +1,101 @@ +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccActiveDirectoryDomainTrust_activeDirectoryDomainTrustBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckActiveDirectoryDomainTrustDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccActiveDirectoryDomainTrust_activeDirectoryDomainTrustBasicExample(context), + }, + { + ResourceName: "google_active_directory_domain_trust.ad-domain-trust", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"trust_handshake_secret", "domain"}, + }, + { + Config: testAccActiveDirectoryDomainTrust_activeDirectoryDomainTrustUpdate(context), + }, + { + ResourceName: "google_active_directory_domain_trust.ad-domain-trust", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"trust_handshake_secret", "domain"}, + }, + }, + }) +} + +func testAccActiveDirectoryDomainTrust_activeDirectoryDomainTrustBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_active_directory_domain_trust" "ad-domain-trust" { + domain = "test-managed-ad.com" + target_domain_name = "example-gcp.com" + target_dns_ip_addresses = ["10.1.0.100"] + trust_direction = "OUTBOUND" + trust_type = "FOREST" + trust_handshake_secret = "Testing1!" +} +`, context) +} + +func testAccActiveDirectoryDomainTrust_activeDirectoryDomainTrustUpdate(context map[string]interface{}) string { + return Nprintf(` +resource "google_active_directory_domain_trust" "ad-domain-trust" { + domain = "test-managed-ad.com" + target_domain_name = "example-gcp.com" + target_dns_ip_addresses = ["10.2.0.100"] + trust_direction = "OUTBOUND" + trust_type = "FOREST" + trust_handshake_secret = "Testing1!" +} +`, context) +} + +func testAccCheckActiveDirectoryDomainTrustDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_active_directory_domain_trust" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{ActiveDirectoryBasePath}}projects/{{project}}/locations/global/domains/{{domain}}") + if err != nil { + return err + } + + res, _ := sendRequest(config, "GET", "", url, nil) + + var v interface{} + var ok bool + + v, ok = res["trusts"] + if ok || v != nil { + return fmt.Errorf("ActiveDirectoryDomainTrust still exists at %s", url) + } + } + return nil + } +} diff --git a/website/docs/r/active_directory_domain.html.markdown b/website/docs/r/active_directory_domain.html.markdown index 2310be83b9c..803ddb88891 100644 --- a/website/docs/r/active_directory_domain.html.markdown +++ b/website/docs/r/active_directory_domain.html.markdown @@ -41,7 +41,7 @@ To get more information about Domain, see: ```hcl resource "google_active_directory_domain" "ad-domain" { - domain_name = "mydomain.org.com" + domain_name = ".org.com" locations = ["us-central1"] reserved_ip_range = "192.168.255.0/24" } diff --git a/website/docs/r/active_directory_domain_trust.html.markdown b/website/docs/r/active_directory_domain_trust.html.markdown new file mode 100644 index 00000000000..f5552fa518d --- /dev/null +++ b/website/docs/r/active_directory_domain_trust.html.markdown @@ -0,0 +1,127 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Managed Microsoft Active Directory" +layout: "google" +page_title: "Google: google_active_directory_domain_trust" +sidebar_current: "docs-google-active-directory-domain-trust" +description: |- + Adds a trust between Active Directory domains +--- + +# google\_active\_directory\_domain\_trust + +Adds a trust between Active Directory domains + + +To get more information about DomainTrust, see: + +* [API documentation](https://cloud.google.com/managed-microsoft-ad/reference/rest/v1/projects.locations.global.domains/attachTrust) +* How-to Guides + * [Active Directory Trust](https://cloud.google.com/managed-microsoft-ad/docs/create-one-way-trust) + +~> **Warning:** All arguments including `trust_handshake_secret` will be stored in the raw +state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html). + +## Example Usage - Active Directory Domain Trust Basic + + +```hcl +resource "google_active_directory_domain_trust" "ad-domain-trust" { + domain = "test-managed-ad.com" + target_domain_name = "example-gcp.com" + target_dns_ip_addresses = ["10.1.0.100"] + trust_direction = "OUTBOUND" + trust_type = "FOREST" + trust_handshake_secret = "Testing1!" +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `target_domain_name` - + (Required) + The fully qualified target domain name which will be in trust with the current domain. + +* `trust_type` - + (Required) + The type of trust represented by the trust resource. + Possible values are `FOREST` and `EXTERNAL`. + +* `trust_direction` - + (Required) + The trust direction, which decides if the current domain is trusted, trusting, or both. + Possible values are `INBOUND`, `OUTBOUND`, and `BIDIRECTIONAL`. + +* `target_dns_ip_addresses` - + (Required) + The target DNS server IP addresses which can resolve the remote domain involved in the trust. + +* `trust_handshake_secret` - + (Required) + The trust secret used for the handshake with the target domain. This will not be stored. + **Note**: This property is sensitive and will not be displayed in the plan. + +* `domain` - + (Required) + The fully qualified domain name. e.g. mydomain.myorganization.com, with the restrictions, + https://cloud.google.com/managed-microsoft-ad/reference/rest/v1/projects.locations.global.domains. + + +- - - + + +* `selective_authentication` - + (Optional) + Whether the trusted side has forest/domain wide access or selective access to an approved set of resources. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/locations/global/domains/{{domain}}/{{target_domain_name}}` + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 10 minutes. +- `update` - Default is 10 minutes. +- `delete` - Default is 10 minutes. + +## Import + +DomainTrust can be imported using any of these accepted formats: + +``` +$ terraform import google_active_directory_domain_trust.default projects/{{project}}/locations/global/domains/{{domain}}/{{target_domain_name}} +$ terraform import google_active_directory_domain_trust.default {{project}}/{{domain}}/{{target_domain_name}} +$ terraform import google_active_directory_domain_trust.default {{domain}}/{{target_domain_name}} +``` + +-> If you're importing a resource with beta features, make sure to include `-provider=google-beta` +as an argument so that Terraform uses the correct provider to import your resource. + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). diff --git a/website/google.erb b/website/google.erb index 5d4df696560..3601e9c5b79 100644 --- a/website/google.erb +++ b/website/google.erb @@ -1939,6 +1939,10 @@ google_active_directory_domain +
  • + google_active_directory_domain_trust +
  • +