diff --git a/.changelog/4805.txt b/.changelog/4805.txt new file mode 100644 index 0000000000..cd6a0d839e --- /dev/null +++ b/.changelog/4805.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +cloudflare_teams_location: support endpoints + BYOIPv6 + DNS IPv4 destinations +``` diff --git a/docs/resources/teams_location.md b/docs/resources/teams_location.md index fa7cf986c2..a135230876 100644 --- a/docs/resources/teams_location.md +++ b/docs/resources/teams_location.md @@ -40,8 +40,11 @@ resource "cloudflare_teams_location" "example" { ### Optional - `client_default` (Boolean) Indicator that this is the default location. +- `dns_destination_ips_id` (String) IPv4 binding assigned to this location. +- `dns_destination_ipv6_block_id` (String) IPv6 block binding assigned to this location. - `ecs_support` (Boolean) Indicator that this location needs to resolve EDNS queries. -- `networks` (Block Set) The networks CIDRs that comprise the location. (see [below for nested schema](#nestedblock--networks)) +- `endpoints` (Block List, Max: 1) Endpoints assigned to this location. (see [below for nested schema](#nestedblock--endpoints)) +- `networks` (Set of Object) The networks CIDRs that comprise the location. (see [below for nested schema](#nestedatt--networks)) ### Read-Only @@ -49,18 +52,112 @@ resource "cloudflare_teams_location" "example" { - `doh_subdomain` (String) The FQDN that DoH clients should be pointed at. - `id` (String) The ID of this resource. - `ip` (String) Client IP address. -- `ipv4_destination` (String) IP to direct all IPv4 DNS queries to. +- `ipv4_destination` (String) IPv4 to direct all IPv4 DNS queries to. +- `ipv4_destination_backup` (String) Backup IPv4 to direct all IPv4 DNS queries to. - -### Nested Schema for `networks` + +### Nested Schema for `endpoints` + +Optional: + +- `doh` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--doh)) +- `dot` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--dot)) +- `ipv4` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--ipv4)) +- `ipv6` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--ipv6)) + + +### Nested Schema for `endpoints.doh` Required: -- `network` (String) CIDR notation representation of the network IP. +- `enabled` (Boolean) + +Optional: + +- `networks` (List of Object) (see [below for nested schema](#nestedatt--endpoints--doh--networks)) Read-Only: -- `id` (String) The ID of this resource. +- `authentication_enabled` (Boolean) +- `require_token` (Boolean) + + +### Nested Schema for `endpoints.doh.networks` + +Optional: + +- `network` (String) + + + + +### Nested Schema for `endpoints.dot` + +Required: + +- `enabled` (Boolean) + +Optional: + +- `networks` (List of Object) (see [below for nested schema](#nestedatt--endpoints--dot--networks)) + +Read-Only: + +- `authentication_enabled` (Boolean) +- `require_token` (Boolean) + + +### Nested Schema for `endpoints.dot.networks` + +Optional: + +- `network` (String) + + + + +### Nested Schema for `endpoints.ipv4` + +Required: + +- `enabled` (Boolean) + +Read-Only: + +- `authentication_enabled` (Boolean) + + + +### Nested Schema for `endpoints.ipv6` + +Required: + +- `enabled` (Boolean) + +Optional: + +- `networks` (List of Object) (see [below for nested schema](#nestedatt--endpoints--ipv6--networks)) + +Read-Only: + +- `authentication_enabled` (Boolean) + + +### Nested Schema for `endpoints.ipv6.networks` + +Optional: + +- `network` (String) + + + + + +### Nested Schema for `networks` + +Optional: + +- `network` (String) ## Import diff --git a/docs/resources/zero_trust_dns_location.md b/docs/resources/zero_trust_dns_location.md index 1b71ebc83b..5f979696a8 100644 --- a/docs/resources/zero_trust_dns_location.md +++ b/docs/resources/zero_trust_dns_location.md @@ -40,8 +40,11 @@ resource "cloudflare_zero_trust_dns_location" "example" { ### Optional - `client_default` (Boolean) Indicator that this is the default location. +- `dns_destination_ips_id` (String) IPv4 binding assigned to this location. +- `dns_destination_ipv6_block_id` (String) IPv6 block binding assigned to this location. - `ecs_support` (Boolean) Indicator that this location needs to resolve EDNS queries. -- `networks` (Block Set) The networks CIDRs that comprise the location. (see [below for nested schema](#nestedblock--networks)) +- `endpoints` (Block List, Max: 1) Endpoints assigned to this location. (see [below for nested schema](#nestedblock--endpoints)) +- `networks` (Set of Object) The networks CIDRs that comprise the location. (see [below for nested schema](#nestedatt--networks)) ### Read-Only @@ -49,18 +52,112 @@ resource "cloudflare_zero_trust_dns_location" "example" { - `doh_subdomain` (String) The FQDN that DoH clients should be pointed at. - `id` (String) The ID of this resource. - `ip` (String) Client IP address. -- `ipv4_destination` (String) IP to direct all IPv4 DNS queries to. +- `ipv4_destination` (String) IPv4 to direct all IPv4 DNS queries to. +- `ipv4_destination_backup` (String) Backup IPv4 to direct all IPv4 DNS queries to. - -### Nested Schema for `networks` + +### Nested Schema for `endpoints` + +Optional: + +- `doh` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--doh)) +- `dot` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--dot)) +- `ipv4` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--ipv4)) +- `ipv6` (Block List, Max: 1) (see [below for nested schema](#nestedblock--endpoints--ipv6)) + + +### Nested Schema for `endpoints.doh` Required: -- `network` (String) CIDR notation representation of the network IP. +- `enabled` (Boolean) + +Optional: + +- `networks` (List of Object) (see [below for nested schema](#nestedatt--endpoints--doh--networks)) Read-Only: -- `id` (String) The ID of this resource. +- `authentication_enabled` (Boolean) +- `require_token` (Boolean) + + +### Nested Schema for `endpoints.doh.networks` + +Optional: + +- `network` (String) + + + + +### Nested Schema for `endpoints.dot` + +Required: + +- `enabled` (Boolean) + +Optional: + +- `networks` (List of Object) (see [below for nested schema](#nestedatt--endpoints--dot--networks)) + +Read-Only: + +- `authentication_enabled` (Boolean) +- `require_token` (Boolean) + + +### Nested Schema for `endpoints.dot.networks` + +Optional: + +- `network` (String) + + + + +### Nested Schema for `endpoints.ipv4` + +Required: + +- `enabled` (Boolean) + +Read-Only: + +- `authentication_enabled` (Boolean) + + + +### Nested Schema for `endpoints.ipv6` + +Required: + +- `enabled` (Boolean) + +Optional: + +- `networks` (List of Object) (see [below for nested schema](#nestedatt--endpoints--ipv6--networks)) + +Read-Only: + +- `authentication_enabled` (Boolean) + + +### Nested Schema for `endpoints.ipv6.networks` + +Optional: + +- `network` (String) + + + + + +### Nested Schema for `networks` + +Optional: + +- `network` (String) ## Import diff --git a/internal/sdkv2provider/resource_cloudflare_teams_location.go b/internal/sdkv2provider/resource_cloudflare_teams_location.go index 1ca7fdef4c..5b2301ae66 100644 --- a/internal/sdkv2provider/resource_cloudflare_teams_location.go +++ b/internal/sdkv2provider/resource_cloudflare_teams_location.go @@ -68,6 +68,7 @@ func resourceCloudflareTeamsLocationRead(ctx context.Context, d *schema.Resource if err := d.Set("networks", flattenTeamsLocationNetworks(location.Networks)); err != nil { return diag.FromErr(fmt.Errorf("error parsing Location networks")) } + if err := d.Set("ip", location.Ip); err != nil { return diag.FromErr(fmt.Errorf("error parsing Location IP")) } @@ -80,12 +81,26 @@ func resourceCloudflareTeamsLocationRead(ctx context.Context, d *schema.Resource if err := d.Set("ipv4_destination", location.IPv4Destination); err != nil { return diag.FromErr(fmt.Errorf("error parsing Location IPv4 destination")) } + if err := d.Set("ipv4_destination_backup", location.IPv4DestinationBackup); err != nil { + return diag.FromErr(fmt.Errorf("error parsing Location IPv4 destination")) + } if err := d.Set("client_default", location.ClientDefault); err != nil { return diag.FromErr(fmt.Errorf("error parsing Location client default")) } if err := d.Set("ecs_support", location.ECSSupport); err != nil { return diag.FromErr(fmt.Errorf("error parsing Location ecs support")) } + if err := d.Set("dns_destination_ipv6_block_id", location.DNSDestinationIPv6BlockID); err != nil { + return diag.FromErr(fmt.Errorf("error parsing Location dns_destination_ipv6_block_id")) + } + + if err := d.Set("dns_destination_ips_id", location.DNSDestinationIPsID); err != nil { + return diag.FromErr(fmt.Errorf("error parsing Location dns_destination_ipv6_block_id")) + } + + if err := d.Set("endpoints", flattenTeamsEndpoints(location.Endpoints)); err != nil { + return diag.FromErr(fmt.Errorf("error parsing Location endpoints")) + } return nil } @@ -97,7 +112,6 @@ func resourceCloudflareTeamsLocationCreate(ctx context.Context, d *schema.Resour if err != nil { return diag.FromErr(fmt.Errorf("error creating Teams Location for account %q: %w, %v", accountID, err, networks)) } - newTeamLocation := cloudflare.TeamsLocation{ Name: d.Get("name").(string), Networks: networks, @@ -105,6 +119,22 @@ func resourceCloudflareTeamsLocationCreate(ctx context.Context, d *schema.Resour ECSSupport: cloudflare.BoolPtr(d.Get("ecs_support").(bool)), } + endpoints, err := inflateTeamsLocationEndpoint(d.Get("endpoints")) + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Teams Location endpoints for account %q: %w, %v", accountID, err, networks)) + } else if endpoints != nil { + newTeamLocation.Endpoints = endpoints + } + + destinationIpId, ok := d.Get("dns_destination_ips_id").(string) + if ok && destinationIpId != "" { + newTeamLocation.DNSDestinationIPsID = &destinationIpId + } + destinationIpv6Id, ok := d.Get("dns_destination_ipv6_block_id").(string) + if ok && destinationIpv6Id != "" { + newTeamLocation.DNSDestinationIPv6BlockID = &destinationIpv6Id + } + tflog.Debug(ctx, fmt.Sprintf("Creating Cloudflare Teams Location from struct: %+v", newTeamLocation)) location, err := client.CreateTeamsLocation(ctx, accountID, newTeamLocation) @@ -188,7 +218,6 @@ func inflateTeamsLocationNetworks(networks interface{}) ([]cloudflare.TeamsLocat return nil, fmt.Errorf("error parsing network") } networkStructs = append(networkStructs, cloudflare.TeamsLocationNetwork{ - ID: network["id"].(string), Network: network["network"].(string), }) } @@ -196,13 +225,205 @@ func inflateTeamsLocationNetworks(networks interface{}) ([]cloudflare.TeamsLocat return networkStructs, nil } +func inflateTeamsLocationNetworksFromList(networks interface{}) ([]cloudflare.TeamsLocationNetwork, error) { + var networkStructs []cloudflare.TeamsLocationNetwork + if networks != nil { + networkList, ok := networks.([]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing network list") + } + for _, i := range networkList { + network, ok := i.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing network") + } + networkStructs = append(networkStructs, cloudflare.TeamsLocationNetwork{ + Network: network["network"].(string), + }) + } + } + return networkStructs, nil +} + +func inflateTeamsLocationEndpoint(endpoint interface{}) (*cloudflare.TeamsLocationEndpoints, error) { + if endpoint == nil { + return nil, nil + } + + epList, ok := endpoint.([]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing endpoint list") + } + for _, i := range epList { + epItem, ok := i.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing endpoint") + } + ipv4Endpoint, err := inflateIpv4Endpoint(epItem["ipv4"]) + if err != nil { + return nil, fmt.Errorf("error parsing ipv4 endpoint") + } + + ipv6Endpoint, err := inflateIpv6Endpoint(epItem["ipv6"]) + if err != nil { + return nil, fmt.Errorf("error parsing ipv6 endpoint") + } + + dotEndpoint, err := inflateDoTEndpoint(epItem["dot"]) + if err != nil { + return nil, fmt.Errorf("error parsing dot endpoint") + } + + dohEndpoint, err := inflateDohEndpoint(epItem["doh"]) + if err != nil { + return nil, fmt.Errorf("error parsing doh endpoint") + } + + return &cloudflare.TeamsLocationEndpoints{ + IPv4Endpoint: *ipv4Endpoint, + IPv6Endpoint: *ipv6Endpoint, + DotEndpoint: *dotEndpoint, + DohEndpoint: *dohEndpoint, + }, nil + } + return nil, fmt.Errorf("empty endpoint") +} + +func inflateIpv4Endpoint(item interface{}) (*cloudflare.TeamsLocationIPv4EndpointFields, error) { + epItems, ok := item.([]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing endpoint item") + } + + return &cloudflare.TeamsLocationIPv4EndpointFields{ + Enabled: firstItemInSet(epItems)["enabled"].(bool), + }, nil +} + +func firstItemInSet(l []interface{}) map[string]interface{} { + return l[0].(map[string]interface{}) +} + +func inflateIpv6Endpoint(item interface{}) (*cloudflare.TeamsLocationIPv6EndpointFields, error) { + epItems, ok := item.([]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing endpoint item") + } + + epItem := firstItemInSet(epItems) + + networks, err := inflateTeamsLocationNetworksFromList(epItem["networks"]) + if err != nil { + return nil, fmt.Errorf("error parsing endpoint ipv6 networks") + } + return &cloudflare.TeamsLocationIPv6EndpointFields{ + TeamsLocationEndpointFields: cloudflare.TeamsLocationEndpointFields{ + Enabled: epItem["enabled"].(bool), + Networks: networks, + }, + }, nil +} + +func inflateDoTEndpoint(item interface{}) (*cloudflare.TeamsLocationDotEndpointFields, error) { + epItems, ok := item.([]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing endpoint item") + } + + epItem := firstItemInSet(epItems) + networks, err := inflateTeamsLocationNetworksFromList(epItem["networks"]) + if err != nil { + return nil, fmt.Errorf("error parsing endpoint dot networks") + } + return &cloudflare.TeamsLocationDotEndpointFields{ + RequireToken: epItem["require_token"].(bool), + TeamsLocationEndpointFields: cloudflare.TeamsLocationEndpointFields{ + Enabled: epItem["enabled"].(bool), + Networks: networks, + }, + }, nil +} + +func inflateDohEndpoint(item interface{}) (*cloudflare.TeamsLocationDohEndpointFields, error) { + epItems, ok := item.([]interface{}) + if !ok { + return nil, fmt.Errorf("error parsing endpoint item") + } + + epItem := firstItemInSet(epItems) + + networks, err := inflateTeamsLocationNetworksFromList(epItem["networks"]) + if err != nil { + return nil, fmt.Errorf("error parsing endpoint dot networks") + } + return &cloudflare.TeamsLocationDohEndpointFields{ + RequireToken: epItem["require_token"].(bool), + TeamsLocationEndpointFields: cloudflare.TeamsLocationEndpointFields{ + Enabled: epItem["enabled"].(bool), + Networks: networks, + }, + }, nil +} + func flattenTeamsLocationNetworks(networks []cloudflare.TeamsLocationNetwork) []interface{} { var flattenedNetworks []interface{} for _, net := range networks { flattenedNetworks = append(flattenedNetworks, map[string]interface{}{ - "id": net.ID, "network": net.Network, }) } return flattenedNetworks } + +func flattenTeamsLocationNetworksIntoList(networks []cloudflare.TeamsLocationNetwork) []interface{} { + var flattenedNetworks []interface{} + for _, net := range networks { + flattenedNetworks = append(flattenedNetworks, map[string]interface{}{ + "network": net.Network, + }) + } + return flattenedNetworks +} + +func flattenTeamsEndpoints(endpoint *cloudflare.TeamsLocationEndpoints) []interface{} { + flattenedEndpoints := map[string]interface{}{ + "ipv4": flattenTeamsEndpointIpv4Field(endpoint.IPv4Endpoint), + "ipv6": flattenTeamsEndpointIpv6Field(endpoint.IPv6Endpoint), + "doh": flattenTeamsEndpointDOHField(endpoint.DohEndpoint), + "dot": flattenTeamsEndpointDOTField(endpoint.DotEndpoint), + } + return []interface{}{flattenedEndpoints} +} + +func flattenTeamsEndpointIpv4Field(field cloudflare.TeamsLocationIPv4EndpointFields) []map[string]interface{} { + return []map[string]interface{}{{ + "enabled": field.Enabled, + "authentication_enabled": field.AuthenticationEnabled, + }} +} + +func flattenTeamsEndpointIpv6Field(field cloudflare.TeamsLocationIPv6EndpointFields) []map[string]interface{} { + return []map[string]interface{}{{ + "enabled": field.Enabled, + "authentication_enabled": field.AuthenticationEnabledUIHelper, + "networks": flattenTeamsLocationNetworksIntoList(field.Networks), + }} +} + +func flattenTeamsEndpointDOTField(field cloudflare.TeamsLocationDotEndpointFields) []map[string]interface{} { + return []map[string]interface{}{{ + "require_token": field.RequireToken, + "enabled": field.Enabled, + "authentication_enabled": field.AuthenticationEnabledUIHelper, + "networks": flattenTeamsLocationNetworksIntoList(field.Networks), + }} +} + +func flattenTeamsEndpointDOHField(field cloudflare.TeamsLocationDohEndpointFields) []map[string]interface{} { + return []map[string]interface{}{{ + "require_token": field.RequireToken, + "enabled": field.Enabled, + "authentication_enabled": field.AuthenticationEnabledUIHelper, + "networks": flattenTeamsLocationNetworksIntoList(field.Networks), + }} +} diff --git a/internal/sdkv2provider/resource_cloudflare_teams_location_test.go b/internal/sdkv2provider/resource_cloudflare_teams_location_test.go index 869900740b..d09baa5c7c 100644 --- a/internal/sdkv2provider/resource_cloudflare_teams_location_test.go +++ b/internal/sdkv2provider/resource_cloudflare_teams_location_test.go @@ -37,6 +37,24 @@ func TestAccCloudflareTeamsLocationBasic(t *testing.T) { resource.TestCheckResourceAttr(name, "name", rnd), resource.TestCheckResourceAttr(name, "client_default", "false"), resource.TestCheckResourceAttr(name, "ecs_support", "false"), + resource.TestCheckResourceAttr(name, "networks.#", "1"), + resource.TestCheckResourceAttr(name, "networks.0.network", "2.5.6.200/32"), + resource.TestCheckResourceAttr(name, "endpoints.#", "1"), + resource.TestCheckResourceAttr(name, "endpoints.0.ipv4.0.enabled", "true"), + resource.TestCheckResourceAttr(name, "endpoints.0.ipv4.0.authentication_enabled", "true"), + + resource.TestCheckResourceAttr(name, "endpoints.0.ipv6.0.enabled", "true"), + resource.TestCheckResourceAttr(name, "endpoints.0.ipv6.0.authentication_enabled", "true"), + resource.TestCheckResourceAttr(name, "endpoints.0.ipv6.0.networks.0.network", "2a09:bac5:50c3:400::6b:57/128"), + + resource.TestCheckResourceAttr(name, "endpoints.0.doh.0.enabled", "true"), + resource.TestCheckResourceAttr(name, "endpoints.0.doh.0.authentication_enabled", "true"), + resource.TestCheckResourceAttr(name, "endpoints.0.doh.0.networks.0.network", "2.5.6.202/32"), + resource.TestCheckResourceAttr(name, "endpoints.0.doh.0.networks.1.network", "3.5.6.203/32"), + + resource.TestCheckResourceAttr(name, "endpoints.0.dot.0.enabled", "true"), + resource.TestCheckResourceAttr(name, "endpoints.0.dot.0.authentication_enabled", "true"), + resource.TestCheckResourceAttr(name, "endpoints.0.dot.0.networks.0.network", "2.5.6.201/32"), ), }, }, @@ -48,6 +66,27 @@ func testAccCloudflareTeamsLocationConfigBasic(rnd, accountID string) string { resource "cloudflare_zero_trust_dns_location" "%[1]s" { name = "%[1]s" account_id = "%[2]s" + networks = [{ network = "2.5.6.200/32" }] + + dns_destination_ips_id = "0e4a32c6-6fb8-4858-9296-98f51631e8e6" + + endpoints { + ipv4 { + enabled = true + } + ipv6 { + enabled = true + networks = [ { network = "2a09:bac5:50c3:400::6b:57/128" } ] + } + dot { + enabled = true + networks = [ { network = "2.5.6.201/32" } ] + } + doh { + enabled = true + networks = [ { network = "2.5.6.202/32" }, { network = "3.5.6.203/32" } ] + } + } } `, rnd, accountID) } diff --git a/internal/sdkv2provider/schema_cloudflare_teams_location.go b/internal/sdkv2provider/schema_cloudflare_teams_location.go index 08334f0abf..1785590b38 100644 --- a/internal/sdkv2provider/schema_cloudflare_teams_location.go +++ b/internal/sdkv2provider/schema_cloudflare_teams_location.go @@ -5,7 +5,18 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) +var TeamsLocationNetworkSchema = &schema.Resource{ + Schema: map[string]*schema.Schema{ + "network": { + Type: schema.TypeString, + Required: true, + Description: "CIDR notation representation of the network IP.", + }, + }, +} + func resourceCloudflareTeamsLocationSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ consts.AccountIDSchemaKey: { Description: consts.AccountIDSchemaDescription, @@ -21,19 +32,8 @@ func resourceCloudflareTeamsLocationSchema() map[string]*schema.Schema { Type: schema.TypeSet, Optional: true, Description: "The networks CIDRs that comprise the location.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - }, - "network": { - Type: schema.TypeString, - Required: true, - Description: "CIDR notation representation of the network IP.", - }, - }, - }, + ConfigMode: schema.SchemaConfigModeAttr, + Elem: TeamsLocationNetworkSchema, }, "client_default": { Type: schema.TypeBool, @@ -63,7 +63,133 @@ func resourceCloudflareTeamsLocationSchema() map[string]*schema.Schema { "ipv4_destination": { Type: schema.TypeString, Computed: true, - Description: "IP to direct all IPv4 DNS queries to.", + Description: "IPv4 to direct all IPv4 DNS queries to.", + }, + "ipv4_destination_backup": { + Type: schema.TypeString, + Computed: true, + Description: "Backup IPv4 to direct all IPv4 DNS queries to.", + }, + "dns_destination_ips_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "IPv4 binding assigned to this location", + }, + "dns_destination_ipv6_block_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "IPv6 block binding assigned to this location", + }, + "endpoints": { + Type: schema.TypeList, + Description: "Endpoints assigned to this location", + Optional: true, + MaxItems: 1, + Elem: TeamsLocationEndpointSchema, }, } } + +var TeamsLocationEndpointSchema = &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ipv4": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "authentication_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + "ipv6": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "authentication_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "networks": { + Type: schema.TypeList, + ConfigMode: schema.SchemaConfigModeAttr, + MinItems: 1, + Optional: true, + Elem: TeamsLocationNetworkSchema, + }, + }, + }, + }, + "doh": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "require_token": { + Type: schema.TypeBool, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "authentication_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "networks": { + Type: schema.TypeList, + MinItems: 1, + Optional: true, + Elem: TeamsLocationNetworkSchema, + ConfigMode: schema.SchemaConfigModeAttr, + }, + }, + }, + }, + "dot": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "require_token": { + Type: schema.TypeBool, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + "authentication_enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "networks": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: TeamsLocationNetworkSchema, + ConfigMode: schema.SchemaConfigModeAttr, + }, + }, + }, + }, + }, +}