From b1ca47fcbb634258c116603ac09824620620b782 Mon Sep 17 00:00:00 2001 From: Anvitha-Jain Date: Thu, 19 Jan 2023 06:13:36 -0800 Subject: [PATCH] [minor_change] Add aci_cloud_ipsec_tunnel_subnet_pool, aci_cloud_external_network and aci_cloud_external_network_vpn_network resources and datasources for Cloud APIC (#948) --- ...data_source_aci_cloudtemplateextnetwork.go | 109 ++++ ..._aci_cloudtemplateipsectunnelsubnetpool.go | 50 ++ ...data_source_aci_cloudtemplatevpnnetwork.go | 154 ++++++ aci/provider.go | 6 + aci/resource_aci_cloudtemplateextnetwork.go | 442 ++++++++++++++++ ...source_aci_cloudtemplateextnetwork_test.go | 149 ++++++ ..._aci_cloudtemplateipsectunnelsubnetpool.go | 186 +++++++ ...cloudtemplateipsectunnelsubnetpool_test.go | 149 ++++++ aci/resource_aci_cloudtemplatevpnnetwork.go | 498 ++++++++++++++++++ ...source_aci_cloudtemplatevpnnetwork_test.go | 157 ++++++ aci/resource_aci_infraportblk.go | 1 - aci/resource_aci_vzbrcp.go | 7 - aci/resource_aci_vzentry.go | 1 - aci/utils.go | 18 + examples/cloud_external_network/aws/main.tf | 106 ++++ examples/cloud_external_network/azure/main.tf | 73 +++ examples/cloud_external_network/gcp/main.tf | 73 +++ .../cloud_ipsec_tunnel_subnet_pool/main.tf | 27 + .../d/cloud_external_network.html.markdown | 50 ++ ...oud_ipsec_tunnel_subnet_pool.html.markdown | 42 ++ .../docs/d/cloud_vpn_network.html.markdown | 53 ++ ...d.html.markdown => cloud_ad.html.markdown} | 0 ...rkdown => cloud_credentials.html.markdown} | 0 .../r/cloud_external_network.html.markdown | 74 +++ ...oud_ipsec_tunnel_subnet_pool.html.markdown | 49 ++ .../docs/r/cloud_vpn_network.html.markdown | 66 +++ 26 files changed, 2531 insertions(+), 9 deletions(-) create mode 100644 aci/data_source_aci_cloudtemplateextnetwork.go create mode 100644 aci/data_source_aci_cloudtemplateipsectunnelsubnetpool.go create mode 100644 aci/data_source_aci_cloudtemplatevpnnetwork.go create mode 100644 aci/resource_aci_cloudtemplateextnetwork.go create mode 100644 aci/resource_aci_cloudtemplateextnetwork_test.go create mode 100644 aci/resource_aci_cloudtemplateipsectunnelsubnetpool.go create mode 100644 aci/resource_aci_cloudtemplateipsectunnelsubnetpool_test.go create mode 100644 aci/resource_aci_cloudtemplatevpnnetwork.go create mode 100644 aci/resource_aci_cloudtemplatevpnnetwork_test.go create mode 100644 examples/cloud_external_network/aws/main.tf create mode 100644 examples/cloud_external_network/azure/main.tf create mode 100644 examples/cloud_external_network/gcp/main.tf create mode 100644 examples/cloud_ipsec_tunnel_subnet_pool/main.tf create mode 100644 website/docs/d/cloud_external_network.html.markdown create mode 100644 website/docs/d/cloud_ipsec_tunnel_subnet_pool.html.markdown create mode 100644 website/docs/d/cloud_vpn_network.html.markdown rename website/docs/r/{cloudad.html.markdown => cloud_ad.html.markdown} (100%) rename website/docs/r/{cloudcredentials.html.markdown => cloud_credentials.html.markdown} (100%) create mode 100644 website/docs/r/cloud_external_network.html.markdown create mode 100644 website/docs/r/cloud_ipsec_tunnel_subnet_pool.html.markdown create mode 100644 website/docs/r/cloud_vpn_network.html.markdown diff --git a/aci/data_source_aci_cloudtemplateextnetwork.go b/aci/data_source_aci_cloudtemplateextnetwork.go new file mode 100644 index 000000000..967a065f8 --- /dev/null +++ b/aci/data_source_aci_cloudtemplateextnetwork.go @@ -0,0 +1,109 @@ +package aci + +import ( + "context" + "fmt" + "log" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceAciCloudTemplateforExternalNetwork() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceAciCloudTemplateforExternalNetworkRead, + SchemaVersion: 1, + Schema: AppendBaseAttrSchema(AppendNameAliasAttrSchema(map[string]*schema.Schema{ + "hub_network_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "vrf_dn": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "regions": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Computed: true, + }, + "cloud_vendor": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + "aws", + "azure", + "gcp", + }, false), + }, + "router_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + "c8kv", + "tgw", + }, false), + }, + })), + } +} + +func dataSourceAciCloudTemplateforExternalNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + aciClient := m.(*client.Client) + name := d.Get("name").(string) + rn := fmt.Sprintf(models.RncloudtemplateExtNetwork, name) + dn := fmt.Sprintf("%s/%s", models.CloudInfraNetworkDefaultTemplateDn, rn) + log.Printf("[DEBUG] %s: Data Source - Beginning Read", dn) + + cloudtemplateExtNetwork, err := getRemoteCloudTemplateforExternalNetwork(aciClient, dn) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(dn) + + _, err = setCloudTemplateforExternalNetworkAttributes(cloudtemplateExtNetwork, d) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] : Data Source - Begining Read of cloud Regions attributes.") + regionsData, err := aciClient.ListCloudProviderandRegionNames(cloudtemplateExtNetwork.DistinguishedName) + if err != nil { + log.Printf("[DEBUG] : Data Source - Error while reading cloud Regions attributes %v", err) + } + + regionsList := make([]string, 0, 1) + for _, regionValue := range regionsData { + + regionsMap, err := setCloudProviderandRegionNamesAttributes(regionValue, make(map[string]string)) + if err != nil { + d.SetId("") + return nil + } + regionsList = append(regionsList, regionsMap["region"]) + d.Set("cloud_vendor", regionsMap["cloud_vendor"]) + if regionsMap["cloud_vendor"] != "aws" { + d.Set("router_type", "") + } + } + log.Printf("[DEBUG] : Data Source - Read cloud regions finished successfully") + d.Set("regions", regionsList) + + log.Printf("[DEBUG] %s: Data Source - Read finished successfully", dn) + return nil +} diff --git a/aci/data_source_aci_cloudtemplateipsectunnelsubnetpool.go b/aci/data_source_aci_cloudtemplateipsectunnelsubnetpool.go new file mode 100644 index 000000000..06c3bc531 --- /dev/null +++ b/aci/data_source_aci_cloudtemplateipsectunnelsubnetpool.go @@ -0,0 +1,50 @@ +package aci + +import ( + "context" + "fmt" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAciSubnetPoolforIpSecTunnels() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceAciSubnetPoolforIpSecTunnelsRead, + SchemaVersion: 1, + Schema: AppendBaseAttrSchema(AppendNameAliasAttrSchema(map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "subnet_pool": { + Type: schema.TypeString, + Required: true, + }, + })), + } +} + +func dataSourceAciSubnetPoolforIpSecTunnelsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + aciClient := m.(*client.Client) + subnetpool := d.Get("subnet_pool").(string) + rn := fmt.Sprintf(models.RncloudtemplateIpSecTunnelSubnetPool, subnetpool) + dn := fmt.Sprintf("%s/%s", models.CloudInfraNetworkDefaultTemplateDn, rn) + + cloudtemplateIpSecTunnelSubnetPool, err := getRemoteSubnetPoolforIpSecTunnels(aciClient, dn) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(dn) + + _, err = setSubnetPoolforIpSecTunnelsAttributes(cloudtemplateIpSecTunnelSubnetPool, d) + if err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/aci/data_source_aci_cloudtemplatevpnnetwork.go b/aci/data_source_aci_cloudtemplatevpnnetwork.go new file mode 100644 index 000000000..1b1207b9f --- /dev/null +++ b/aci/data_source_aci_cloudtemplatevpnnetwork.go @@ -0,0 +1,154 @@ +package aci + +import ( + "context" + "fmt" + "log" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAciCloudTemplateforVPNNetwork() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceAciCloudTemplateforVPNNetworkRead, + SchemaVersion: 1, + Schema: AppendBaseAttrSchema(AppendNameAliasAttrSchema(map[string]*schema.Schema{ + "aci_cloud_external_network_dn": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "remote_site_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "remote_site_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "ipsec_tunnel": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ike_version": { + Type: schema.TypeString, + Required: true, + }, + "public_ip_address": { + Type: schema.TypeString, + Required: true, + }, + "subnet_pool_name": { + Type: schema.TypeString, + Required: true, + }, + "pre_shared_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "bgp_peer_asn": { + Type: schema.TypeString, + Required: true, + }, + "source_interfaces": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Computed: true, + }, + }, + }, + }, + })), + } +} + +func dataSourceAciCloudTemplateforVPNNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + aciClient := m.(*client.Client) + name := d.Get("name").(string) + TemplateforExternalNetworkDn := d.Get("aci_cloud_external_network_dn").(string) + rn := fmt.Sprintf(models.RncloudtemplateVpnNetwork, name) + dn := fmt.Sprintf("%s/%s", TemplateforExternalNetworkDn, rn) + log.Printf("[DEBUG] %s: Data Source - Beginning Read", dn) + + cloudtemplateVpnNetwork, err := getRemoteTemplateforVPNNetwork(aciClient, dn) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(dn) + + _, err = setTemplateforVPNNetworkAttributes(cloudtemplateVpnNetwork, d) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] Data Source - Begining Read of cloud IPsec Tunnel attributes.") + cloudtemplateIpSecTunnelData, err := aciClient.ListCloudTemplateforIpSectunnel(dn) + if err != nil { + log.Printf("[DEBUG] Data Source - Error while reading cloud IPsec Tunnel attributes %v", err) + } + + cloudtemplateIpSecTunnelSet := make([]map[string]interface{}, 0, 1) + for _, cloudtemplateIpSecTunnel := range cloudtemplateIpSecTunnelData { + + cloudIpSecTunnelAttMap, cloudtemplateIpSecTunnelDn, err := setCloudTemplateforIpSecTunnelAttributes(cloudtemplateIpSecTunnel, make(map[string]interface{})) + if err != nil { + d.SetId("") + return nil + } + + log.Printf("[DEBUG] Data Source - Begining Read of cloud BGP IPV4 Peer attributes.") + bgpIPv4PeerData, err := aciClient.ListCloudTemplateBGPIPv4Peer(cloudtemplateIpSecTunnelDn) + if err != nil { + log.Printf("[DEBUG] Data Source - Error while reading cloud BGP IPV4 Peer attributes %v", err) + } + for _, bgpIPv4Peer := range bgpIPv4PeerData { + bgpPeerAsnAtt, err := getASNfromBGPTPV4Peer(bgpIPv4Peer, make(map[string]string)) + if err != nil { + d.SetId("") + return nil + } + cloudIpSecTunnelAttMap["bgp_peer_asn"] = bgpPeerAsnAtt["bgp_peer_asn_att"] + } + log.Printf("[DEBUG] Data Source - Read cloud BGP IPV4 Peer finished successfully.") + + log.Printf("[DEBUG] Data Source - Begining Read of cloud IPsec Tunnel Source Interface attributes.") + ipSectunnelSourceInterfaceData, err := aciClient.ListCloudTemplateforIpSectunnelSourceInterface(cloudtemplateIpSecTunnelDn) + if err != nil { + log.Printf("[DEBUG] Data Source - Error while reading cloud IPsec Tunnel Source Interface attributes %v", err) + } + + ipSectunnelSourceInterfaceList := make([]string, 0, 1) + for _, ipSecTunnelSourceInterfaceValue := range ipSectunnelSourceInterfaceData { + ipSectunnelSourceInterfaceName, err := formatTemplateforIpSectunnelAttributes(ipSecTunnelSourceInterfaceValue) + if err != nil { + d.SetId("") + return nil + } + ipSectunnelSourceInterfaceList = append(ipSectunnelSourceInterfaceList, ipSectunnelSourceInterfaceName) + } + cloudIpSecTunnelAttMap["source_interfaces"] = ipSectunnelSourceInterfaceList + log.Printf("[DEBUG] : Data Source - Read cloud IPsec Tunnel Source Interface finished successfully") + + cloudtemplateIpSecTunnelSet = append(cloudtemplateIpSecTunnelSet, cloudIpSecTunnelAttMap) + } + d.Set("ipsec_tunnel", cloudtemplateIpSecTunnelSet) + log.Printf("[DEBUG] Data Source - Read cloud IPsec Tunnel finished successfully.") + + log.Printf("[DEBUG] %s: Data Source - Read finished successfully", dn) + return nil +} diff --git a/aci/provider.go b/aci/provider.go index 70514a7cb..6856570d0 100644 --- a/aci/provider.go +++ b/aci/provider.go @@ -135,6 +135,9 @@ func Provider() *schema.Provider { "aci_logical_interface_profile": resourceAciLogicalInterfaceProfile(), "aci_l3_ext_subnet": resourceAciL3ExtSubnet(), "aci_cloud_applicationcontainer": resourceAciCloudApplicationcontainer(), + "aci_cloud_ipsec_tunnel_subnet_pool": resourceAciSubnetPoolforIpSecTunnels(), + "aci_cloud_external_network": resourceAciCloudTemplateforExternalNetwork(), + "aci_cloud_external_network_vpn_network": resourceAciCloudTemplateforVPNNetwork(), "aci_cloud_aws_provider": resourceAciCloudAWSProvider(), "aci_cloud_cidr_pool": resourceAciCloudCIDRPool(), "aci_cloud_domain_profile": resourceAciCloudDomainProfile(), @@ -366,6 +369,9 @@ func Provider() *schema.Provider { "aci_logical_interface_profile": dataSourceAciLogicalInterfaceProfile(), "aci_l3_ext_subnet": dataSourceAciL3ExtSubnet(), "aci_cloud_applicationcontainer": dataSourceAciCloudApplicationcontainer(), + "aci_cloud_ipsec_tunnel_subnet_pool": dataSourceAciSubnetPoolforIpSecTunnels(), + "aci_cloud_external_network": dataSourceAciCloudTemplateforExternalNetwork(), + "aci_cloud_external_network_vpn_network": dataSourceAciCloudTemplateforVPNNetwork(), "aci_cloud_aws_provider": dataSourceAciCloudAWSProvider(), "aci_autonomous_system_profile": dataSourceAciAutonomousSystemProfile(), "aci_cloud_cidr_pool": dataSourceAciCloudCIDRPool(), diff --git a/aci/resource_aci_cloudtemplateextnetwork.go b/aci/resource_aci_cloudtemplateextnetwork.go new file mode 100644 index 000000000..dd11b204c --- /dev/null +++ b/aci/resource_aci_cloudtemplateextnetwork.go @@ -0,0 +1,442 @@ +package aci + +import ( + "context" + "fmt" + "log" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceAciCloudTemplateforExternalNetwork() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAciCloudTemplateforExternalNetworkCreate, + UpdateContext: resourceAciCloudTemplateforExternalNetworkUpdate, + ReadContext: resourceAciCloudTemplateforExternalNetworkRead, + DeleteContext: resourceAciCloudTemplateforExternalNetworkDelete, + + Importer: &schema.ResourceImporter{ + State: resourceAciCloudTemplateforExternalNetworkImport, + }, + + SchemaVersion: 1, + + CustomizeDiff: func(_ context.Context, diff *schema.ResourceDiff, v interface{}) error { + if diff.Get("cloud_vendor") != "gcp" { + if diff.Get("all_regions") != "yes" { + return fmt.Errorf("all_regions should always be set to yes when using %v Cloud APIC", diff.Get("cloud_vendor")) + } + } else { + if diff.Get("all_regions") != "no" { + return fmt.Errorf("all_regions should always be set to no when using GCP Cloud APIC") + } + } + return nil + }, + + Schema: AppendBaseAttrSchema(AppendNameAliasAttrSchema(map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vrf_dn": { + Type: schema.TypeString, + Required: true, + }, + "all_regions": { + Type: schema.TypeString, + Optional: true, + Default: "no", + ValidateFunc: validation.StringInSlice([]string{ + "yes", + "no", + }, false), + }, + "host_router_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "vpn_router_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "hub_network_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "cloud_vendor": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "Name of the vendor", + ValidateFunc: validation.StringInSlice([]string{ + "aws", + "azure", + "gcp", + }, false), + }, + "regions": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Computed: true, + }, + "router_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + Description: "Parameter used only for AWS cAPIC", + ValidateFunc: validation.StringInSlice([]string{ + "c8kv", + "tgw", + }, false), + }, + })), + } +} + +func getRemoteCloudTemplateforExternalNetwork(client *client.Client, dn string) (*models.CloudTemplateforExternalNetwork, error) { + cloudtemplateExtNetworkCont, err := client.Get(dn) + if err != nil { + return nil, err + } + cloudtemplateExtNetwork := models.CloudTemplateforExternalNetworkFromContainer(cloudtemplateExtNetworkCont) + if cloudtemplateExtNetwork.DistinguishedName == "" { + return nil, fmt.Errorf("Cloud Template for External Network %s not found", dn) + } + return cloudtemplateExtNetwork, nil +} + +func setCloudTemplateforExternalNetworkAttributes(cloudtemplateExtNetwork *models.CloudTemplateforExternalNetwork, d *schema.ResourceData) (*schema.ResourceData, error) { + d.SetId(cloudtemplateExtNetwork.DistinguishedName) + + cloudtemplateExtNetworkMap, err := cloudtemplateExtNetwork.ToMap() + if err != nil { + return d, err + } + d.Set("annotation", cloudtemplateExtNetworkMap["annotation"]) + d.Set("hub_network_name", cloudtemplateExtNetworkMap["hubNetworkName"]) + d.Set("name", cloudtemplateExtNetworkMap["name"]) + d.Set("name_alias", cloudtemplateExtNetworkMap["nameAlias"]) + if cloudtemplateExtNetworkMap["vrfName"] != "" { + d.Set("vrf_dn", fmt.Sprintf("uni/tn-infra/ctx-%s", cloudtemplateExtNetworkMap["vrfName"])) + } else { + d.Set("vrf_dn", "") + } + d.Set("all_regions", cloudtemplateExtNetworkMap["allRegion"]) + d.Set("host_router_name", cloudtemplateExtNetworkMap["hostRouterName"]) + d.Set("vpn_router_name", cloudtemplateExtNetworkMap["vpnRouterName"]) + + if cloudtemplateExtNetworkMap["hubNetworkName"] != "default" && cloudtemplateExtNetworkMap["hubNetworkName"] != "" { + d.Set("router_type", "tgw") + } else { + d.Set("router_type", "c8kv") + } + return d, nil +} + +func setCloudProviderandRegionNamesAttributes(cloudRegionName *models.CloudProviderandRegionNames, d map[string]string) (map[string]string, error) { + cloudRegionNameMap, err := cloudRegionName.ToMap() + if err != nil { + return d, err + } + + d = map[string]string{ + "cloud_vendor": cloudRegionNameMap["provider"], + "region": cloudRegionNameMap["region"], + } + + return d, nil +} + +func resourceAciCloudTemplateforExternalNetworkImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] %s: Beginning Import", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + cloudtemplateExtNetwork, err := getRemoteCloudTemplateforExternalNetwork(aciClient, dn) + if err != nil { + return nil, err + } + schemaFilled, err := setCloudTemplateforExternalNetworkAttributes(cloudtemplateExtNetwork, d) + if err != nil { + return nil, err + } + + log.Printf("[DEBUG] Begining Import of cloud Regions attributes.") + regionsData, err := aciClient.ListCloudProviderandRegionNames(cloudtemplateExtNetwork.DistinguishedName) + if err != nil { + log.Printf("[DEBUG] Error while importing cloud Regions attributes %v", err) + } + + regionsList := make([]string, 0, 1) + for _, regionValue := range regionsData { + regionsMap, err := setCloudProviderandRegionNamesAttributes(regionValue, make(map[string]string)) + if err != nil { + d.SetId("") + return nil, err + } + regionsList = append(regionsList, regionsMap["region"]) + d.Set("cloud_vendor", regionsMap["cloud_vendor"]) + if regionsMap["cloud_vendor"] != "aws" { + d.Set("router_type", "") + } + } + log.Printf("[DEBUG] : Import cloud regions finished successfully") + d.Set("regions", regionsList) + + log.Printf("[DEBUG] %s: Import finished successfully", d.Id()) + return []*schema.ResourceData{schemaFilled}, nil +} + +func resourceAciCloudTemplateforExternalNetworkCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] CloudTemplateforExternalNetwork: Beginning Creation") + aciClient := m.(*client.Client) + name := d.Get("name").(string) + cloudVendor := d.Get("cloud_vendor").(string) + + cloudtemplateExtNetworkAttr := models.CloudTemplateforExternalNetworkAttributes{} + + nameAlias := "" + if NameAlias, ok := d.GetOk("name_alias"); ok { + nameAlias = NameAlias.(string) + } + + if Annotation, ok := d.GetOk("annotation"); ok { + cloudtemplateExtNetworkAttr.Annotation = Annotation.(string) + } else { + cloudtemplateExtNetworkAttr.Annotation = "{}" + } + + if Name, ok := d.GetOk("name"); ok { + cloudtemplateExtNetworkAttr.Name = Name.(string) + } + + if AllRegions, ok := d.GetOk("all_regions"); ok { + cloudtemplateExtNetworkAttr.AllRegion = AllRegions.(string) + if AllRegions.(string) == "yes" { + if cloudVendor == "aws" { + if RouterType, ok := d.GetOk("router_type"); ok { + if RouterType == "c8kv" { + cloudtemplateExtNetworkAttr.HostRouterName = "default" + } else { + // object class cloudGatewayRouterP + if HubNetworkName, ok := d.GetOk("hub_network_name"); ok { + cloudtemplateExtNetworkAttr.HubNetworkName = HubNetworkName.(string) + } + } + } + } else { + // Always true for Azure cloud + cloudtemplateExtNetworkAttr.HostRouterName = "default" + } + } else { + // following 2 attributes are used only in GCP + cloudtemplateExtNetworkAttr.HubNetworkName = "default" + cloudtemplateExtNetworkAttr.VpnRouterName = "default" + } + } + + if VrfDn, ok := d.GetOk("vrf_dn"); ok { + cloudtemplateExtNetworkAttr.VrfName = GetMOName(VrfDn.(string)) + } + + cloudtemplateExtNetwork := models.NewCloudTemplateforExternalNetwork(fmt.Sprintf(models.RncloudtemplateExtNetwork, name), models.CloudInfraNetworkDefaultTemplateDn, nameAlias, cloudtemplateExtNetworkAttr) + + err := aciClient.Save(cloudtemplateExtNetwork) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] cloudRegionName: Beginning Creation") + if Regions, ok := d.GetOk("regions"); ok { + for _, value := range Regions.([]interface{}) { + cloudRegionNameAttr := models.CloudProviderandRegionNamesAttributes{} + cloudRegionNameAttr.Region = value.(string) + cloudRegionNameAttr.Provider = cloudVendor + + cloudRegionName := models.NewCloudProviderandRegionNames(fmt.Sprintf(models.RncloudRegionName, cloudRegionNameAttr.Provider, cloudRegionNameAttr.Region), cloudtemplateExtNetwork.DistinguishedName, cloudRegionNameAttr) + err := aciClient.Save(cloudRegionName) + if err != nil { + return diag.FromErr(err) + } + } + log.Printf("[DEBUG] : Creation finished successfully") + } + + d.SetId(cloudtemplateExtNetwork.DistinguishedName) + log.Printf("[DEBUG] %s: Creation finished successfully", d.Id()) + return resourceAciCloudTemplateforExternalNetworkRead(ctx, d, m) +} + +func resourceAciCloudTemplateforExternalNetworkUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] CloudTemplateforExternalNetwork: Beginning Update") + aciClient := m.(*client.Client) + name := d.Get("name").(string) + cloudVendor := d.Get("cloud_vendor").(string) + + cloudtemplateExtNetworkAttr := models.CloudTemplateforExternalNetworkAttributes{} + + nameAlias := "" + if NameAlias, ok := d.GetOk("name_alias"); ok { + nameAlias = NameAlias.(string) + } + + if Annotation, ok := d.GetOk("annotation"); ok { + cloudtemplateExtNetworkAttr.Annotation = Annotation.(string) + } else { + cloudtemplateExtNetworkAttr.Annotation = "{}" + } + + if Name, ok := d.GetOk("name"); ok { + cloudtemplateExtNetworkAttr.Name = Name.(string) + } + + if AllRegions, ok := d.GetOk("all_regions"); ok { + cloudtemplateExtNetworkAttr.AllRegion = AllRegions.(string) + if AllRegions.(string) == "yes" { + if cloudVendor == "aws" { + if RouterType, ok := d.GetOk("router_type"); ok { + if RouterType == "c8kv" { + cloudtemplateExtNetworkAttr.HostRouterName = "default" + } else { + // object class cloudGatewayRouterP + if HubNetworkName, ok := d.GetOk("hub_network_name"); ok { + cloudtemplateExtNetworkAttr.HubNetworkName = HubNetworkName.(string) + } + } + } + } else { + // Always true for Azure cloud + cloudtemplateExtNetworkAttr.HostRouterName = "default" + } + } else { + // following 2 attributes are used only in GCP + cloudtemplateExtNetworkAttr.HubNetworkName = "default" + cloudtemplateExtNetworkAttr.VpnRouterName = "default" + } + } + + if VrfDn, ok := d.GetOk("vrf_dn"); ok { + cloudtemplateExtNetworkAttr.VrfName = GetMOName(VrfDn.(string)) + } + + cloudtemplateExtNetwork := models.NewCloudTemplateforExternalNetwork(fmt.Sprintf(models.RncloudtemplateExtNetwork, name), models.CloudInfraNetworkDefaultTemplateDn, nameAlias, cloudtemplateExtNetworkAttr) + + cloudtemplateExtNetwork.Status = "modified" + + err := aciClient.Save(cloudtemplateExtNetwork) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] cloudRegionName: Beginning Update") + + if d.HasChange("regions") { + oldList, newList := d.GetChange("regions") + + // when getStringsFromListNotInOtherList(oldList, newList) it gives a list of strings which has to be removed + deleteRegionsList := getStringsFromListNotInOtherList(oldList, newList) + if len(deleteRegionsList) != 0 { + for _, value := range deleteRegionsList { + cloudRegionsDn := cloudtemplateExtNetwork.DistinguishedName + "/" + fmt.Sprintf(models.RncloudRegionName, cloudVendor, value) + err = aciClient.DeleteCloudProviderandRegionNames(cloudRegionsDn) + if err != nil { + return diag.FromErr(err) + } + } + } + + // when getStringsFromListNotInOtherList(newList, oldList) it gives a list of strings which has to be added + createRegionsList := getStringsFromListNotInOtherList(newList, oldList) + if len(createRegionsList) != 0 { + for _, value := range createRegionsList { + cloudRegionNameAttr := models.CloudProviderandRegionNamesAttributes{} + cloudRegionNameAttr.Region = value.(string) + cloudRegionNameAttr.Provider = "gcp" + + cloudRegionName := models.NewCloudProviderandRegionNames(fmt.Sprintf(models.RncloudRegionName, cloudRegionNameAttr.Provider, cloudRegionNameAttr.Region), cloudtemplateExtNetwork.DistinguishedName, cloudRegionNameAttr) + err := aciClient.Save(cloudRegionName) + if err != nil { + return diag.FromErr(err) + } + } + } + } + log.Printf("[DEBUG] : Update Cloud Regions finished successfully") + + d.SetId(cloudtemplateExtNetwork.DistinguishedName) + log.Printf("[DEBUG] %s: Update finished successfully", d.Id()) + return resourceAciCloudTemplateforExternalNetworkRead(ctx, d, m) +} + +func resourceAciCloudTemplateforExternalNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] %s: Beginning Read", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + + cloudtemplateExtNetwork, err := getRemoteCloudTemplateforExternalNetwork(aciClient, dn) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + + _, err = setCloudTemplateforExternalNetworkAttributes(cloudtemplateExtNetwork, d) + if err != nil { + d.SetId("") + return nil + } + + log.Printf("[DEBUG] Begining Read of cloud Regions attributes.") + + regionsData, err := aciClient.ListCloudProviderandRegionNames(cloudtemplateExtNetwork.DistinguishedName) + if err != nil { + log.Printf("[DEBUG] Error while reading cloud Regions attributes %v", err) + } + + regionsList := make([]string, 0, 1) + for _, regionValue := range regionsData { + + regionsMap, err := setCloudProviderandRegionNamesAttributes(regionValue, make(map[string]string)) + if err != nil { + d.SetId("") + return nil + } + regionsList = append(regionsList, regionsMap["region"]) + d.Set("cloud_vendor", regionsMap["cloud_vendor"]) + if regionsMap["cloud_vendor"] != "aws" { + d.Set("router_type", "") + } + } + log.Printf("[DEBUG] : Read cloud regions finished successfully") + d.Set("regions", regionsList) + + log.Printf("[DEBUG] %s: Read finished successfully", d.Id()) + return nil +} + +func resourceAciCloudTemplateforExternalNetworkDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] %s: Beginning Destroy", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + + err := aciClient.DeleteByDn(dn, "cloudtemplateExtNetwork") + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] %s: Destroy finished successfully", d.Id()) + d.SetId("") + return diag.FromErr(err) +} diff --git a/aci/resource_aci_cloudtemplateextnetwork_test.go b/aci/resource_aci_cloudtemplateextnetwork_test.go new file mode 100644 index 000000000..7b3d4b35a --- /dev/null +++ b/aci/resource_aci_cloudtemplateextnetwork_test.go @@ -0,0 +1,149 @@ +package aci + +import ( + "fmt" + "testing" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAciCloudTemplateforExternalNetwork_Basic(t *testing.T) { + var templatefor_external_network models.CloudTemplateforExternalNetwork + fv_tenant_name := acctest.RandString(5) + aci_cloud_external_network_vpn_network := acctest.RandString(5) + cloud_external_network := acctest.RandString(5) + description := "templatefor_external_network created while acceptance testing" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAciCloudTemplateforExternalNetworkDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckAciCloudTemplateforExternalNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciCloudTemplateforExternalNetworkExists("aci_cloud_external_network.footemplatefor_external_network", &templatefor_external_network), + testAccCheckAciCloudTemplateforExternalNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, description, &templatefor_external_network), + ), + }, + }, + }) +} + +func TestAccAciCloudTemplateforExternalNetwork_Update(t *testing.T) { + var templatefor_external_network models.CloudTemplateforExternalNetwork + fv_tenant_name := acctest.RandString(5) + aci_cloud_external_network_vpn_network := acctest.RandString(5) + cloud_external_network := acctest.RandString(5) + description := "templatefor_external_network created while acceptance testing" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAciCloudTemplateforExternalNetworkDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckAciCloudTemplateforExternalNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciCloudTemplateforExternalNetworkExists("aci_cloud_external_network.footemplatefor_external_network", &templatefor_external_network), + testAccCheckAciCloudTemplateforExternalNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, description, &templatefor_external_network), + ), + }, + { + Config: testAccCheckAciCloudTemplateforExternalNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciCloudTemplateforExternalNetworkExists("aci_cloud_external_network.footemplatefor_external_network", &templatefor_external_network), + testAccCheckAciCloudTemplateforExternalNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, description, &templatefor_external_network), + ), + }, + }, + }) +} + +func testAccCheckAciCloudTemplateforExternalNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network string) string { + return fmt.Sprintf(` + + resource "aci_tenant" "footenant" { + name = "%s" + description = "tenant created while acceptance testing" + + } + + resource "aci_infra_network_template" "fooinfra_network_template" { + name = "%s" + description = "infra_network_template created while acceptance testing" + tenant_dn = aci_tenant.footenant.id + } + + resource "aci_cloud_external_network" "footemplatefor_external_network" { + name = "%s" + description = "templatefor_external_network created while acceptance testing" + infra_network_template_dn = aci_infra_network_template.fooinfra_network_template.id + } + + `, fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network) +} + +func testAccCheckAciCloudTemplateforExternalNetworkExists(name string, templatefor_external_network *models.CloudTemplateforExternalNetwork) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + + if !ok { + return fmt.Errorf("Template for External Network %s not found", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Template for External Network dn was set") + } + + client := testAccProvider.Meta().(*client.Client) + + cont, err := client.Get(rs.Primary.ID) + if err != nil { + return err + } + + templatefor_external_networkFound := models.CloudTemplateforExternalNetworkFromContainer(cont) + if templatefor_external_networkFound.DistinguishedName != rs.Primary.ID { + return fmt.Errorf("Template for External Network %s not found", rs.Primary.ID) + } + *templatefor_external_network = *templatefor_external_networkFound + return nil + } +} + +func testAccCheckAciCloudTemplateforExternalNetworkDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*client.Client) + for _, rs := range s.RootModule().Resources { + if rs.Type == "aci_cloud_external_network" { + cont, err := client.Get(rs.Primary.ID) + templatefor_external_network := models.CloudTemplateforExternalNetworkFromContainer(cont) + if err == nil { + return fmt.Errorf("Template for External Network %s Still exists", templatefor_external_network.DistinguishedName) + } + } else { + continue + } + } + return nil +} + +func testAccCheckAciCloudTemplateforExternalNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, description string, templatefor_external_network *models.CloudTemplateforExternalNetwork) resource.TestCheckFunc { + return func(s *terraform.State) error { + if cloud_external_network != GetMOName(templatefor_external_network.DistinguishedName) { + return fmt.Errorf("Bad cloudtemplate_ext_network %s", GetMOName(templatefor_external_network.DistinguishedName)) + } + + if aci_cloud_external_network_vpn_network != GetMOName(GetParentDn(templatefor_external_network.DistinguishedName)) { + return fmt.Errorf(" Bad cloudtemplate_infra_network %s", GetMOName(GetParentDn(templatefor_external_network.DistinguishedName))) + } + if description != templatefor_external_network.Description { + return fmt.Errorf("Bad templatefor_external_network Description %s", templatefor_external_network.Description) + } + return nil + } +} diff --git a/aci/resource_aci_cloudtemplateipsectunnelsubnetpool.go b/aci/resource_aci_cloudtemplateipsectunnelsubnetpool.go new file mode 100644 index 000000000..e7cf0fb95 --- /dev/null +++ b/aci/resource_aci_cloudtemplateipsectunnelsubnetpool.go @@ -0,0 +1,186 @@ +package aci + +import ( + "context" + "fmt" + "log" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAciSubnetPoolforIpSecTunnels() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAciSubnetPoolforIpSecTunnelsCreate, + UpdateContext: resourceAciSubnetPoolforIpSecTunnelsUpdate, + ReadContext: resourceAciSubnetPoolforIpSecTunnelsRead, + DeleteContext: resourceAciSubnetPoolforIpSecTunnelsDelete, + + Importer: &schema.ResourceImporter{ + State: resourceAciSubnetPoolforIpSecTunnelsImport, + }, + + SchemaVersion: 1, + Schema: AppendBaseAttrSchema(map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "subnet_pool": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }), + } +} + +func getRemoteSubnetPoolforIpSecTunnels(client *client.Client, dn string) (*models.SubnetPoolforIpSecTunnels, error) { + cloudtemplateIpSecTunnelSubnetPoolCont, err := client.Get(dn) + if err != nil { + return nil, err + } + cloudtemplateIpSecTunnelSubnetPool := models.SubnetPoolforIpSecTunnelsFromContainer(cloudtemplateIpSecTunnelSubnetPoolCont) + if cloudtemplateIpSecTunnelSubnetPool.DistinguishedName == "" { + return nil, fmt.Errorf("Subnet Pool for IPsec Tunnels %s not found", dn) + } + return cloudtemplateIpSecTunnelSubnetPool, nil +} + +func setSubnetPoolforIpSecTunnelsAttributes(cloudtemplateIpSecTunnelSubnetPool *models.SubnetPoolforIpSecTunnels, d *schema.ResourceData) (*schema.ResourceData, error) { + dn := d.Id() + d.SetId(cloudtemplateIpSecTunnelSubnetPool.DistinguishedName) + cloudtemplateIpSecTunnelSubnetPoolMap, err := cloudtemplateIpSecTunnelSubnetPool.ToMap() + if err != nil { + return d, err + } + + if dn != cloudtemplateIpSecTunnelSubnetPool.DistinguishedName { + d.Set("infra_network_template_dn", "") + } else { + d.Set("infra_network_template_dn", GetParentDn(dn, "/"+fmt.Sprintf(models.RncloudtemplateIpSecTunnelSubnetPool, cloudtemplateIpSecTunnelSubnetPoolMap["subnetpool"]))) + } + d.Set("annotation", cloudtemplateIpSecTunnelSubnetPoolMap["annotation"]) + d.Set("name", cloudtemplateIpSecTunnelSubnetPoolMap["poolname"]) + d.Set("subnet_pool", cloudtemplateIpSecTunnelSubnetPoolMap["subnetpool"]) + return d, nil +} + +func resourceAciSubnetPoolforIpSecTunnelsImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] %s: Beginning Import", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + cloudtemplateIpSecTunnelSubnetPool, err := getRemoteSubnetPoolforIpSecTunnels(aciClient, dn) + if err != nil { + return nil, err + } + schemaFilled, err := setSubnetPoolforIpSecTunnelsAttributes(cloudtemplateIpSecTunnelSubnetPool, d) + if err != nil { + return nil, err + } + log.Printf("[DEBUG] %s: Import finished successfully", d.Id()) + return []*schema.ResourceData{schemaFilled}, nil +} + +func resourceAciSubnetPoolforIpSecTunnelsCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] SubnetPoolforIpSecTunnels: Beginning Creation") + aciClient := m.(*client.Client) + + cloudtemplateIpSecTunnelSubnetPoolAttr := models.SubnetPoolforIpSecTunnelsAttributes{} + + if Annotation, ok := d.GetOk("annotation"); ok { + cloudtemplateIpSecTunnelSubnetPoolAttr.Annotation = Annotation.(string) + } else { + cloudtemplateIpSecTunnelSubnetPoolAttr.Annotation = "{}" + } + + if Poolname, ok := d.GetOk("name"); ok { + cloudtemplateIpSecTunnelSubnetPoolAttr.Poolname = Poolname.(string) + } + + if Subnetpool, ok := d.GetOk("subnet_pool"); ok { + cloudtemplateIpSecTunnelSubnetPoolAttr.Subnetpool = Subnetpool.(string) + } + cloudtemplateIpSecTunnelSubnetPool := models.NewSubnetPoolforIpSecTunnels(fmt.Sprintf(models.RncloudtemplateIpSecTunnelSubnetPool, cloudtemplateIpSecTunnelSubnetPoolAttr.Subnetpool), models.CloudInfraNetworkDefaultTemplateDn, cloudtemplateIpSecTunnelSubnetPoolAttr) + + err := aciClient.Save(cloudtemplateIpSecTunnelSubnetPool) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(cloudtemplateIpSecTunnelSubnetPool.DistinguishedName) + log.Printf("[DEBUG] %s: Creation finished successfully", d.Id()) + return resourceAciSubnetPoolforIpSecTunnelsRead(ctx, d, m) +} + +func resourceAciSubnetPoolforIpSecTunnelsUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] SubnetPoolforIpSecTunnels: Beginning Update") + aciClient := m.(*client.Client) + + cloudtemplateIpSecTunnelSubnetPoolAttr := models.SubnetPoolforIpSecTunnelsAttributes{} + + if Annotation, ok := d.GetOk("annotation"); ok { + cloudtemplateIpSecTunnelSubnetPoolAttr.Annotation = Annotation.(string) + } else { + cloudtemplateIpSecTunnelSubnetPoolAttr.Annotation = "{}" + } + + if Poolname, ok := d.GetOk("name"); ok { + cloudtemplateIpSecTunnelSubnetPoolAttr.Poolname = Poolname.(string) + } + + if Subnetpool, ok := d.GetOk("subnet_pool"); ok { + cloudtemplateIpSecTunnelSubnetPoolAttr.Subnetpool = Subnetpool.(string) + } + cloudtemplateIpSecTunnelSubnetPool := models.NewSubnetPoolforIpSecTunnels(fmt.Sprintf("ipsecsubnetpool-[%s]", cloudtemplateIpSecTunnelSubnetPoolAttr.Subnetpool), models.CloudInfraNetworkDefaultTemplateDn, cloudtemplateIpSecTunnelSubnetPoolAttr) + + cloudtemplateIpSecTunnelSubnetPool.Status = "modified" + + err := aciClient.Save(cloudtemplateIpSecTunnelSubnetPool) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(cloudtemplateIpSecTunnelSubnetPool.DistinguishedName) + log.Printf("[DEBUG] %s: Update finished successfully", d.Id()) + return resourceAciSubnetPoolforIpSecTunnelsRead(ctx, d, m) +} + +func resourceAciSubnetPoolforIpSecTunnelsRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] %s: Beginning Read", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + + cloudtemplateIpSecTunnelSubnetPool, err := getRemoteSubnetPoolforIpSecTunnels(aciClient, dn) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + + _, err = setSubnetPoolforIpSecTunnelsAttributes(cloudtemplateIpSecTunnelSubnetPool, d) + if err != nil { + d.SetId("") + return nil + } + + log.Printf("[DEBUG] %s: Read finished successfully", d.Id()) + return nil +} + +func resourceAciSubnetPoolforIpSecTunnelsDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] %s: Beginning Destroy", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + + err := aciClient.DeleteByDn(dn, "cloudtemplateIpSecTunnelSubnetPool") + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] %s: Destroy finished successfully", d.Id()) + d.SetId("") + return diag.FromErr(err) +} diff --git a/aci/resource_aci_cloudtemplateipsectunnelsubnetpool_test.go b/aci/resource_aci_cloudtemplateipsectunnelsubnetpool_test.go new file mode 100644 index 000000000..1c95e49c5 --- /dev/null +++ b/aci/resource_aci_cloudtemplateipsectunnelsubnetpool_test.go @@ -0,0 +1,149 @@ +package aci + +import ( + "fmt" + "testing" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAciSubnetPoolforIpSecTunnels_Basic(t *testing.T) { + var subnet_poolfor_ip_sec_tunnels models.SubnetPoolforIpSecTunnels + fv_tenant_name := acctest.RandString(5) + aci_cloud_external_network_vpn_network := acctest.RandString(5) + aci_cloud_ipsec_tunnel_subnet_pool := acctest.RandString(5) + description := "subnet_poolfor_ip_sec_tunnels created while acceptance testing" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAciSubnetPoolforIpSecTunnelsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckAciSubnetPoolforIpSecTunnelsConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciSubnetPoolforIpSecTunnelsExists("aci_subnet_poolfor_ip_sec_tunnels.foosubnet_poolfor_ip_sec_tunnels", &subnet_poolfor_ip_sec_tunnels), + testAccCheckAciSubnetPoolforIpSecTunnelsAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool, description, &subnet_poolfor_ip_sec_tunnels), + ), + }, + }, + }) +} + +func TestAccAciSubnetPoolforIpSecTunnels_Update(t *testing.T) { + var subnet_poolfor_ip_sec_tunnels models.SubnetPoolforIpSecTunnels + fv_tenant_name := acctest.RandString(5) + aci_cloud_external_network_vpn_network := acctest.RandString(5) + aci_cloud_ipsec_tunnel_subnet_pool := acctest.RandString(5) + description := "subnet_poolfor_ip_sec_tunnels created while acceptance testing" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAciSubnetPoolforIpSecTunnelsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckAciSubnetPoolforIpSecTunnelsConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciSubnetPoolforIpSecTunnelsExists("aci_subnet_poolfor_ip_sec_tunnels.foosubnet_poolfor_ip_sec_tunnels", &subnet_poolfor_ip_sec_tunnels), + testAccCheckAciSubnetPoolforIpSecTunnelsAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool, description, &subnet_poolfor_ip_sec_tunnels), + ), + }, + { + Config: testAccCheckAciSubnetPoolforIpSecTunnelsConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciSubnetPoolforIpSecTunnelsExists("aci_subnet_poolfor_ip_sec_tunnels.foosubnet_poolfor_ip_sec_tunnels", &subnet_poolfor_ip_sec_tunnels), + testAccCheckAciSubnetPoolforIpSecTunnelsAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool, description, &subnet_poolfor_ip_sec_tunnels), + ), + }, + }, + }) +} + +func testAccCheckAciSubnetPoolforIpSecTunnelsConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool string) string { + return fmt.Sprintf(` + + resource "aci_tenant" "footenant" { + name = "%s" + description = "tenant created while acceptance testing" + + } + + resource "aci_infra_network_template" "fooinfra_network_template" { + name = "%s" + description = "infra_network_template created while acceptance testing" + tenant_dn = aci_tenant.footenant.id + } + + resource "aci_subnet_poolfor_ip_sec_tunnels" "foosubnet_poolfor_ip_sec_tunnels" { + name = "%s" + description = "subnet_poolfor_ip_sec_tunnels created while acceptance testing" + infra_network_template_dn = aci_infra_network_template.fooinfra_network_template.id + } + + `, fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool) +} + +func testAccCheckAciSubnetPoolforIpSecTunnelsExists(name string, subnet_poolfor_ip_sec_tunnels *models.SubnetPoolforIpSecTunnels) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + + if !ok { + return fmt.Errorf("Subnet Pool for IPsec Tunnels %s not found", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Subnet Pool for IPsec Tunnels dn was set") + } + + client := testAccProvider.Meta().(*client.Client) + + cont, err := client.Get(rs.Primary.ID) + if err != nil { + return err + } + + subnet_poolfor_ip_sec_tunnelsFound := models.SubnetPoolforIpSecTunnelsFromContainer(cont) + if subnet_poolfor_ip_sec_tunnelsFound.DistinguishedName != rs.Primary.ID { + return fmt.Errorf("Subnet Pool for IPsec Tunnels %s not found", rs.Primary.ID) + } + *subnet_poolfor_ip_sec_tunnels = *subnet_poolfor_ip_sec_tunnelsFound + return nil + } +} + +func testAccCheckAciSubnetPoolforIpSecTunnelsDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*client.Client) + for _, rs := range s.RootModule().Resources { + if rs.Type == "aci_subnet_poolfor_ip_sec_tunnels" { + cont, err := client.Get(rs.Primary.ID) + subnet_poolfor_ip_sec_tunnels := models.SubnetPoolforIpSecTunnelsFromContainer(cont) + if err == nil { + return fmt.Errorf("Subnet Pool for IPsec Tunnels %s Still exists", subnet_poolfor_ip_sec_tunnels.DistinguishedName) + } + } else { + continue + } + } + return nil +} + +func testAccCheckAciSubnetPoolforIpSecTunnelsAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, aci_cloud_ipsec_tunnel_subnet_pool, description string, subnet_poolfor_ip_sec_tunnels *models.SubnetPoolforIpSecTunnels) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aci_cloud_ipsec_tunnel_subnet_pool != GetMOName(subnet_poolfor_ip_sec_tunnels.DistinguishedName) { + return fmt.Errorf("Bad cloudtemplate_ip_sec_tunnel_subnet_pool %s", GetMOName(subnet_poolfor_ip_sec_tunnels.DistinguishedName)) + } + + if aci_cloud_external_network_vpn_network != GetMOName(GetParentDn(subnet_poolfor_ip_sec_tunnels.DistinguishedName, models.RncloudtemplateIpSecTunnelSubnetPool)) { + return fmt.Errorf(" Bad cloudtemplate_infra_network %s", GetMOName(GetParentDn(subnet_poolfor_ip_sec_tunnels.DistinguishedName, models.RncloudtemplateIpSecTunnelSubnetPool))) + } + if description != subnet_poolfor_ip_sec_tunnels.Description { + return fmt.Errorf("Bad subnet_poolfor_ip_sec_tunnels Description %s", subnet_poolfor_ip_sec_tunnels.Description) + } + return nil + } +} diff --git a/aci/resource_aci_cloudtemplatevpnnetwork.go b/aci/resource_aci_cloudtemplatevpnnetwork.go new file mode 100644 index 000000000..132e81c71 --- /dev/null +++ b/aci/resource_aci_cloudtemplatevpnnetwork.go @@ -0,0 +1,498 @@ +package aci + +import ( + "context" + "fmt" + "log" + "regexp" + "strings" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceAciCloudTemplateforVPNNetwork() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAciCloudTemplateforVPNNetworkCreate, + UpdateContext: resourceAciCloudTemplateforVPNNetworkUpdate, + ReadContext: resourceAciCloudTemplateforVPNNetworkRead, + DeleteContext: resourceAciCloudTemplateforVPNNetworkDelete, + + Importer: &schema.ResourceImporter{ + State: resourceAciCloudTemplateforVPNNetworkImport, + }, + + SchemaVersion: 1, + Schema: AppendBaseAttrSchema(AppendNameAliasAttrSchema(map[string]*schema.Schema{ + "aci_cloud_external_network_dn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "remote_site_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "remote_site_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "ipsec_tunnel": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ike_version": { + Type: schema.TypeString, + Optional: true, + Default: "ikev2", + ValidateFunc: validation.StringInSlice([]string{ + "ikev1", + "ikev2", + }, false), + }, + "public_ip_address": { + Type: schema.TypeString, + Required: true, + }, + "subnet_pool_name": { + Type: schema.TypeString, + Required: true, + }, + "pre_shared_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "bgp_peer_asn": { + Type: schema.TypeString, + Required: true, + }, + "source_interfaces": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Computed: true, + }, + }, + }, + }, + })), + } +} + +func getRemoteTemplateforVPNNetwork(client *client.Client, dn string) (*models.CloudTemplateforVPNNetwork, error) { + cloudtemplateVpnNetworkCont, err := client.Get(dn) + if err != nil { + return nil, err + } + cloudtemplateVpnNetwork := models.CloudTemplateforVPNNetworkFromContainer(cloudtemplateVpnNetworkCont) + if cloudtemplateVpnNetwork.DistinguishedName == "" { + return nil, fmt.Errorf("Template for VPN Network %s not found", dn) + } + return cloudtemplateVpnNetwork, nil +} + +func setTemplateforVPNNetworkAttributes(cloudtemplateVpnNetwork *models.CloudTemplateforVPNNetwork, d *schema.ResourceData) (*schema.ResourceData, error) { + dn := d.Id() + d.SetId(cloudtemplateVpnNetwork.DistinguishedName) + + cloudtemplateVpnNetworkMap, err := cloudtemplateVpnNetwork.ToMap() + if err != nil { + return d, err + } + + if dn != cloudtemplateVpnNetwork.DistinguishedName { + d.Set("aci_cloud_external_network_dn", "") + } else { + d.Set("aci_cloud_external_network_dn", GetParentDn(dn, "/"+fmt.Sprintf(models.RncloudtemplateVpnNetwork, cloudtemplateVpnNetworkMap["name"]))) + } + d.Set("annotation", cloudtemplateVpnNetworkMap["annotation"]) + d.Set("name", cloudtemplateVpnNetworkMap["name"]) + d.Set("remote_site_id", cloudtemplateVpnNetworkMap["remoteSiteId"]) + d.Set("remote_site_name", cloudtemplateVpnNetworkMap["remoteSiteName"]) + d.Set("name_alias", cloudtemplateVpnNetworkMap["nameAlias"]) + + return d, nil +} + +func setCloudTemplateforIpSecTunnelAttributes(cloudtemplateIpSecTunnel *models.CloudTemplateforIpSectunnel, d map[string]interface{}) (map[string]interface{}, string, error) { + cloudtemplateIpSecTunnelMap, err := cloudtemplateIpSecTunnel.ToMap() + if err != nil { + return nil, "", err + } + + d["ike_version"] = cloudtemplateIpSecTunnelMap["ikeVersion"] + d["public_ip_address"] = cloudtemplateIpSecTunnelMap["peeraddr"] + d["subnet_pool_name"] = cloudtemplateIpSecTunnelMap["poolname"] + d["pre_shared_key"] = cloudtemplateIpSecTunnelMap["preSharedKey"] + + return d, cloudtemplateIpSecTunnel.DistinguishedName, nil +} + +func getASNfromBGPTPV4Peer(cloudtemplateBgpIpv4 *models.CloudTemplateBGPIPv4Peer, d map[string]string) (map[string]string, error) { + + cloudtemplateBgpIpv4Map, err := cloudtemplateBgpIpv4.ToMap() + if err != nil { + return d, err + } + + d = map[string]string{ + "bgp_peer_asn_att": cloudtemplateBgpIpv4Map["peerasn"], + } + return d, nil +} + +func formatTemplateforIpSectunnelAttributes(cloudtemplateIpSecTunnelSourceInterface *models.CloudTemplateforIpSectunnelSourceInterface) (string, error) { + cloudtemplateIpSecTunnelSourceInterfaceMap, err := cloudtemplateIpSecTunnelSourceInterface.ToMap() + if err != nil { + return "", err + } + sourceInterface := fmt.Sprintf("gig%s", cloudtemplateIpSecTunnelSourceInterfaceMap["sourceInterfaceId"]) + return sourceInterface, nil +} + +func resourceAciCloudTemplateforVPNNetworkImport(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { + log.Printf("[DEBUG] %s: Beginning Import", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + cloudtemplateVpnNetwork, err := getRemoteTemplateforVPNNetwork(aciClient, dn) + if err != nil { + return nil, err + } + + schemaFilled, err := setTemplateforVPNNetworkAttributes(cloudtemplateVpnNetwork, d) + if err != nil { + return nil, err + } + + log.Printf("[DEBUG] Begining Import of cloud IPsec Tunnel attributes.") + cloudtemplateIpSecTunnelData, err := aciClient.ListCloudTemplateforIpSectunnel(dn) + if err != nil { + log.Printf("[DEBUG] Error while importing cloud IPsec Tunnel attributes %v", err) + } + + cloudtemplateIpSecTunnelSet := make([]map[string]interface{}, 0, 1) + for _, cloudtemplateIpSecTunnel := range cloudtemplateIpSecTunnelData { + + cloudIpSecTunnelAttMap, cloudtemplateIpSecTunnelDn, err := setCloudTemplateforIpSecTunnelAttributes(cloudtemplateIpSecTunnel, make(map[string]interface{})) + if err != nil { + d.SetId("") + return nil, err + } + + log.Printf("[DEBUG] Begining Import of cloud BGP IPV4 Peer attributes.") + bgpIPv4PeerData, err := aciClient.ListCloudTemplateBGPIPv4Peer(cloudtemplateIpSecTunnelDn) + if err != nil { + log.Printf("[DEBUG] Error while importing cloud BGP IPV4 Peer attributes %v", err) + } + for _, bgpIPv4Peer := range bgpIPv4PeerData { + bgpPeerAsnAtt, err := getASNfromBGPTPV4Peer(bgpIPv4Peer, make(map[string]string)) + if err != nil { + d.SetId("") + return nil, err + } + cloudIpSecTunnelAttMap["bgp_peer_asn"] = bgpPeerAsnAtt["bgp_peer_asn_att"] + } + log.Printf("[DEBUG] Import of cloud BGP IPV4 Peer finished successfully.") + + cloudtemplateIpSecTunnelSet = append(cloudtemplateIpSecTunnelSet, cloudIpSecTunnelAttMap) + } + d.Set("ipsec_tunnel", cloudtemplateIpSecTunnelSet) + log.Printf("[DEBUG] Import of cloud IPsec Tunnel finished successfully.") + + log.Printf("[DEBUG] %s: Import finished successfully", d.Id()) + return []*schema.ResourceData{schemaFilled}, nil +} + +func resourceAciCloudTemplateforVPNNetworkCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] TemplateforVPNNetwork: Beginning Creation") + aciClient := m.(*client.Client) + name := d.Get("name").(string) + TemplateforExternalNetworkDn := d.Get("aci_cloud_external_network_dn").(string) + + cloudtemplateVpnNetworkAttr := models.CloudTemplateforVPNNetworkAttributes{} + + nameAlias := "" + if NameAlias, ok := d.GetOk("name_alias"); ok { + nameAlias = NameAlias.(string) + } + + if Annotation, ok := d.GetOk("annotation"); ok { + cloudtemplateVpnNetworkAttr.Annotation = Annotation.(string) + } else { + cloudtemplateVpnNetworkAttr.Annotation = "{}" + } + + if Name, ok := d.GetOk("name"); ok { + cloudtemplateVpnNetworkAttr.Name = Name.(string) + } + + if RemoteSiteId, ok := d.GetOk("remote_site_id"); ok { + cloudtemplateVpnNetworkAttr.RemoteSiteId = RemoteSiteId.(string) + } + + if RemoteSiteName, ok := d.GetOk("remote_site_name"); ok { + cloudtemplateVpnNetworkAttr.RemoteSiteName = RemoteSiteName.(string) + } + cloudtemplateVpnNetwork := models.NewCloudTemplateforVPNNetwork(fmt.Sprintf(models.RncloudtemplateVpnNetwork, name), TemplateforExternalNetworkDn, nameAlias, cloudtemplateVpnNetworkAttr) + + err := aciClient.Save(cloudtemplateVpnNetwork) + if err != nil { + return diag.FromErr(err) + } + if ipSecTunnelPeers, ok := d.GetOk("ipsec_tunnel"); ok { + clopudIpSecTunnels := ipSecTunnelPeers.(*schema.Set).List() + // Looping throught List of IPsec Tunnels + for _, val := range clopudIpSecTunnels { + ipSecTunnels := val.(map[string]interface{}) + + cloudtemplateIpSecTunnelAttr := models.CloudTemplateforIpSectunnelAttributes{} + cloudtemplateIpSecTunnelAttr.Annotation = cloudtemplateVpnNetworkAttr.Annotation + cloudtemplateIpSecTunnelAttr.IkeVersion = ipSecTunnels["ike_version"].(string) + cloudtemplateIpSecTunnelAttr.Poolname = ipSecTunnels["subnet_pool_name"].(string) + cloudtemplateIpSecTunnelAttr.PreSharedKey = ipSecTunnels["pre_shared_key"].(string) + cloudtemplateIpSecTunnelAttr.Peeraddr = ipSecTunnels["public_ip_address"].(string) + + cloudtemplateIpSecTunnel := models.NewCloudTemplateforIpSectunnel(fmt.Sprintf(models.RncloudtemplateIpSecTunnel, cloudtemplateIpSecTunnelAttr.Peeraddr), cloudtemplateVpnNetwork.DistinguishedName, cloudtemplateIpSecTunnelAttr) + err := aciClient.Save(cloudtemplateIpSecTunnel) + if err != nil { + return diag.FromErr(err) + } + cloudtemplateBgpIpv4Attr := models.CloudTemplateBGPIPv4PeerAttributes{} + cloudtemplateBgpIpv4Attr.Peeraddr = "0.0.0.0/0" + cloudtemplateBgpIpv4Attr.Peerasn = ipSecTunnels["bgp_peer_asn"].(string) + + cloudtemplateBgpIpv4 := models.NewCloudTemplateBGPIPv4Peer(fmt.Sprintf(models.RncloudtemplateBgpIpv4, cloudtemplateBgpIpv4Attr.Peeraddr), cloudtemplateIpSecTunnel.DistinguishedName, cloudtemplateBgpIpv4Attr) + err = aciClient.Save(cloudtemplateBgpIpv4) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] cloudtemplateIpSecTunnelSourceInterface: Beginning Creation") + for _, sourceInterfaces := range ipSecTunnels["source_interfaces"].([]interface{}) { + sourceInterfaceAtt := sourceInterfaces.(string) + sourceInterfacePattern, err := regexp.MatchString("^gig[0-9]$", sourceInterfaceAtt) + if err != nil { + return diag.FromErr(err) + } + _, ipSecTunnelSourceInterfaceVal, _ := strings.Cut(sourceInterfaceAtt, "gig") + + if sourceInterfacePattern { + ipSecTunnelSourceInterfaceAttr := models.CloudTemplateforIpSectunnelSourceInterfaceAttributes{} + ipSecTunnelSourceInterfaceAttr.SourceInterfaceId = ipSecTunnelSourceInterfaceVal + + ipSecTunnelSourceInterface := models.NewCloudTemplateIpSecTunnelSourceInterface(fmt.Sprintf(models.RncloudtemplateIpSecTunnelSourceInterface, ipSecTunnelSourceInterfaceAttr.SourceInterfaceId), cloudtemplateIpSecTunnel.DistinguishedName, ipSecTunnelSourceInterfaceAttr) + err := aciClient.Save(ipSecTunnelSourceInterface) + if err != nil { + return diag.FromErr(err) + } + } + } + log.Printf("[DEBUG] : cloudtemplateIpSecTunnelSourceInterface Creation finished successfully") + } + } + + d.SetId(cloudtemplateVpnNetwork.DistinguishedName) + log.Printf("[DEBUG] %s: Creation finished successfully", d.Id()) + return resourceAciCloudTemplateforVPNNetworkRead(ctx, d, m) +} + +func resourceAciCloudTemplateforVPNNetworkUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] TemplateforVPNNetwork: Beginning Update") + aciClient := m.(*client.Client) + name := d.Get("name").(string) + TemplateforExternalNetworkDn := d.Get("aci_cloud_external_network_dn").(string) + + cloudtemplateVpnNetworkAttr := models.CloudTemplateforVPNNetworkAttributes{} + + nameAlias := "" + if NameAlias, ok := d.GetOk("name_alias"); ok { + nameAlias = NameAlias.(string) + } + + if Annotation, ok := d.GetOk("annotation"); ok { + cloudtemplateVpnNetworkAttr.Annotation = Annotation.(string) + } else { + cloudtemplateVpnNetworkAttr.Annotation = "{}" + } + + if Name, ok := d.GetOk("name"); ok { + cloudtemplateVpnNetworkAttr.Name = Name.(string) + } + + if RemoteSiteId, ok := d.GetOk("remote_site_id"); ok { + cloudtemplateVpnNetworkAttr.RemoteSiteId = RemoteSiteId.(string) + } + + if RemoteSiteName, ok := d.GetOk("remote_site_name"); ok { + cloudtemplateVpnNetworkAttr.RemoteSiteName = RemoteSiteName.(string) + } + cloudtemplateVpnNetwork := models.NewCloudTemplateforVPNNetwork(fmt.Sprintf(models.RncloudtemplateVpnNetwork, name), TemplateforExternalNetworkDn, nameAlias, cloudtemplateVpnNetworkAttr) + + cloudtemplateVpnNetwork.Status = "modified" + + err := aciClient.Save(cloudtemplateVpnNetwork) + if err != nil { + return diag.FromErr(err) + } + + if ipSecTunnelPeers, ok := d.GetOk("ipsec_tunnel"); ok { + clopudIpSecTunnels := ipSecTunnelPeers.(*schema.Set).List() + // Looping throught List of IPsec Tunnels + for _, val := range clopudIpSecTunnels { + ipSecTunnels := val.(map[string]interface{}) + + cloudtemplateIpSecTunnelAttr := models.CloudTemplateforIpSectunnelAttributes{} + cloudtemplateIpSecTunnelAttr.Annotation = cloudtemplateVpnNetworkAttr.Annotation + cloudtemplateIpSecTunnelAttr.IkeVersion = ipSecTunnels["ike_version"].(string) + cloudtemplateIpSecTunnelAttr.Poolname = ipSecTunnels["subnet_pool_name"].(string) + cloudtemplateIpSecTunnelAttr.PreSharedKey = ipSecTunnels["pre_shared_key"].(string) + cloudtemplateIpSecTunnelAttr.Peeraddr = ipSecTunnels["public_ip_address"].(string) + + cloudtemplateIpSecTunnel := models.NewCloudTemplateforIpSectunnel(fmt.Sprintf(models.RncloudtemplateIpSecTunnel, cloudtemplateIpSecTunnelAttr.Peeraddr), cloudtemplateVpnNetwork.DistinguishedName, cloudtemplateIpSecTunnelAttr) + err := aciClient.Save(cloudtemplateIpSecTunnel) + if err != nil { + return diag.FromErr(err) + } + cloudtemplateBgpIpv4Attr := models.CloudTemplateBGPIPv4PeerAttributes{} + cloudtemplateBgpIpv4Attr.Peeraddr = "0.0.0.0/0" + cloudtemplateBgpIpv4Attr.Peerasn = ipSecTunnels["bgp_peer_asn"].(string) + + cloudtemplateBgpIpv4 := models.NewCloudTemplateBGPIPv4Peer(fmt.Sprintf(models.RncloudtemplateBgpIpv4, cloudtemplateBgpIpv4Attr.Peeraddr), cloudtemplateIpSecTunnel.DistinguishedName, cloudtemplateBgpIpv4Attr) + err = aciClient.Save(cloudtemplateBgpIpv4) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] cloudtemplateIpSecTunnelSourceInterface: Beginning Creation") + for _, sourceInterfaces := range ipSecTunnels["source_interfaces"].([]interface{}) { + sourceInterfaceAtt := sourceInterfaces.(string) + sourceInterfacePattern, err := regexp.MatchString("^gig[0-9]$", sourceInterfaceAtt) + if err != nil { + return diag.FromErr(err) + } + _, ipSecTunnelSourceInterfaceVal, _ := strings.Cut(sourceInterfaceAtt, "gig") + + if sourceInterfacePattern { + ipSecTunnelSourceInterfaceAttr := models.CloudTemplateforIpSectunnelSourceInterfaceAttributes{} + ipSecTunnelSourceInterfaceAttr.SourceInterfaceId = ipSecTunnelSourceInterfaceVal + + ipSecTunnelSourceInterface := models.NewCloudTemplateIpSecTunnelSourceInterface(fmt.Sprintf(models.RncloudtemplateIpSecTunnelSourceInterface, ipSecTunnelSourceInterfaceAttr.SourceInterfaceId), cloudtemplateIpSecTunnel.DistinguishedName, ipSecTunnelSourceInterfaceAttr) + err := aciClient.Save(ipSecTunnelSourceInterface) + if err != nil { + return diag.FromErr(err) + } + } + } + log.Printf("[DEBUG] : cloudtemplateIpSecTunnelSourceInterface Creation finished successfully") + } + } + + d.SetId(cloudtemplateVpnNetwork.DistinguishedName) + log.Printf("[DEBUG] %s: Update finished successfully", d.Id()) + return resourceAciCloudTemplateforVPNNetworkRead(ctx, d, m) +} + +func resourceAciCloudTemplateforVPNNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] %s: Beginning Read", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + + cloudtemplateVpnNetwork, err := getRemoteTemplateforVPNNetwork(aciClient, dn) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + + _, err = setTemplateforVPNNetworkAttributes(cloudtemplateVpnNetwork, d) + if err != nil { + d.SetId("") + return nil + } + + log.Printf("[DEBUG] Begining Read of cloud IPsec Tunnel attributes.") + cloudtemplateIpSecTunnelData, err := aciClient.ListCloudTemplateforIpSectunnel(dn) + if err != nil { + log.Printf("[DEBUG] Error while reading cloud IPsec Tunnel attributes %v", err) + } + + cloudtemplateIpSecTunnelSet := make([]map[string]interface{}, 0, 1) + for _, cloudtemplateIpSecTunnel := range cloudtemplateIpSecTunnelData { + + cloudIpSecTunnelAttMap, cloudtemplateIpSecTunnelDn, err := setCloudTemplateforIpSecTunnelAttributes(cloudtemplateIpSecTunnel, make(map[string]interface{})) + if err != nil { + d.SetId("") + return nil + } + + log.Printf("[DEBUG] Begining Read of cloud BGP IPV4 Peer attributes.") + bgpIPv4PeerData, err := aciClient.ListCloudTemplateBGPIPv4Peer(cloudtemplateIpSecTunnelDn) + if err != nil { + log.Printf("[DEBUG] Error while reading cloud BGP IPV4 Peer attributes %v", err) + } + for _, bgpIPv4Peer := range bgpIPv4PeerData { + bgpPeerAsnAtt, err := getASNfromBGPTPV4Peer(bgpIPv4Peer, make(map[string]string)) + if err != nil { + d.SetId("") + return nil + } + cloudIpSecTunnelAttMap["bgp_peer_asn"] = bgpPeerAsnAtt["bgp_peer_asn_att"] + } + log.Printf("[DEBUG] Read cloud BGP IPV4 Peer finished successfully.") + + log.Printf("[DEBUG] Begining Read of cloud IPsec Tunnel Source Interface attributes.") + ipSectunnelSourceInterfaceData, err := aciClient.ListCloudTemplateforIpSectunnelSourceInterface(cloudtemplateIpSecTunnelDn) + if err != nil { + log.Printf("[DEBUG] Error while reading cloud IPsec Tunnel Source Interface attributes %v", err) + } + + ipSectunnelSourceInterfaceList := make([]string, 0, 1) + for _, ipSecTunnelSourceInterfaceValue := range ipSectunnelSourceInterfaceData { + ipSectunnelSourceInterfaceName, err := formatTemplateforIpSectunnelAttributes(ipSecTunnelSourceInterfaceValue) + // change set to get or format + if err != nil { + d.SetId("") + return nil + } + ipSectunnelSourceInterfaceList = append(ipSectunnelSourceInterfaceList, ipSectunnelSourceInterfaceName) + } + cloudIpSecTunnelAttMap["source_interfaces"] = ipSectunnelSourceInterfaceList + log.Printf("[DEBUG] : Read cloud IPsec Tunnel Source Interface finished successfully") + + cloudtemplateIpSecTunnelSet = append(cloudtemplateIpSecTunnelSet, cloudIpSecTunnelAttMap) + } + d.Set("ipsec_tunnel", cloudtemplateIpSecTunnelSet) + log.Printf("[DEBUG] Read cloud IPsec Tunnel finished successfully.") + + log.Printf("[DEBUG] %s: Read finished successfully", d.Id()) + return nil +} + +func resourceAciCloudTemplateforVPNNetworkDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + log.Printf("[DEBUG] %s: Beginning Destroy", d.Id()) + aciClient := m.(*client.Client) + dn := d.Id() + + err := aciClient.DeleteByDn(dn, "cloudtemplateVpnNetwork") + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] %s: Destroy finished successfully", d.Id()) + d.SetId("") + return diag.FromErr(err) +} diff --git a/aci/resource_aci_cloudtemplatevpnnetwork_test.go b/aci/resource_aci_cloudtemplatevpnnetwork_test.go new file mode 100644 index 000000000..ab6f95dcb --- /dev/null +++ b/aci/resource_aci_cloudtemplatevpnnetwork_test.go @@ -0,0 +1,157 @@ +package aci + +import ( + "fmt" + "testing" + + "github.com/ciscoecosystem/aci-go-client/v2/client" + "github.com/ciscoecosystem/aci-go-client/v2/models" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccAciCloudTemplateforVPNNetwork_Basic(t *testing.T) { + var templatefor_vpn_network models.CloudTemplateforVPNNetwork + fv_tenant_name := acctest.RandString(5) + aci_cloud_external_network_vpn_network := acctest.RandString(5) + cloud_external_network := acctest.RandString(5) + cloudtemplate_vpn_network_name := acctest.RandString(5) + description := "templatefor_vpn_network created while acceptance testing" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAciCloudTemplateforVPNNetworkDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckAciCloudTemplateforVPNNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciCloudTemplateforVPNNetworkExists("aci_cloud_external_network_vpn_network.footemplatefor_vpn_network", &templatefor_vpn_network), + testAccCheckAciCloudTemplateforVPNNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name, description, &templatefor_vpn_network), + ), + }, + }, + }) +} + +func TestAccAciCloudTemplateforVPNNetwork_Update(t *testing.T) { + var templatefor_vpn_network models.CloudTemplateforVPNNetwork + fv_tenant_name := acctest.RandString(5) + aci_cloud_external_network_vpn_network := acctest.RandString(5) + cloud_external_network := acctest.RandString(5) + cloudtemplate_vpn_network_name := acctest.RandString(5) + description := "templatefor_vpn_network created while acceptance testing" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAciCloudTemplateforVPNNetworkDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckAciCloudTemplateforVPNNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciCloudTemplateforVPNNetworkExists("aci_cloud_external_network_vpn_network.footemplatefor_vpn_network", &templatefor_vpn_network), + testAccCheckAciCloudTemplateforVPNNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name, description, &templatefor_vpn_network), + ), + }, + { + Config: testAccCheckAciCloudTemplateforVPNNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAciCloudTemplateforVPNNetworkExists("aci_cloud_external_network_vpn_network.footemplatefor_vpn_network", &templatefor_vpn_network), + testAccCheckAciCloudTemplateforVPNNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name, description, &templatefor_vpn_network), + ), + }, + }, + }) +} + +func testAccCheckAciCloudTemplateforVPNNetworkConfig_basic(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name string) string { + return fmt.Sprintf(` + + resource "aci_tenant" "footenant" { + name = "%s" + description = "tenant created while acceptance testing" + + } + + resource "aci_infra_network_template" "fooinfra_network_template" { + name = "%s" + description = "infra_network_template created while acceptance testing" + tenant_dn = aci_tenant.footenant.id + } + + resource "aci_cloud_external_network" "footemplatefor_external_network" { + name = "%s" + description = "templatefor_external_network created while acceptance testing" + infra_network_template_dn = aci_infra_network_template.fooinfra_network_template.id + } + + resource "aci_cloud_external_network_vpn_network" "footemplatefor_vpn_network" { + name = "%s" + description = "templatefor_vpn_network created while acceptance testing" + aci_cloud_external_network_dn = aci_cloud_external_network.footemplatefor_external_network.id + } + + `, fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name) +} + +func testAccCheckAciCloudTemplateforVPNNetworkExists(name string, templatefor_vpn_network *models.CloudTemplateforVPNNetwork) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + + if !ok { + return fmt.Errorf("Template for VPN Network %s not found", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Template for VPN Network dn was set") + } + + client := testAccProvider.Meta().(*client.Client) + + cont, err := client.Get(rs.Primary.ID) + if err != nil { + return err + } + + templatefor_vpn_networkFound := models.CloudTemplateforVPNNetworkFromContainer(cont) + if templatefor_vpn_networkFound.DistinguishedName != rs.Primary.ID { + return fmt.Errorf("Template for VPN Network %s not found", rs.Primary.ID) + } + *templatefor_vpn_network = *templatefor_vpn_networkFound + return nil + } +} + +func testAccCheckAciCloudTemplateforVPNNetworkDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*client.Client) + for _, rs := range s.RootModule().Resources { + if rs.Type == "aci_cloud_external_network_vpn_network" { + cont, err := client.Get(rs.Primary.ID) + templatefor_vpn_network := models.CloudTemplateforVPNNetworkFromContainer(cont) + if err == nil { + return fmt.Errorf("Template for VPN Network %s Still exists", templatefor_vpn_network.DistinguishedName) + } + } else { + continue + } + } + return nil +} + +func testAccCheckAciCloudTemplateforVPNNetworkAttributes(fv_tenant_name, aci_cloud_external_network_vpn_network, cloud_external_network, cloudtemplate_vpn_network_name, description string, templatefor_vpn_network *models.CloudTemplateforVPNNetwork) resource.TestCheckFunc { + return func(s *terraform.State) error { + if cloudtemplate_vpn_network_name != GetMOName(templatefor_vpn_network.DistinguishedName) { + return fmt.Errorf("Bad cloudtemplate_vpn_network %s", GetMOName(templatefor_vpn_network.DistinguishedName)) + } + + if cloud_external_network != GetMOName(GetParentDn(templatefor_vpn_network.DistinguishedName)) { + return fmt.Errorf(" Bad cloudtemplate_ext_network %s", GetMOName(GetParentDn(templatefor_vpn_network.DistinguishedName))) + } + if description != templatefor_vpn_network.Description { + return fmt.Errorf("Bad templatefor_vpn_network Description %s", templatefor_vpn_network.Description) + } + return nil + } +} diff --git a/aci/resource_aci_infraportblk.go b/aci/resource_aci_infraportblk.go index d9de5f7e1..59c3d02d8 100644 --- a/aci/resource_aci_infraportblk.go +++ b/aci/resource_aci_infraportblk.go @@ -172,7 +172,6 @@ func resourceAciAccessPortBlockCreate(ctx context.Context, d *schema.ResourceDat tp := models.G(contList[i], "name") blkNames = append(blkNames, tp) } - log.Println("check .. : ", blkNames) cnt := contListLen + 1 for true { diff --git a/aci/resource_aci_vzbrcp.go b/aci/resource_aci_vzbrcp.go index ec8dba1fa..cd9aa978d 100644 --- a/aci/resource_aci_vzbrcp.go +++ b/aci/resource_aci_vzbrcp.go @@ -657,12 +657,10 @@ func resourceAciContractCreate(ctx context.Context, d *schema.ResourceData, m in if filter["filter_entry"] != nil { vzfilterentries := filter["filter_entry"].([]interface{}) - log.Println("Filter entries ... :", vzfilterentries) for _, entry := range vzfilterentries { vzEntryAttr := models.FilterEntryAttributes{} vzEntry := entry.(map[string]interface{}) - log.Println("Entries ......... :", vzEntry) entryDesc := vzEntry["description"].(string) entryName := vzEntry["filter_entry_name"].(string) @@ -730,7 +728,6 @@ func resourceAciContractCreate(ctx context.Context, d *schema.ResourceData, m in // fMap["id"] = vzFilter.DistinguishedName filterIDS = append(filterIDS, vzFilter.DistinguishedName) } - log.Println("Check ... :", filterIDS) d.Set("filter_ids", filterIDS) d.Set("filter_entry_ids", filterentryIDS) } else { @@ -838,7 +835,6 @@ func resourceAciContractUpdate(ctx context.Context, d *schema.ResourceData, m in vzFilter := models.NewFilter(fmt.Sprintf("flt-%s", name), TenantDn, desc, vzFilterAttr) - // vzFilter.Status = "modified" err := aciClient.Save(vzFilter) if err != nil { return diag.FromErr(err) @@ -846,12 +842,10 @@ func resourceAciContractUpdate(ctx context.Context, d *schema.ResourceData, m in if filter["filter_entry"] != nil { vzfilterentries := filter["filter_entry"].([]interface{}) - log.Println("Filter entries ... :", vzfilterentries) for _, entry := range vzfilterentries { vzEntryAttr := models.FilterEntryAttributes{} vzEntry := entry.(map[string]interface{}) - log.Println("Entries ......... :", vzEntry) entryDesc := vzEntry["description"].(string) entryName := vzEntry["filter_entry_name"].(string) @@ -974,7 +968,6 @@ func resourceAciContractRead(ctx context.Context, d *schema.ResourceData, m inte return nil } filters := d.Get("filter_ids").([]interface{}) - log.Println("Check ... :", filters) vzFilters := make([]*models.Filter, 0, 1) vzEntries := make([]*models.FilterEntry, 0, 1) diff --git a/aci/resource_aci_vzentry.go b/aci/resource_aci_vzentry.go index 433244ded..5fa72c824 100644 --- a/aci/resource_aci_vzentry.go +++ b/aci/resource_aci_vzentry.go @@ -280,7 +280,6 @@ func setFilterEntryAttributes(vzEntry *models.FilterEntry, d *schema.ResourceDat if err != nil { return d, err } - log.Println("Check .... :", d.Get("d_from_port")) d.Set("filter_dn", GetParentDn(dn, fmt.Sprintf("/e-%s", vzEntryMap["name"]))) diff --git a/aci/utils.go b/aci/utils.go index c309caafc..0c55e59bb 100644 --- a/aci/utils.go +++ b/aci/utils.go @@ -361,3 +361,21 @@ func getTargetObjectName(paramMap map[string]interface{}, targetDn, targetName s return paramMap[targetName].(string) } } + +// Compares two list-of-strings and sends a new list-of-strings with only unique strings from the first list. +// Reversing the two lists generates a new list with different outputs +func getStringsFromListNotInOtherList(previousValueList interface{}, newValueList interface{}) (generatedList []interface{}) { + for _, oldValue := range previousValueList.([]interface{}) { + found := false + for _, newValue := range newValueList.([]interface{}) { + if oldValue == newValue { + found = true + break + } + } + if !found { + generatedList = append(generatedList, oldValue) + } + } + return generatedList +} diff --git a/examples/cloud_external_network/aws/main.tf b/examples/cloud_external_network/aws/main.tf new file mode 100644 index 000000000..36a9f77ba --- /dev/null +++ b/examples/cloud_external_network/aws/main.tf @@ -0,0 +1,106 @@ +terraform { + required_providers { + aci = { + source = "ciscodevnet/aci" + } + } +} + +provider "aci" { + username = "" + password = "" + url = "" + insecure = true +} + +data "aci_tenant" "infra_tenant" { + name = "infra" +} + +resource "aci_vrf" "vrf" { + tenant_dn = data.aci_tenant.infra_tenant.id # Create vrf only in infra tenant. + name = "cloudVrf" +} + +resource "aci_cloud_ipsec_tunnel_subnet_pool" "ipsec_tunnel_subnet_pool" { + name = "cloud_pool" + subnet_pool = "169.254.0.0/16" +} + +# AWS Cloud +# all_regions is set to "yes" only in AWS Cloud +# +# router_type = "tgw" +resource "aci_cloud_external_network" "external_network_aws_tgw" { + name = "cloud_external_network" + vrf_dn = aci_vrf.vrf.id + all_regions = "yes" + cloud_vendor = "aws" + router_type = "tgw" + hub_network_name = "Hub" #hub_network_name should be set when router_type is "tgw" in AWS cloud +} + +resource "aci_cloud_external_network_vpn_network" "vpn_network" { + aci_cloud_external_network_dn = aci_cloud_external_network.external_network_aws_tgw.id + name = "cloud_vpn_network" + ipsec_tunnel { + ike_version = "ikev1" + public_ip_address = "10.10.10.2" + subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name + bgp_peer_asn = "1002" + } + ipsec_tunnel { + ike_version = "ikev2" + public_ip_address = "10.10.10.7" + subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name + bgp_peer_asn = "1005" + } +} + +data "aci_cloud_external_network" "external_network_example" { + name = aci_cloud_external_network.external_network_aws_tgw.name +} + +output "external_network_output" { + value = data.aci_cloud_external_network.external_network_example +} + +data "aci_cloud_external_network_vpn_network" "example" { + aci_cloud_external_network_dn = aci_cloud_external_network_vpn_network.vpn_network.aci_cloud_external_network_dn + name = aci_cloud_external_network_vpn_network.vpn_network.name +} + +output "vpn_network_output" { + value = data.aci_cloud_external_network_vpn_network.example +} + +# # AWS Cloud +# # all_regions is set to "yes" only in AWS Cloud +# # +# # # router_type = "c8kv" +# resource "aci_cloud_external_network" "external_network_aws_c8kv" { +# name = "cloud_external_network" +# vrf_dn = aci_vrf.vrf.id +# all_regions = "yes" +# cloud_vendor = "aws" +# router_type = "c8kv" +# } + +# resource "aci_cloud_external_network_vpn_network" "vpn_network_c8kv" { +# aci_cloud_external_network_dn = aci_cloud_external_network.external_network_aws_c8kv.id +# name = "cloud_vpn_network" +# ipsec_tunnel { +# ike_version = "ikev1" +# public_ip_address = "10.10.10.2" +# subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name +# bgp_peer_asn = "1002" +# source_interfaces = ["gig2", "gig3", "gig4"] #source_interfaces available when router_type is "c8kv" in AWS cloud +# } +# ipsec_tunnel { +# ike_version = "ikev2" +# public_ip_address = "10.10.10.7" +# subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name +# bgp_peer_asn = "1005" +# source_interfaces = ["gig2"] #source_interfaces available when router_type is "c8kv" in AWS cloud +# } +# } \ No newline at end of file diff --git a/examples/cloud_external_network/azure/main.tf b/examples/cloud_external_network/azure/main.tf new file mode 100644 index 000000000..ef655c9fd --- /dev/null +++ b/examples/cloud_external_network/azure/main.tf @@ -0,0 +1,73 @@ +terraform { + required_providers { + aci = { + source = "ciscodevnet/aci" + } + } +} + +provider "aci" { + username = "" + password = "" + url = "" + insecure = true +} + +data "aci_tenant" "infra_tenant" { + name = "infra" +} + +resource "aci_vrf" "vrf" { + tenant_dn = data.aci_tenant.infra_tenant.id # Create vrf only in infra tenant. + name = "cloudVrf" +} + +resource "aci_cloud_ipsec_tunnel_subnet_pool" "ipsec_tunnel_subnet_pool" { + name = "cloud_pool" + subnet_pool = "169.254.0.0/16" +} + +# Azure Cloud +# all_regions is set to "yes" only in Azure Cloud +resource "aci_cloud_external_network" "external_network_azure" { + name = "cloud_external_network" + vrf_dn = aci_vrf.vrf.id + all_regions = "yes" +} + +# Azure Cloud +resource "aci_cloud_external_network_vpn_network" "vpn_network" { + aci_cloud_external_network_dn = aci_cloud_external_network.external_network_azure.id + name = "cloud_vpn_network" + ipsec_tunnel { + ike_version = "ikev1" + public_ip_address = "10.10.10.2" + subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name + bgp_peer_asn = "1002" + source_interfaces = ["gig2", "gig3", "gig4"] #source_interfaces available only in Azure cloud + } + ipsec_tunnel { + ike_version = "ikev2" + public_ip_address = "10.10.10.7" + subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name + bgp_peer_asn = "1005" + source_interfaces = ["gig2"] #source_interfaces available only in Azure cloud + } +} + +data "aci_cloud_external_network" "external_network_example" { + name = aci_cloud_external_network.external_network_azure.name +} + +output "external_network_output" { + value = data.aci_cloud_external_network.external_network_example +} + +data "aci_cloud_external_network_vpn_network" "example" { + aci_cloud_external_network_dn = aci_cloud_external_network_vpn_network.vpn_network.aci_cloud_external_network_dn + name = aci_cloud_external_network_vpn_network.vpn_network.name +} + +output "vpn_network_output" { + value = data.aci_cloud_external_network_vpn_network.example +} \ No newline at end of file diff --git a/examples/cloud_external_network/gcp/main.tf b/examples/cloud_external_network/gcp/main.tf new file mode 100644 index 000000000..0f11e6efa --- /dev/null +++ b/examples/cloud_external_network/gcp/main.tf @@ -0,0 +1,73 @@ +terraform { + required_providers { + aci = { + source = "ciscodevnet/aci" + } + } +} + +provider "aci" { + username = "" + password = "" + url = "" + insecure = true +} + +data "aci_tenant" "infra_tenant" { + name = "infra" +} + +resource "aci_vrf" "vrf" { + tenant_dn = data.aci_tenant.infra_tenant.id # Create vrf only in infra tenant. + name = "cloudVrf" +} + +resource "aci_cloud_ipsec_tunnel_subnet_pool" "ipsec_tunnel_subnet_pool" { + name = "cloud_pool" + subnet_pool = "169.254.0.0/16" +} + +# GCP cloud +# all_regions is set to "no" and regions can be set only in GCP Cloud +resource "aci_cloud_external_network" "external_network_gcp" { + name = "cloud_external_network" + vrf_dn = aci_vrf.vrf.id + cloud_vendor = "gcp" + all_regions = "yes" + regions = ["europe-west4", "europe-west3"] +} + +# GCP cloud +resource "aci_cloud_external_network_vpn_network" "vpn_network" { + aci_cloud_external_network_dn = aci_cloud_external_network.external_network_gcp.id + name = "cloud_vpn_network" + ipsec_tunnel { + ike_version = "ikev1" + public_ip_address = "10.10.10.2" + subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name + bgp_peer_asn = "1002" + } + ipsec_tunnel { + ike_version = "ikev2" + public_ip_address = "10.10.10.7" + subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name + bgp_peer_asn = "1005" + } +} + +data "aci_cloud_external_network" "external_network_example" { + name = aci_cloud_external_network.external_network_gcp.name +} + +output "external_network_output" { + value = data.aci_cloud_external_network.external_network_example +} + +data "aci_cloud_external_network_vpn_network" "example" { + aci_cloud_external_network_dn = aci_cloud_external_network_vpn_network.vpn_network.aci_cloud_external_network_dn + name = aci_cloud_external_network_vpn_network.vpn_network.name +} + +output "vpn_network_output" { + value = data.aci_cloud_external_network_vpn_network.example +} \ No newline at end of file diff --git a/examples/cloud_ipsec_tunnel_subnet_pool/main.tf b/examples/cloud_ipsec_tunnel_subnet_pool/main.tf new file mode 100644 index 000000000..e86cd7a79 --- /dev/null +++ b/examples/cloud_ipsec_tunnel_subnet_pool/main.tf @@ -0,0 +1,27 @@ +terraform { + required_providers { + aci = { + source = "ciscodevnet/aci" + } + } +} + +provider "aci" { + username = "" + password = "" + url = "" + insecure = true +} + +resource "aci_cloud_ipsec_tunnel_subnet_pool" "ipsec_tunnel_subnet_pool" { + name = "test" + subnet_pool = "160.254.10.0/16" +} + +data "aci_cloud_ipsec_tunnel_subnet_pool" "example" { + subnet_pool = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool +} + +output "ipsec_tunnel_subnet_pool_output" { + value = data.aci_cloud_ipsec_tunnel_subnet_pool.example +} diff --git a/website/docs/d/cloud_external_network.html.markdown b/website/docs/d/cloud_external_network.html.markdown new file mode 100644 index 000000000..9678b10fc --- /dev/null +++ b/website/docs/d/cloud_external_network.html.markdown @@ -0,0 +1,50 @@ +--- +subcategory: "Cloud" +layout: "aci" +page_title: "ACI: aci_cloud_external_network" +sidebar_current: "docs-aci-data-source-aci_cloud_external_network" +description: |- + Data source for ACI Cloud External Network +--- + +# aci_cloud_external_network # + +Data source for ACI Cloud External Network +Note: This resource is supported in Cloud APIC version > 25.0 only. + + +## API Information ## + +* `Class` - cloudtemplateExtNetwork +* `Distinguished Name` - uni/tn-{tenant_name}/infranetwork-{infra_network_name}/extnetwork-{external_network_name} + +## GUI Information ## + +* `Location` - Cloud APIC -> Application Management -> External Networks + + + +## Example Usage ## + +```hcl +data "aci_cloud_external_network" "example" { + name = "example" +} +``` + +## Argument Reference ## + +* `name` - (Required) Name of the Cloud External Network. + +## Attribute Reference ## +* `id` - Attribute id set to the Dn of the Cloud External Network. +* `annotation` - (Optional) Annotation of the Cloud External Network. +* `name_alias` - (Optional) Name Alias of the Cloud External Network. +* `vrf_dn` - (Optional) Distinguished name of the VRF. Note that the VRF has to be created under the infra tenant. +* `hub_network_name` - (Optional) Hub Network name of the Cloud External Network. +* `vpn_router_name` - (Optional) VPN Router name of the Cloud External Network. +* `host_router_name` - (Optional) Host Router name of the Cloud External Network. +* `all_regions` - (Optional) Selects all regions available to the Cloud External Network. This option is always set to "yes" for Azure and AWS cAPICs and "no" in GCP cAPIC. +* `regions` - (Optional) Manually adds the regions to the Cloud External Network. This option is only available in GCP cAPICs. +* `router_type` - (Optional) Router type. Allowed values are "c8kv", "tgw". (Available only for AWS cAPIC). +* `cloud_vendor` - (Optional) Name of the vendor. Allowed values are "aws", "azure", "gcp". \ No newline at end of file diff --git a/website/docs/d/cloud_ipsec_tunnel_subnet_pool.html.markdown b/website/docs/d/cloud_ipsec_tunnel_subnet_pool.html.markdown new file mode 100644 index 000000000..27f0d9cd6 --- /dev/null +++ b/website/docs/d/cloud_ipsec_tunnel_subnet_pool.html.markdown @@ -0,0 +1,42 @@ +--- +subcategory: "Cloud" +layout: "aci" +page_title: "ACI: aci_cloud_ipsec_tunnel_subnet_pool" +sidebar_current: "docs-aci-data-source-aci_cloud_ipsec_tunnel_subnet_pool" +description: |- + Data source for the ACI Cloud Subnet Pool for IPsec Tunnels +--- + +# aci_cloud_ipsec_tunnel_subnet_pool # + +Data source for the ACI Cloud Subnet Pool for IPsec Tunnels +Note: This resource is supported in Cloud APIC version > 25.0 only. + + +## API Information ## + +* `Class` - cloudtemplateIpSecTunnelSubnetPool +* `Distinguished Name` - uni/tn-{tenant_name}/infranetwork-{infra_network_name}/ipsecsubnetpool-[{subnet_pool}] + +## GUI Information ## + +* `Location` - Cloud APIC -> Infrastructure -> Inter-Site Connectivity -> Region Management + + + +## Example Usage ## + +```hcl +data "aci_cloud_ipsec_tunnel_subnet_pool" "example" { + name = "example" + subnet_pool = "160.254.10.0/16" +} +``` + +## Argument Reference ## + +* `subnet_pool` - (Required) Subnet of the Subnet Pool for IPsec Tunnels object. + +## Attribute Reference ## +* `annotation` - (Optional) Annotation of the Subnet Pool for IPsec Tunnels object. +* `name` - (Required) Subnet Pool Name of the Subnet Pool for IPsec Tunnels object. diff --git a/website/docs/d/cloud_vpn_network.html.markdown b/website/docs/d/cloud_vpn_network.html.markdown new file mode 100644 index 000000000..39222897d --- /dev/null +++ b/website/docs/d/cloud_vpn_network.html.markdown @@ -0,0 +1,53 @@ +--- +subcategory: "Cloud" +layout: "aci" +page_title: "ACI: aci_cloud_external_network_vpn_network" +sidebar_current: "docs-aci-data-source-aci_cloud_external_network_vpn_network" +description: |- + Data source for ACI Cloud Template for VPN Network +--- + +# aci_cloud_external_network_vpn_network # + +Data source for ACI Cloud Template for VPN Network +Note: This resource is supported in Cloud APIC version > 25.0 only. + + +## API Information ## + +* `Class` - cloudtemplateVpnNetwork +* `Distinguished Name` - uni/tn-{tenant_name}/infranetwork-{infra_network_name}/extnetwork-{external_network_name}/vpnnetwork-{vpn_network_name} + +## GUI Information ## + +* `Location` - Cloud APIC -> Application Management -> External Networks -> VPN Networks + + + +## Example Usage ## + +```hcl +data "aci_cloud_external_network_vpn_network" "example" { + aci_cloud_external_network_dn = aci_cloud_external_network.example.id + name = "example" +} +``` + +## Argument Reference ## + +* `aci_cloud_external_network_dn` - (Required) Distinguished name of parent TemplateforExternalNetwork object. +* `name` - (Required) Name of the Cloud VPN Network object. + +## Attribute Reference ## +* `id` - Attribute id set to the Dn of the Cloud VPN Network. +* `remote_site_id` - (Optional) Remote Site ID. +* `remote_site_name` - (Optional) Name of the Remote Site. +* `ipsec_tunnel` - (Optional) IPsec tunnel destination (cloudtemplateIpSecTunnelSourceInterface class). Type: Block. + * `ike_version` - (Required) IKE version. Allowed values are "ikev1", "ikev2", and default value is "ikev2". + * `public_ip_address` - (Required) Peer address of the Cloud IPsec tunnel object. + * `subnet_pool_name` - (Required) Subnet Pool Name. + * `pre_shared_key` - (Optional) Pre Shared Key for all tunnels to this peer address. + * `bgp_peer_asn` - (Required) BGP ASN Number. A number that uniquely identifies an autonomous system. + * `source_interfaces` - (Optional) Source Interface Ids of the object for IPsec tunnel Source Interface. It is available only on Azure cAPIC. + + diff --git a/website/docs/r/cloudad.html.markdown b/website/docs/r/cloud_ad.html.markdown similarity index 100% rename from website/docs/r/cloudad.html.markdown rename to website/docs/r/cloud_ad.html.markdown diff --git a/website/docs/r/cloudcredentials.html.markdown b/website/docs/r/cloud_credentials.html.markdown similarity index 100% rename from website/docs/r/cloudcredentials.html.markdown rename to website/docs/r/cloud_credentials.html.markdown diff --git a/website/docs/r/cloud_external_network.html.markdown b/website/docs/r/cloud_external_network.html.markdown new file mode 100644 index 000000000..d29c6ac18 --- /dev/null +++ b/website/docs/r/cloud_external_network.html.markdown @@ -0,0 +1,74 @@ +--- +subcategory: "Cloud" +layout: "aci" +page_title: "ACI: aci_cloud_external_network" +sidebar_current: "docs-aci-resource-aci_cloud_external_network" +description: |- + Manages ACI Cloud External Network +--- + +# aci_cloud_external_network # + +Manages ACI Cloud External Network. +Note: This resource is supported in Cloud APIC version > 25.0 only. + +## API Information ## + +* `Class` - cloudtemplateExtNetwork +* `Distinguished Name` - uni/tn-{tenant_name}/infranetwork-{infra_network_name}/extnetwork-{external_network_name} + +## GUI Information ## + +* `Location` - Cloud APIC -> Application Management -> External Networks + + +## Example Usage ## + +```hcl +resource "aci_cloud_external_network" "example" { + name = "example" + annotation = "orchestrator:terraform" + vrf_dn = aci_vrf.vrf.id +} + +# GCP cloud - all_regions is set to "no" and regions can be set only in GCP Cloud +resource "aci_cloud_external_network" "external_network" { + name = "cloud_external_network" + vrf_dn = aci_vrf.vrf.id + cloud_vendor = "gcp" + regions = ["europe-west3", "europe-west4"] +} + +# Azure Cloud - all_regions is set to "yes" only in Azure Cloud +resource "aci_cloud_external_network" "external_network" { + name = "cloud_external_network" + vrf_dn = aci_vrf.vrf.id + all_regions = "yes" +} +``` + +## Argument Reference ## + +* `name` - (Required) Name of the Cloud External Network. +* `name_alias` - (Optional) Name Alias of the Cloud External Network. +* `annotation` - (Optional) Annotation of the Cloud External Network. +* `vrf_dn` - (Required) Distinguished name of the VRF. Note that the VRF has to be created under the infra tenant. +* `hub_network_name` - (Optional) Hub Network name of the Cloud External Network. +* `vpn_router_name` - (Optional) VPN Router name of the Cloud External Network. +* `host_router_name` - (Optional) Host Router name of the Cloud External Network. +* `all_regions` - (Optional) Selects all regions available to the Cloud External Network. This option is always set to "yes" for Azure cAPICs. +* `regions` - (Optional) Manually adds the regions to the Cloud External Network. This option is only available in GCP cAPICs. +* `router_type` - (Optional) Router type. Allowed values are "c8kv", "tgw". (Available only for AWS cAPIC). +* `cloud_vendor` - (Optional) Name of the vendor. Allowed values are "aws", "azure", "gcp". + + + +## Importing ## + +An existing Cloud External Network can be [imported][docs-import] into this resource via its Dn, via the following command: +[docs-import]: https://www.terraform.io/docs/import/index.html + + +``` +terraform import aci_cloud_external_network.example "" +``` \ No newline at end of file diff --git a/website/docs/r/cloud_ipsec_tunnel_subnet_pool.html.markdown b/website/docs/r/cloud_ipsec_tunnel_subnet_pool.html.markdown new file mode 100644 index 000000000..55f1d6edf --- /dev/null +++ b/website/docs/r/cloud_ipsec_tunnel_subnet_pool.html.markdown @@ -0,0 +1,49 @@ +--- +subcategory: "Cloud" +layout: "aci" +page_title: "ACI: aci_cloud_ipsec_tunnel_subnet_pool +sidebar_current: "docs-aci-resource-aci_cloud_ipsec_tunnel_subnet_pool" +description: |- + Manages ACI Cloud Subnet Pool for IPsec Tunnels +--- + +# aci_cloud_ipsec_tunnel_subnet_pool # + +Manages ACI Cloud Subnet Pool for IPsec Tunnels +Note: This resource is supported in Cloud APIC version > 25.0 only. + +## API Information ## + +* `Class` - cloudtemplateIpSecTunnelSubnetPool +* `Distinguished Name` - uni/tn-{tenant_name}/infranetwork-{infra_network_name}/ipsecsubnetpool-[{subnetpool}] + +## GUI Information ## + +* `Location` - Cloud APIC -> Infrastructure -> Inter-Site Connectivity -> Region Management + + +## Example Usage ## + +```hcl +resource "aci_cloud_ipsec_tunnel_subnet_pool" "example" { + name = "subent_pool_1" + subnet_pool = "160.254.10.0/16" +} +``` + +## Argument Reference ## + +* `name` - (Required) Subnet Pool Name of the Subnet Pool for IPsec Tunnels object. +* `subnet_pool` - (Required) Subnetpool address of the Subnet Pool for IPsec Tunnels object. +* `annotation` - (Optional) Annotation of the Subnet Pool for IPsec Tunnels object. + + +## Importing ## + +An existing Cloud Subnet Pool for IPsec Tunnels can be [imported][docs-import] into this resource via its Dn, via the following command: +[docs-import]: https://www.terraform.io/docs/import/index.html + + +``` +terraform import aci_cloud_ipsec_tunnel_subnet_pool.example "" +``` \ No newline at end of file diff --git a/website/docs/r/cloud_vpn_network.html.markdown b/website/docs/r/cloud_vpn_network.html.markdown new file mode 100644 index 000000000..5e9293fb1 --- /dev/null +++ b/website/docs/r/cloud_vpn_network.html.markdown @@ -0,0 +1,66 @@ +--- +subcategory: "Cloud" +layout: "aci" +page_title: "ACI: aci_cloud_external_network_vpn_network" +sidebar_current: "docs-aci-resource-aci_cloud_external_network_vpn_network" +description: |- + Manages ACI Cloud Template for VPN Network +--- + +# aci_cloud_external_network_vpn_network # + +Manages ACI Cloud Template for VPN Network +Note: This resource is supported in Cloud APIC version > 25.0 only. + +## API Information ## + +* `Class` - cloudtemplateVpnNetwork +* `Distinguished Name` - uni/tn-{tenant_name}/infranetwork-{infra_network_name}/extnetwork-{external_network_name}/vpnnetwork-{vpn_network_name} + +## GUI Information ## + +* `Location` - Cloud APIC -> Application Management -> External Networks -> VPN Networks + + +## Example Usage ## + +```hcl +resource "aci_cloud_external_network_vpn_network" "example" { + aci_cloud_external_network_dn = aci_cloud_external_network.example.id + name = "example" + remote_site_id = "0" + remote_site_name = "remote_site_1" + ipsec_tunnel { + ike_version = "ikev2" + public_ip_address = "10.10.10.2" + subnet_pool_name = aci_cloud_ipsec_tunnel_subnet_pool.ipsec_tunnel_subnet_pool.subnet_pool_name + bgp_peer_asn = "1000" + source_interfaces = ["gig2", "gig3", "gig4"] + } +} +``` + +## Argument Reference ## + +* `aci_cloud_external_network_dn` - (Required) Distinguished name of the parent TemplateforExternalNetwork object. +* `name` - (Required) Name of the Cloud VPN Network object. +* `remote_site_id` - (Optional) Remote Site ID. Allowed range is 0-1000 and default value is "0". +* `remote_site_name` - (Optional) Name of the Remote Site. +* `ipsec_tunnel` - (Optional) IPsec tunnel destination (cloudtemplateIpSecTunnelSourceInterface class). Type: Block. + * `ike_version` - (Required) IKE version. Allowed values are "ikev1", "ikev2", and default value is "ikev2". + * `public_ip_address` - (Required) Peer address of the Cloud IPsec tunnel object. + * `subnet_pool_name` - (Required) Subnet Pool Name. + * `pre_shared_key` - (Optional) Pre Shared Key for all tunnels to this peeraddr. + * `bgp_peer_asn` - (Required) BGP ASN Number. A number that uniquely identifies an autonomous system. + * `source_interfaces` - (Optional) Source Interface Ids of the object for IPsec tunnel Source Interface. It is available only on Azure cAPIC. + + +## Importing ## + +An existing Cloud VPN Network can be [imported][docs-import] into this resource via its Dn, via the following command: +[docs-import]: https://www.terraform.io/docs/import/index.html + + +``` +terraform import aci_cloud_external_network_vpn_network.example "" +``` \ No newline at end of file