diff --git a/ibm/service/vpc/resource_ibm_is_vpc.go b/ibm/service/vpc/resource_ibm_is_vpc.go index 68e75e6fad..da9104f86c 100644 --- a/ibm/service/vpc/resource_ibm_is_vpc.go +++ b/ibm/service/vpc/resource_ibm_is_vpc.go @@ -190,6 +190,18 @@ func ResourceIBMISVPC() *schema.Resource { Computed: true, Description: "The type of the DNS resolver used for the VPC.- `delegated`: DNS server addresses are provided by the DNS resolver of the VPC specified in `dns.resolver.vpc`.- `manual`: DNS server addresses are specified in `dns.resolver.manual_servers`.- `system`: DNS server addresses are provided by the system.", }, + + "dns_binding_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + Description: "The VPC dns binding id whose DNS resolver provides the DNS server addresses for this VPC.", + }, + "dns_binding_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC.", + }, "vpc_id": &schema.Schema{ Type: schema.TypeString, Optional: true, @@ -714,6 +726,51 @@ func vpcCreate(d *schema.ResourceData, meta interface{}, name, apm, rg string, i return err } + if dnsresolvertpeOk, ok := d.GetOk("dns.0.resolver.0.type"); ok { + if dnsresolvertpeOk.(string) == "delegated" && d.Get("dns.0.resolver.0.vpc_id").(string) != "" { + vpcId := d.Get("dns.0.resolver.0.vpc_id").(string) + createDnsBindings := &vpcv1.CreateVPCDnsResolutionBindingOptions{ + VPCID: vpc.ID, + VPC: &vpcv1.VPCIdentity{ + ID: &vpcId, + }, + } + if bindingNameOk, ok := d.GetOk("dns.0.resolver.0.dns_binding_name"); ok { + bindingName := bindingNameOk.(string) + createDnsBindings.Name = &bindingName + } + _, response, err := sess.CreateVPCDnsResolutionBinding(createDnsBindings) + if err != nil { + log.Printf("[DEBUG] CreateVPCDnsResolutionBindingWithContext failed %s\n%s", err, response) + return fmt.Errorf("[ERROR] CreateVPCDnsResolutionBinding failed in vpc resource %s\n%s", err, response) + } + resolverType := "delegated" + dnsPatch := &vpcv1.VpcdnsPatch{ + Resolver: &vpcv1.VpcdnsResolverPatch{ + Type: &resolverType, + VPC: &vpcv1.VpcdnsResolverVPCPatch{ + ID: &vpcId, + }, + }, + } + vpcPatchModel := &vpcv1.VPCPatch{} + vpcPatchModel.Dns = dnsPatch + vpcPatchModelAsPatch, err := vpcPatchModel.AsPatch() + if err != nil { + return fmt.Errorf("[ERROR] CreateVPCDnsResolutionBinding failed in vpcpatch as patch %s", err) + } + updateVpcOptions := &vpcv1.UpdateVPCOptions{ + ID: vpc.ID, + } + updateVpcOptions.VPCPatch = vpcPatchModelAsPatch + _, response, err = sess.UpdateVPC(updateVpcOptions) + if err != nil { + log.Printf("[DEBUG] Update vpc with delegated failed %s\n%s", err, response) + return fmt.Errorf("[ERROR] Update vpc with delegated failed in vpc resource %s\n%s", err, response) + } + } + } + if sgAclRules, ok := d.GetOk(isVPCNoSgAclRules); ok { sgAclRules := sgAclRules.(bool) if sgAclRules { @@ -920,6 +977,38 @@ func vpcGet(d *schema.ResourceData, meta interface{}, id string) error { if err != nil { return err } + resolverMapArray := dnsMap["resolver"].([]map[string]interface{}) + resolverMap := resolverMapArray[0] + if resolverMap["type"] != nil && resolverMap["vpc_id"] != nil { + resType := resolverMap["type"].(*string) + resVpc := resolverMap["vpc_id"].(string) + if *resType == "delegated" { + listVPCDnsResolutionBindingOptions := &vpcv1.ListVPCDnsResolutionBindingsOptions{ + VPCID: vpc.ID, + } + + pager, err := sess.NewVPCDnsResolutionBindingsPager(listVPCDnsResolutionBindingOptions) + if err != nil { + return fmt.Errorf("[ERROR] Error getting VPC dns bindings: %s", err) + } + var allResults []vpcv1.VpcdnsResolutionBinding + for pager.HasNext() { + nextPage, err := pager.GetNext() + if err != nil { + return fmt.Errorf("[ERROR] Error getting VPC dns bindings pager next: %s", err) + } + allResults = append(allResults, nextPage...) + } + for _, binding := range allResults { + if *binding.VPC.ID == resVpc { + resolverMap["dns_binding_id"] = binding.ID + resolverMap["dns_binding_name"] = binding.Name + resolverMapArray[0] = resolverMap + dnsMap["resolver"] = resolverMapArray + } + } + } + } if err = d.Set(isVPCDns, []map[string]interface{}{dnsMap}); err != nil { return fmt.Errorf("[ERROR] Error setting dns: %s", err) } @@ -1543,7 +1632,11 @@ func resourceIBMIsVPCMapToVpcdnsPrototype(modelMap map[string]interface{}) (*vpc func resourceIBMIsVPCMapToVpcdnsResolverPrototype(modelMap map[string]interface{}) (vpcv1.VpcdnsResolverPrototypeIntf, error) { model := &vpcv1.VpcdnsResolverPrototype{} if modelMap["type"] != nil && modelMap["type"].(string) != "" { - model.Type = core.StringPtr(modelMap["type"].(string)) + if modelMap["type"].(string) == "delegated" { + model.Type = core.StringPtr("system") + } else { + model.Type = core.StringPtr(modelMap["type"].(string)) + } } if modelMap["manual_servers"] != nil && modelMap["manual_servers"].(*schema.Set).Len() > 0 { model.Type = core.StringPtr("manual") @@ -1583,6 +1676,7 @@ func resourceIBMIsVPCVpcdnsToMap(model *vpcv1.Vpcdns, vpcId, vpcCrn string) (map return modelMap, err } modelMap["resolver"] = []map[string]interface{}{resolverMap} + return modelMap, nil } diff --git a/ibm/service/vpc/resource_ibm_is_vpc_test.go b/ibm/service/vpc/resource_ibm_is_vpc_test.go index 6f9cc88a66..5468af48d1 100644 --- a/ibm/service/vpc/resource_ibm_is_vpc_test.go +++ b/ibm/service/vpc/resource_ibm_is_vpc_test.go @@ -298,6 +298,53 @@ func TestAccIBMISVPC_dns_delegated(t *testing.T) { }, }) } +func TestAccIBMISVPC_dns_delegated_first(t *testing.T) { + var vpc string + name1 := fmt.Sprintf("terraformvpcuat-%d", acctest.RandIntRange(10, 100)) + name2 := fmt.Sprintf("terraformvpcuat-%d", acctest.RandIntRange(10, 100)) + subnet1 := fmt.Sprintf("terraformsubnet-%d", acctest.RandIntRange(10, 100)) + subnet2 := fmt.Sprintf("terraformsubnet-%d", acctest.RandIntRange(10, 100)) + resourecinstance := fmt.Sprintf("terraformresource-%d", acctest.RandIntRange(10, 100)) + resolver1 := fmt.Sprintf("terraformresolver-%d", acctest.RandIntRange(10, 100)) + binding := fmt.Sprintf("terraformbinding-%d", acctest.RandIntRange(10, 100)) + enableHubTrue := true + enableHubFalse := false + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMISVPCDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMISVPCDnsDelegatedFirstConfig(name1, name2, subnet1, subnet2, resourecinstance, resolver1, binding, enableHubTrue, enableHubFalse), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMISVPCExists("ibm_is_vpc.hub_true", vpc), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_true", "name", name1), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_false_delegated", "name", name2), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_true", "dns.0.enable_hub", fmt.Sprintf("%t", enableHubTrue)), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_true", "dns.0.resolver.0.type", "system"), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_false_delegated", "dns.0.enable_hub", fmt.Sprintf("%t", enableHubFalse)), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.type", "delegated"), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_false_delegated", "dns.0.resolution_binding_count", "1"), + resource.TestCheckResourceAttr( + "ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_name", binding), + resource.TestCheckResourceAttrSet( + "ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.dns_binding_id"), + resource.TestCheckResourceAttrSet( + "ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_id"), + resource.TestCheckResourceAttrSet( + "ibm_is_vpc.hub_false_delegated", "dns.0.resolver.0.vpc_name"), + ), + }, + }, + }) +} func TestAccIBMISVPC_basic_apm(t *testing.T) { var vpc string @@ -597,6 +644,69 @@ func testAccCheckIBMISVPCDnsDelegatedConfig(vpcname, vpcname2, subnetname1, subn `, vpcname, enableHub, vpcname2, enablehubfalse, subnetname1, acc.ISZoneName, subnetname2, acc.ISZoneName, subnetname3, acc.ISZoneName, subnetname4, acc.ISZoneName, resourceinstance, resolver1, resolver2, bindingname) +} +func testAccCheckIBMISVPCDnsDelegatedFirstConfig(vpcname, vpcname2, subnetname1, subnetname2, resourceinstance, resolver1, bindingname string, enableHub, enablehubfalse bool) string { + return fmt.Sprintf(` + data "ibm_resource_group" "rg" { + is_default = true + } + + resource ibm_is_vpc hub_true { + name = "%s" + dns { + enable_hub = %t + } + } + + resource ibm_is_vpc hub_false_delegated { + depends_on = [ ibm_dns_custom_resolver.test_hub_true ] + name = "%s" + dns { + enable_hub = %t + resolver { + type = "delegated" + vpc_id = ibm_is_vpc.hub_true.id + dns_binding_name = "%s" + } + } + } + + resource "ibm_is_subnet" "hub_true_sub1" { + name = "%s" + vpc = ibm_is_vpc.hub_true.id + zone = "%s" + total_ipv4_address_count = 16 + } + resource "ibm_is_subnet" "hub_true_sub2" { + name = "%s" + vpc = ibm_is_vpc.hub_true.id + zone = "%s" + total_ipv4_address_count = 16 + } + resource "ibm_resource_instance" "dns-cr-instance" { + name = "%s" + resource_group_id = data.ibm_resource_group.rg.id + location = "global" + service = "dns-svcs" + plan = "standard-dns" + } + resource "ibm_dns_custom_resolver" "test_hub_true" { + name = "%s" + instance_id = ibm_resource_instance.dns-cr-instance.guid + description = "new test CR - TF" + high_availability = true + enabled = true + locations { + subnet_crn = ibm_is_subnet.hub_true_sub1.crn + enabled = true + } + locations { + subnet_crn = ibm_is_subnet.hub_true_sub2.crn + enabled = true + } + } + `, vpcname, enableHub, vpcname2, enablehubfalse, bindingname, subnetname1, acc.ISZoneName, subnetname2, acc.ISZoneName, resourceinstance, resolver1) + } func testAccCheckIBMISVPCDnsDelegatedUpdate1Config(vpcname, vpcname2, subnetname1, subnetname2, subnetname3, subnetname4, resourceinstance, resolver1, resolver2, bindingname string, enableHub, enablehubfalse bool) string { return fmt.Sprintf(` diff --git a/website/docs/r/is_vpc.html.markdown b/website/docs/r/is_vpc.html.markdown index 0249e5778b..1ef48546e8 100644 --- a/website/docs/r/is_vpc.html.markdown +++ b/website/docs/r/is_vpc.html.markdown @@ -83,6 +83,22 @@ resource "ibm_is_vpc" "example-system" { } } +// delegated type resolver + +resource "ibm_is_vpc" "example-delegated" { + // required : add a dependency on ibm dns custom resolver of the hub vpc + depends_on = [ ibm_dns_custom_resolver.example-hub ] + name = "example-hub-false-delegated" + dns { + enable_hub = false + resolver { + type = "delegated" + vpc_id = ibm_is_vpc.example.id + dns_binding_name = "example-vpc-binding" + } + } +} + ``` ## Timeouts @@ -116,6 +132,9 @@ Review the argument references that you can specify for your resource. - `resolver` - (Optional, List) The zone list this backup policy plan will create snapshot clones in. Nested scheme for `resolver`: + - `dns_binding_id` - (String) The VPC dns binding id whose DNS resolver provides the DNS server addresses for this VPC. (If any) + - `dns_binding_name` - (Optional, String) The VPC dns binding name whose DNS resolver provides the DNS server addresses for this VPC. Only applicable for `delegated`, providing value would create binding with this name. + ~> **Note:** `manual_servers` must be set if and only if `dns.resolver.type` is manual. - `manual_servers` - (Optional, List) The DNS servers to use for this VPC, replacing any existing servers. All the DNS servers must either: **have a unique zone_affinity**, or **not have a zone_affinity**. @@ -139,7 +158,7 @@ Review the argument references that you can specify for your resource. ~> **Note:** Updating from `manual` requires dns resolver `manual_servers` to be specified as null.
Updating to `manual` requires dns resolver `manual_servers` to be specified and not empty.
- Updating from `delegated` requires `dns.resolver.vpc` to be specified as null. + Updating from `delegated` requires `dns.resolver.vpc` to be specified as null. If type is `delegated` while creation then `vpc_id` is required - `vpc_id` - (Optional, List) (update only) The VPC ID to provide DNS server addresses for this VPC. The specified VPC must be configured with a DNS Services custom resolver and must be in one of this VPC's DNS resolution bindings. Mutually exclusive with `vpc_crn` ~> **Note:**