From 2b4ae034e81cf634efd6458f25edada1bb782cfe Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 27 Mar 2018 11:07:42 -0400 Subject: [PATCH 1/5] New Resource: `azurerm_express_route_circuit_peering` --- azurerm/config.go | 5 + ..._arm_express_route_circuit_peering_test.go | 76 +++++ azurerm/provider.go | 1 + ...ource_arm_express_route_circuit_peering.go | 274 ++++++++++++++++++ ..._arm_express_route_circuit_peering_test.go | 255 ++++++++++++++++ website/azurerm.erb | 4 + ...xpress_route_circuit_peering.html.markdown | 94 ++++++ 7 files changed, 709 insertions(+) create mode 100644 azurerm/import_arm_express_route_circuit_peering_test.go create mode 100644 azurerm/resource_arm_express_route_circuit_peering.go create mode 100644 azurerm/resource_arm_express_route_circuit_peering_test.go create mode 100644 website/docs/r/express_route_circuit_peering.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index 1e031c8600d2..e474549f4525 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -146,6 +146,7 @@ type ArmClient struct { applicationSecurityGroupsClient network.ApplicationSecurityGroupsClient expressRouteAuthsClient network.ExpressRouteCircuitAuthorizationsClient expressRouteCircuitClient network.ExpressRouteCircuitsClient + expressRoutePeeringsClient network.ExpressRouteCircuitPeeringsClient ifaceClient network.InterfacesClient loadBalancerClient network.LoadBalancersClient localNetConnClient network.LocalNetworkGatewaysClient @@ -698,6 +699,10 @@ func (c *ArmClient) registerNetworkingClients(endpoint, subscriptionId string, a c.configureClient(&expressRouteCircuitsClient.Client, auth) c.expressRouteCircuitClient = expressRouteCircuitsClient + expressRoutePeeringsClient := network.NewExpressRouteCircuitPeeringsClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&expressRoutePeeringsClient.Client, auth) + c.expressRoutePeeringsClient = expressRoutePeeringsClient + interfacesClient := network.NewInterfacesClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&interfacesClient.Client, auth) c.ifaceClient = interfacesClient diff --git a/azurerm/import_arm_express_route_circuit_peering_test.go b/azurerm/import_arm_express_route_circuit_peering_test.go new file mode 100644 index 000000000000..deee0a4f4338 --- /dev/null +++ b/azurerm/import_arm_express_route_circuit_peering_test.go @@ -0,0 +1,76 @@ +package azurerm + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccAzureRMExpressRouteCircuitPeering_importAzurePrivatePeering(t *testing.T) { + rInt := acctest.RandInt() + location := testLocation() + resourceName := "azurerm_express_route_circuit_peering.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(rInt, location), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"shared_key"}, + }, + }, + }) +} + +func TestAccAzureRMExpressRouteCircuitPeering_importAzurePublicPeering(t *testing.T) { + rInt := acctest.RandInt() + location := testLocation() + resourceName := "azurerm_express_route_circuit_peering.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(rInt, location), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"shared_key"}, + }, + }, + }) +} + +func TestAccAzureRMExpressRouteCircuitPeering_importMicrosoftPeering(t *testing.T) { + rInt := acctest.RandInt() + location := testLocation() + resourceName := "azurerm_express_route_circuit_peering.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMExpressRouteCircuitPeering_microsoftPeering(rInt, location), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 37dee6f77074..a866f0c05dc9 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -140,6 +140,7 @@ func Provider() terraform.ResourceProvider { "azurerm_eventhub_namespace": resourceArmEventHubNamespace(), "azurerm_express_route_circuit": resourceArmExpressRouteCircuit(), "azurerm_express_route_circuit_authorization": resourceArmExpressRouteCircuitAuthorization(), + "azurerm_express_route_circuit_peering": resourceArmExpressRouteCircuitPeering(), "azurerm_function_app": resourceArmFunctionApp(), "azurerm_image": resourceArmImage(), "azurerm_iothub": resourceArmIotHub(), diff --git a/azurerm/resource_arm_express_route_circuit_peering.go b/azurerm/resource_arm_express_route_circuit_peering.go new file mode 100644 index 000000000000..b16c58d2df8d --- /dev/null +++ b/azurerm/resource_arm_express_route_circuit_peering.go @@ -0,0 +1,274 @@ +package azurerm + +import ( + "fmt" + "log" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2017-09-01/network" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmExpressRouteCircuitPeering() *schema.Resource { + return &schema.Resource{ + Create: resourceArmExpressRouteCircuitPeeringCreateUpdate, + Read: resourceArmExpressRouteCircuitPeeringRead, + Update: resourceArmExpressRouteCircuitPeeringCreateUpdate, + Delete: resourceArmExpressRouteCircuitPeeringDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "peering_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.AzurePrivatePeering), + string(network.AzurePublicPeering), + string(network.MicrosoftPeering), + }, false), + }, + + "express_route_circuit_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "primary_peer_address_prefix": { + Type: schema.TypeString, + Required: true, + }, + + "secondary_peer_address_prefix": { + Type: schema.TypeString, + Required: true, + }, + + "vlan_id": { + Type: schema.TypeInt, + Required: true, + }, + + "shared_key": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.StringLenBetween(1, 25), + }, + + "peer_asn": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + + "microsoft_peering_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "advertised_public_prefixes": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + + "azure_asn": { + Type: schema.TypeInt, + Computed: true, + }, + + "primary_azure_port": { + Type: schema.TypeString, + Computed: true, + }, + + "secondary_azure_port": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceArmExpressRouteCircuitPeeringCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).expressRoutePeeringsClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for Express Route Peering create/update.") + + peeringType := d.Get("peering_type").(string) + circuitName := d.Get("express_route_circuit_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + sharedKey := d.Get("shared_key").(string) + primaryPeerAddressPrefix := d.Get("primary_peer_address_prefix").(string) + secondaryPeerAddressPrefix := d.Get("secondary_peer_address_prefix").(string) + vlanId := d.Get("vlan_id").(int) + azureASN := d.Get("azure_asn").(int) + peerASN := d.Get("peer_asn").(int) + + parameters := network.ExpressRouteCircuitPeering{ + ExpressRouteCircuitPeeringPropertiesFormat: &network.ExpressRouteCircuitPeeringPropertiesFormat{ + PeeringType: network.ExpressRouteCircuitPeeringType(peeringType), + SharedKey: utils.String(sharedKey), + PrimaryPeerAddressPrefix: utils.String(primaryPeerAddressPrefix), + SecondaryPeerAddressPrefix: utils.String(secondaryPeerAddressPrefix), + AzureASN: utils.Int32(int32(azureASN)), + PeerASN: utils.Int32(int32(peerASN)), + VlanID: utils.Int32(int32(vlanId)), + }, + } + + if strings.EqualFold(peeringType, string(network.MicrosoftPeering)) { + peerings := d.Get("microsoft_peering_config").([]interface{}) + if len(peerings) == 0 { + return fmt.Errorf("`microsoft_peering_config` must be specified when `peering_type` is set to `MicrosoftPeering`") + } + + peeringConfig := expandExpressRouteCircuitPeeringMicrosoftConfig(peerings) + parameters.ExpressRouteCircuitPeeringPropertiesFormat.MicrosoftPeeringConfig = peeringConfig + } + + azureRMLockByName(circuitName, expressRouteCircuitResourceName) + defer azureRMUnlockByName(circuitName, expressRouteCircuitResourceName) + + future, err := client.CreateOrUpdate(ctx, resourceGroup, circuitName, peeringType, parameters) + if err != nil { + return err + } + + err = future.WaitForCompletion(ctx, client.Client) + if err != nil { + return err + } + + read, err := client.Get(ctx, resourceGroup, circuitName, peeringType) + if err != nil { + return err + } + + d.SetId(*read.ID) + + return resourceArmExpressRouteCircuitPeeringRead(d, meta) +} + +func resourceArmExpressRouteCircuitPeeringRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).expressRoutePeeringsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + circuitName := id.Path["expressRouteCircuits"] + peeringType := id.Path["peerings"] + + resp, err := client.Get(ctx, resourceGroup, circuitName, peeringType) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Express Route Circuit Peering %q (Circuit %q / Resource Group %q): %+v", peeringType, circuitName, resourceGroup, err) + } + + d.Set("peering_type", peeringType) + d.Set("express_route_circuit_name", circuitName) + d.Set("resource_group_name", resourceGroup) + + if props := resp.ExpressRouteCircuitPeeringPropertiesFormat; props != nil { + d.Set("azure_asn", props.AzureASN) + d.Set("peer_asn", props.PeerASN) + d.Set("primary_azure_port", props.PrimaryAzurePort) + d.Set("secondary_azure_port", props.SecondaryAzurePort) + d.Set("primary_peer_address_prefix", props.PrimaryPeerAddressPrefix) + d.Set("secondary_peer_address_prefix", props.SecondaryPeerAddressPrefix) + d.Set("vlan_id", props.VlanID) + + if msConfig := props.MicrosoftPeeringConfig; msConfig != nil { + config := flattenExpressRouteCircuitPeeringMicrosoftConfig(msConfig) + if err := d.Set("microsoft_peering_config", config); err != nil { + return fmt.Errorf("Error flattening `microsoft_peering_config`: %+v", err) + } + } + } + + return nil +} + +func resourceArmExpressRouteCircuitPeeringDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).expressRoutePeeringsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + circuitName := id.Path["expressRouteCircuits"] + peeringType := id.Path["peerings"] + + azureRMLockByName(circuitName, expressRouteCircuitResourceName) + defer azureRMUnlockByName(circuitName, expressRouteCircuitResourceName) + + future, err := client.Delete(ctx, resourceGroup, circuitName, peeringType) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error issuing delete request for Express Route Circuit Peering %q (Circuit %q / Resource Group %q): %+v", peeringType, circuitName, resourceGroup, err) + } + + err = future.WaitForCompletion(ctx, client.Client) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error waiting for Express Route Circuit Peering %q (Circuit %q / Resource Group %q) to be deleted: %+v", peeringType, circuitName, resourceGroup, err) + } + + return err +} + +func expandExpressRouteCircuitPeeringMicrosoftConfig(input []interface{}) *network.ExpressRouteCircuitPeeringConfig { + peering := input[0].(map[string]interface{}) + + prefixes := make([]string, 0) + inputPrefixes := peering["advertised_public_prefixes"].([]interface{}) + for _, v := range inputPrefixes { + prefixes = append(prefixes, v.(string)) + } + + return &network.ExpressRouteCircuitPeeringConfig{ + AdvertisedPublicPrefixes: &prefixes, + } +} + +func flattenExpressRouteCircuitPeeringMicrosoftConfig(input *network.ExpressRouteCircuitPeeringConfig) interface{} { + config := make(map[string]interface{}, 0) + + prefixes := make([]string, 0) + if ps := input.AdvertisedPublicPrefixes; ps != nil { + for _, p := range *ps { + prefixes = append(prefixes, p) + } + } + + config["advertised_public_prefixes"] = prefixes + + return []interface{}{config} +} diff --git a/azurerm/resource_arm_express_route_circuit_peering_test.go b/azurerm/resource_arm_express_route_circuit_peering_test.go new file mode 100644 index 000000000000..80d95064a434 --- /dev/null +++ b/azurerm/resource_arm_express_route_circuit_peering_test.go @@ -0,0 +1,255 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(t *testing.T) { + resourceName := "azurerm_express_route_circuit_peering.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "peering_type", "AzurePrivatePeering"), + resource.TestCheckResourceAttr(resourceName, "microsoft_peering_config.#", "0"), + ), + }, + }, + }) +} + +func TestAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(t *testing.T) { + resourceName := "azurerm_express_route_circuit_peering.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "peering_type", "AzurePublicPeering"), + resource.TestCheckResourceAttr(resourceName, "microsoft_peering_config.#", "0"), + ), + }, + }, + }) +} + +func TestAccAzureRMExpressRouteCircuitPeering_microsoftPeering(t *testing.T) { + resourceName := "azurerm_express_route_circuit_peering.test" + ri := acctest.RandInt() + location := testLocation() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMExpressRouteCircuitPeering_microsoftPeering(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "peering_type", "MicrosoftPeering"), + resource.TestCheckResourceAttr(resourceName, "microsoft_peering_config.#", "1"), + ), + }, + }, + }) +} + +func testCheckAzureRMExpressRouteCircuitPeeringExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + peeringType := rs.Primary.Attributes["peering_type"] + circuitName := rs.Primary.Attributes["express_route_circuit_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Express Route Circuit Peering: %s", peeringType) + } + + client := testAccProvider.Meta().(*ArmClient).expressRoutePeeringsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, circuitName, peeringType) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Express Route Circuit Peering %q (Circuit %q / Resource Group %q) does not exist", peeringType, circuitName, resourceGroup) + } + + return fmt.Errorf("Bad: Get on expressRoutePeeringsClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMExpressRouteCircuitPeeringDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).expressRoutePeeringsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_express_route_circuit_peering" { + continue + } + + peeringType := rs.Primary.Attributes["peering_type"] + circuitName := rs.Primary.Attributes["express_route_circuit_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, circuitName, peeringType) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Express Route Circuit Peering still exists:\n%#v", resp) + } + } + + return nil +} + +func testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_express_route_circuit" "test" { + name = "acctest-erc-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + service_provider_name = "Equinix" + peering_location = "Silicon Valley" + bandwidth_in_mbps = 50 + + sku { + tier = "Standard" + family = "MeteredData" + } + + tags { + Environment = "production" + Purpose = "AcceptanceTests" + } +} + +resource "azurerm_express_route_circuit_peering" "test" { + peering_type = "AzurePrivatePeering" + express_route_circuit_name = "${azurerm_express_route_circuit.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + shared_key = "ABCdefGHIJklm@nOPqrsTU!!" + peer_asn = 100 + primary_peer_address_prefix = "192.168.1.0/30" + secondary_peer_address_prefix = "192.168.2.0/30" + vlan_id = 100 +} +`, rInt, location, rInt) +} + +func testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_express_route_circuit" "test" { + name = "acctest-erc-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + service_provider_name = "Equinix" + peering_location = "Silicon Valley" + bandwidth_in_mbps = 50 + + sku { + tier = "Standard" + family = "MeteredData" + } + + tags { + Environment = "production" + Purpose = "AcceptanceTests" + } +} + +resource "azurerm_express_route_circuit_peering" "test" { + peering_type = "AzurePublicPeering" + express_route_circuit_name = "${azurerm_express_route_circuit.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + shared_key = "ABCdefGHIJklm@nOPqrsTU!!" + peer_asn = 100 + primary_peer_address_prefix = "123.0.0.0/30" + secondary_peer_address_prefix = "123.0.0.4/30" + vlan_id = 300 +} +`, rInt, location, rInt) +} + +func testAccAzureRMExpressRouteCircuitPeering_microsoftPeering(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-%d" + location = "%s" +} + +resource "azurerm_express_route_circuit" "test" { + name = "acctest-erc-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + service_provider_name = "Equinix" + peering_location = "Silicon Valley" + bandwidth_in_mbps = 50 + + sku { + tier = "Premium" + family = "MeteredData" + } + + tags { + Environment = "production" + Purpose = "AcceptanceTests" + } +} + +resource "azurerm_express_route_circuit_peering" "test" { + peering_type = "MicrosoftPeering" + express_route_circuit_name = "${azurerm_express_route_circuit.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + peer_asn = 100 + primary_peer_address_prefix = "192.168.1.0/30" + secondary_peer_address_prefix = "192.168.2.0/30" + vlan_id = 300 + + microsoft_peering_config { + advertised_public_prefixes = ["123.1.0.0/24"] + } +} +`, rInt, location, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index 0139ea74338a..e30c6a28d0b5 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -540,6 +540,10 @@ azurerm_express_route_circuit_authorization + > + azurerm_express_route_circuit_peering + + > azurerm_local_network_gateway diff --git a/website/docs/r/express_route_circuit_peering.html.markdown b/website/docs/r/express_route_circuit_peering.html.markdown new file mode 100644 index 000000000000..e975c90b518d --- /dev/null +++ b/website/docs/r/express_route_circuit_peering.html.markdown @@ -0,0 +1,94 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_express_route_circuit_peering" +sidebar_current: "docs-azurerm-resource-network-express-route-circuit-peering" +description: |- + Creates an ExpressRoute Circuit Peering. +--- + +# azurerm_express_route_circuit_peering + +Creates an ExpressRoute Circuit Peering. + +## Example Usage (Creating a Microsoft Peering) + +```hcl +resource "azurerm_resource_group" "test" { + name = "exprtTest" + location = "West US" +} + +resource "azurerm_express_route_circuit" "test" { + name = "expressRoute1" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + service_provider_name = "Equinix" + peering_location = "Silicon Valley" + bandwidth_in_mbps = 50 + sku { + tier = "Standard" + family = "MeteredData" + } + allow_classic_operations = false + + tags { + environment = "Production" + } +} + +resource "azurerm_express_route_circuit_peering" "test" { + peering_type = "MicrosoftPeering" + express_route_circuit_name = "${azurerm_express_route_circuit.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + peer_asn = 100 + primary_peer_address_prefix = "123.0.0.0/30" + secondary_peer_address_prefix = "123.0.0.4/30" + vlan_id = 300 + + microsoft_peering_config { + advertised_public_prefixes = ["123.1.0.0/24"] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `peering_type` - (Required) The type of the ExpressRoute Circuit Peering. Acceptable values include `AzurePrivatePeering`, `AzurePublicPeering` and `MicrosoftPeering`. Changing this forces a new resource to be created. + +~> **NOTE:** only one Peering of each Type can be created. Attempting to create multiple peerings of the same type will overwrite the original peering. + +* `express_route_circuit_name` - (Required) The name of the ExpressRoute Circuit in which to create the Peering. + +* `resource_group_name` - (Required) The name of the resource group in which to + create the Express Route Circuit Peering. Changing this forces a new resource to be created. + +* `primary_peer_address_prefix` - (Optional) A `/30` subnet for the primary link. +* `secondary_peer_address_prefix` - (Optional) A `/30` subnet for the secondary link. +* `vlan_id` - (Optional) A valid VLAN ID to establish this peering on. +* `shared_key` - (Optional) The shared key. Can be a maximum of 25 characters. +* `peer_asn` - (Optional) The Either a 16-bit or a 32-bit ASN. Can either be public or private.. +* `microsoft_peering_config` - (Optional) A `microsoft_peering_config` block as defined below. Required when `peering_type` is set to `MicrosoftPeering`. + +--- + +A `microsoft_peering_config` block contains: + +* `advertised_public_prefixes` - (Required) A list of Advertised Public Prefixes + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Resource ID of the ExpressRoute Circuit Peering. + +* `azure_asn` - (Optional) The ASN used by Azure. + +## Import + +ExpressRoute Circuit Peerings can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_express_route_circuit_peering.peering1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/expressRouteCircuits/myExpressRoute/peerings/peering1 +``` From 6c9dc78f4a321992d2bf01abe002af0793e53dbc Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 27 Mar 2018 11:07:57 -0400 Subject: [PATCH 2/5] Fixing some C&P fails --- website/docs/r/express_route_circuit.html.markdown | 4 ++-- .../docs/r/express_route_circuit_authorization.html.markdown | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/r/express_route_circuit.html.markdown b/website/docs/r/express_route_circuit.html.markdown index 630d78ca9dea..39aa87524e32 100644 --- a/website/docs/r/express_route_circuit.html.markdown +++ b/website/docs/r/express_route_circuit.html.markdown @@ -42,7 +42,7 @@ The following arguments are supported: * `name` - (Required) The name of the ExpressRoute circuit. Changing this forces a new resource to be created. -* `resource_group_name` - (Required) The name of the resource group in which to create the namespace. Changing this forces a new resource to be created. +* `resource_group_name` - (Required) The name of the resource group in which to create the ExpressRoute circuit. Changing this forces a new resource to be created. * `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. @@ -64,7 +64,7 @@ The following arguments are supported: `sku` supports the following: -* `tier` - (Required) The service tier. Possible values are `Standard` or `Premium``. +* `tier` - (Required) The service tier. Possible values are `Standard` or `Premium`. * `family` - (Required) The billing mode for bandwidth. Possible values are `MeteredData` or `UnlimitedData`. diff --git a/website/docs/r/express_route_circuit_authorization.html.markdown b/website/docs/r/express_route_circuit_authorization.html.markdown index 2047cdfb7b4e..baa13d0ae666 100644 --- a/website/docs/r/express_route_circuit_authorization.html.markdown +++ b/website/docs/r/express_route_circuit_authorization.html.markdown @@ -51,7 +51,7 @@ The following arguments are supported: new resource to be created. * `resource_group_name` - (Required) The name of the resource group in which to - create the namespace. Changing this forces a new resource to be created. + create the ExpressRoute circuit. Changing this forces a new resource to be created. * `express_route_circuit_name` - (Required) The name of the Express Route Circuit in which to create the Authorization. From 86ae2d927016205381580dbd9b68ae21c0aa34b1 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 27 Mar 2018 11:09:33 -0400 Subject: [PATCH 3/5] Documenting two missing fields --- website/docs/r/express_route_circuit_peering.html.markdown | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/docs/r/express_route_circuit_peering.html.markdown b/website/docs/r/express_route_circuit_peering.html.markdown index e975c90b518d..26aced162fe9 100644 --- a/website/docs/r/express_route_circuit_peering.html.markdown +++ b/website/docs/r/express_route_circuit_peering.html.markdown @@ -83,7 +83,11 @@ The following attributes are exported: * `id` - The Resource ID of the ExpressRoute Circuit Peering. -* `azure_asn` - (Optional) The ASN used by Azure. +* `azure_asn` - The ASN used by Azure. + +* `primary_azure_port` - The Primary Port used by Azure for this Peering. + +* `secondary_azure_port` - The Secondary Port used by Azure for this Peering. ## Import From 24eb2aacd0a870584ea1e304bcecb4bfe8c57804 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 27 Mar 2018 11:53:30 -0400 Subject: [PATCH 4/5] Adding a single test function to work around provisioning requirements --- ..._arm_express_route_circuit_peering_test.go | 12 ++--- ..._arm_express_route_circuit_peering_test.go | 50 +++++++++++++++---- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/azurerm/import_arm_express_route_circuit_peering_test.go b/azurerm/import_arm_express_route_circuit_peering_test.go index deee0a4f4338..f7f682c71caa 100644 --- a/azurerm/import_arm_express_route_circuit_peering_test.go +++ b/azurerm/import_arm_express_route_circuit_peering_test.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform/helper/resource" ) -func TestAccAzureRMExpressRouteCircuitPeering_importAzurePrivatePeering(t *testing.T) { +func testAccAzureRMExpressRouteCircuitPeering_importAzurePrivatePeering(t *testing.T) { rInt := acctest.RandInt() location := testLocation() resourceName := "azurerm_express_route_circuit_peering.test" @@ -18,7 +18,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_importAzurePrivatePeering(t *testi CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(rInt, location), + Config: testAccAzureRMExpressRouteCircuitPeering_privatePeering(rInt, location), }, { ResourceName: resourceName, @@ -30,7 +30,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_importAzurePrivatePeering(t *testi }) } -func TestAccAzureRMExpressRouteCircuitPeering_importAzurePublicPeering(t *testing.T) { +func testAccAzureRMExpressRouteCircuitPeering_importAzurePublicPeering(t *testing.T) { rInt := acctest.RandInt() location := testLocation() resourceName := "azurerm_express_route_circuit_peering.test" @@ -41,7 +41,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_importAzurePublicPeering(t *testin CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(rInt, location), + Config: testAccAzureRMExpressRouteCircuitPeering_publicPeering(rInt, location), }, { ResourceName: resourceName, @@ -53,7 +53,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_importAzurePublicPeering(t *testin }) } -func TestAccAzureRMExpressRouteCircuitPeering_importMicrosoftPeering(t *testing.T) { +func testAccAzureRMExpressRouteCircuitPeering_importMicrosoftPeering(t *testing.T) { rInt := acctest.RandInt() location := testLocation() resourceName := "azurerm_express_route_circuit_peering.test" @@ -64,7 +64,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_importMicrosoftPeering(t *testing. CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMExpressRouteCircuitPeering_microsoftPeering(rInt, location), + Config: testAccAzureRMExpressRouteCircuitPeering_msPeering(rInt, location), }, { ResourceName: resourceName, diff --git a/azurerm/resource_arm_express_route_circuit_peering_test.go b/azurerm/resource_arm_express_route_circuit_peering_test.go index 80d95064a434..263ef8225f0a 100644 --- a/azurerm/resource_arm_express_route_circuit_peering_test.go +++ b/azurerm/resource_arm_express_route_circuit_peering_test.go @@ -11,7 +11,39 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func TestAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(t *testing.T) { +func TestAccAzureRMExpressRouteCircuitPeering(t *testing.T) { + // NOTE: this is a combined test rather than separate split out tests due to + // Azure only being happy about provisioning one at once + // (which our test suite can't easily workaround) + testCases := map[string]map[string]func(t *testing.T){ + "PrivatePeering": { + "azurePrivatePeering": testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering, + "importPrivatePeering": testAccAzureRMExpressRouteCircuitPeering_importAzurePrivatePeering, + }, + "PublicPeering": { + "azurePublicPeering": testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering, + "importPublicPeering": testAccAzureRMExpressRouteCircuitPeering_importAzurePublicPeering, + }, + "MicrosoftPeering": { + "microsoftPeering": testAccAzureRMExpressRouteCircuitPeering_microsoftPeering, + "importMicrosoftPeering": testAccAzureRMExpressRouteCircuitPeering_importMicrosoftPeering, + }, + } + + for group, m := range testCases { + m := m + t.Run(group, func(t *testing.T) { + for name, tc := range m { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } + }) + } +} + +func testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(t *testing.T) { resourceName := "azurerm_express_route_circuit_peering.test" ri := acctest.RandInt() location := testLocation() @@ -22,7 +54,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(t *testing.T) CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(ri, location), + Config: testAccAzureRMExpressRouteCircuitPeering_privatePeering(ri, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName), resource.TestCheckResourceAttr(resourceName, "peering_type", "AzurePrivatePeering"), @@ -33,7 +65,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(t *testing.T) }) } -func TestAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(t *testing.T) { +func testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(t *testing.T) { resourceName := "azurerm_express_route_circuit_peering.test" ri := acctest.RandInt() location := testLocation() @@ -44,7 +76,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(t *testing.T) { CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(ri, location), + Config: testAccAzureRMExpressRouteCircuitPeering_publicPeering(ri, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName), resource.TestCheckResourceAttr(resourceName, "peering_type", "AzurePublicPeering"), @@ -55,7 +87,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(t *testing.T) { }) } -func TestAccAzureRMExpressRouteCircuitPeering_microsoftPeering(t *testing.T) { +func testAccAzureRMExpressRouteCircuitPeering_microsoftPeering(t *testing.T) { resourceName := "azurerm_express_route_circuit_peering.test" ri := acctest.RandInt() location := testLocation() @@ -66,7 +98,7 @@ func TestAccAzureRMExpressRouteCircuitPeering_microsoftPeering(t *testing.T) { CheckDestroy: testCheckAzureRMExpressRouteCircuitPeeringDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMExpressRouteCircuitPeering_microsoftPeering(ri, location), + Config: testAccAzureRMExpressRouteCircuitPeering_msPeering(ri, location), Check: resource.ComposeTestCheckFunc( testCheckAzureRMExpressRouteCircuitPeeringExists(resourceName), resource.TestCheckResourceAttr(resourceName, "peering_type", "MicrosoftPeering"), @@ -134,7 +166,7 @@ func testCheckAzureRMExpressRouteCircuitPeeringDestroy(s *terraform.State) error return nil } -func testAccAzureRMExpressRouteCircuitPeering_azurePrivatePeering(rInt int, location string) string { +func testAccAzureRMExpressRouteCircuitPeering_privatePeering(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestrg-%d" @@ -173,7 +205,7 @@ resource "azurerm_express_route_circuit_peering" "test" { `, rInt, location, rInt) } -func testAccAzureRMExpressRouteCircuitPeering_azurePublicPeering(rInt int, location string) string { +func testAccAzureRMExpressRouteCircuitPeering_publicPeering(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestrg-%d" @@ -212,7 +244,7 @@ resource "azurerm_express_route_circuit_peering" "test" { `, rInt, location, rInt) } -func testAccAzureRMExpressRouteCircuitPeering_microsoftPeering(rInt int, location string) string { +func testAccAzureRMExpressRouteCircuitPeering_msPeering(rInt int, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { name = "acctestrg-%d" From 28779ea3acd3bc9e422faab43c90d0914aa57589 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Tue, 27 Mar 2018 17:54:32 -0400 Subject: [PATCH 5/5] Ensuring we always set the `microsoft_peering_config` field --- .../resource_arm_express_route_circuit_peering.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/azurerm/resource_arm_express_route_circuit_peering.go b/azurerm/resource_arm_express_route_circuit_peering.go index b16c58d2df8d..8ea9fc051a4a 100644 --- a/azurerm/resource_arm_express_route_circuit_peering.go +++ b/azurerm/resource_arm_express_route_circuit_peering.go @@ -198,11 +198,9 @@ func resourceArmExpressRouteCircuitPeeringRead(d *schema.ResourceData, meta inte d.Set("secondary_peer_address_prefix", props.SecondaryPeerAddressPrefix) d.Set("vlan_id", props.VlanID) - if msConfig := props.MicrosoftPeeringConfig; msConfig != nil { - config := flattenExpressRouteCircuitPeeringMicrosoftConfig(msConfig) - if err := d.Set("microsoft_peering_config", config); err != nil { - return fmt.Errorf("Error flattening `microsoft_peering_config`: %+v", err) - } + config := flattenExpressRouteCircuitPeeringMicrosoftConfig(props.MicrosoftPeeringConfig) + if err := d.Set("microsoft_peering_config", config); err != nil { + return fmt.Errorf("Error flattening `microsoft_peering_config`: %+v", err) } } @@ -259,8 +257,11 @@ func expandExpressRouteCircuitPeeringMicrosoftConfig(input []interface{}) *netwo } func flattenExpressRouteCircuitPeeringMicrosoftConfig(input *network.ExpressRouteCircuitPeeringConfig) interface{} { - config := make(map[string]interface{}, 0) + if input == nil { + return []interface{}{} + } + config := make(map[string]interface{}, 0) prefixes := make([]string, 0) if ps := input.AdvertisedPublicPrefixes; ps != nil { for _, p := range *ps {