[MS] provider/azurerm: New resource - Express Route Circuit (#14265)
* Adds ExpressRoute circuit documentation * Adds tests and doc improvements * Code for basic Express Route Circuit support * Use the built-in validation helper * Added ignoreCaseDiffSuppressFunc to a few fields * Added more information to docs * Touchup * Moving SKU properties into a set. * Updates doc * A bit more tweaks * Switch to Sprintf for test string * Updating the acceptance test name for consistency
This commit is contained in:
parent
11e386993b
commit
6ca50dd81d
|
@ -53,6 +53,7 @@ type ArmClient struct {
|
|||
|
||||
appGatewayClient network.ApplicationGatewaysClient
|
||||
ifaceClient network.InterfacesClient
|
||||
expressRouteCircuitClient network.ExpressRouteCircuitsClient
|
||||
loadBalancerClient network.LoadBalancersClient
|
||||
localNetConnClient network.LocalNetworkGatewaysClient
|
||||
publicIPClient network.PublicIPAddressesClient
|
||||
|
@ -281,6 +282,12 @@ func (c *Config) getArmClient() (*ArmClient, error) {
|
|||
ifc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.ifaceClient = ifc
|
||||
|
||||
erc := network.NewExpressRouteCircuitsClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&erc.Client)
|
||||
erc.Authorizer = spt
|
||||
erc.Sender = autorest.CreateSender(withRequestLogging())
|
||||
client.expressRouteCircuitClient = erc
|
||||
|
||||
lbc := network.NewLoadBalancersClientWithBaseURI(endpoint, c.SubscriptionID)
|
||||
setUserAgent(&lbc.Client)
|
||||
lbc.Authorizer = spt
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package azurerm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/network"
|
||||
"github.com/hashicorp/errwrap"
|
||||
)
|
||||
|
||||
func extractResourceGroupAndErcName(resourceId string) (resourceGroup string, name string, err error) {
|
||||
id, err := parseAzureResourceID(resourceId)
|
||||
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
resourceGroup = id.ResourceGroup
|
||||
name = id.Path["expressRouteCircuits"]
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func retrieveErcByResourceId(resourceId string, meta interface{}) (erc *network.ExpressRouteCircuit, resourceGroup string, e error) {
|
||||
ercClient := meta.(*ArmClient).expressRouteCircuitClient
|
||||
|
||||
resGroup, name, err := extractResourceGroupAndErcName(resourceId)
|
||||
if err != nil {
|
||||
return nil, "", errwrap.Wrapf("Error Parsing Azure Resource ID - {{err}}", err)
|
||||
}
|
||||
|
||||
resp, err := ercClient.Get(resGroup, name)
|
||||
if err != nil {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, "", nil
|
||||
}
|
||||
return nil, "", errwrap.Wrapf(fmt.Sprintf("Error making Read request on Express Route Circuit %s: {{err}}", name), err)
|
||||
}
|
||||
|
||||
return &resp, resGroup, nil
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package azurerm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAzureRMExpressRouteCircuit_importBasic(t *testing.T) {
|
||||
resourceName := "azurerm_express_route_circuit.test"
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testCheckAzureRMExpressRouteCircuitDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAzureRMExpressRouteCircuit_basic(acctest.RandInt()),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -78,6 +78,8 @@ func Provider() terraform.ResourceProvider {
|
|||
"azurerm_eventhub_consumer_group": resourceArmEventHubConsumerGroup(),
|
||||
"azurerm_eventhub_namespace": resourceArmEventHubNamespace(),
|
||||
|
||||
"azurerm_express_route_circuit": resourceArmExpressRouteCircuit(),
|
||||
|
||||
"azurerm_lb": resourceArmLoadBalancer(),
|
||||
"azurerm_lb_backend_address_pool": resourceArmLoadBalancerBackendAddressPool(),
|
||||
"azurerm_lb_nat_rule": resourceArmLoadBalancerNatRule(),
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
package azurerm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/network"
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/helper/validation"
|
||||
)
|
||||
|
||||
func resourceArmExpressRouteCircuit() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceArmExpressRouteCircuitCreateOrUpdate,
|
||||
Read: resourceArmExpressRouteCircuitRead,
|
||||
Update: resourceArmExpressRouteCircuitCreateOrUpdate,
|
||||
Delete: resourceArmExpressRouteCircuitDelete,
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"resource_group_name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"location": locationSchema(),
|
||||
|
||||
"service_provider_name": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
|
||||
},
|
||||
|
||||
"peering_location": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
|
||||
},
|
||||
|
||||
"bandwidth_in_mbps": {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"sku": {
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
MinItems: 1,
|
||||
MaxItems: 1,
|
||||
Elem: &schema.Resource{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"tier": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringInSlice([]string{
|
||||
string(network.ExpressRouteCircuitSkuTierStandard),
|
||||
string(network.ExpressRouteCircuitSkuTierPremium),
|
||||
}, true),
|
||||
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
|
||||
},
|
||||
|
||||
"family": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ValidateFunc: validation.StringInSlice([]string{
|
||||
string(network.MeteredData),
|
||||
string(network.UnlimitedData),
|
||||
}, true),
|
||||
DiffSuppressFunc: ignoreCaseDiffSuppressFunc,
|
||||
},
|
||||
},
|
||||
},
|
||||
Set: resourceArmExpressRouteCircuitSkuHash,
|
||||
},
|
||||
|
||||
"allow_classic_operations": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
|
||||
"service_provider_provisioning_state": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"service_key": {
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"tags": tagsSchema(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceArmExpressRouteCircuitCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*ArmClient)
|
||||
ercClient := client.expressRouteCircuitClient
|
||||
|
||||
log.Printf("[INFO] preparing arguments for Azure ARM ExpressRouteCircuit creation.")
|
||||
|
||||
name := d.Get("name").(string)
|
||||
resGroup := d.Get("resource_group_name").(string)
|
||||
location := d.Get("location").(string)
|
||||
serviceProviderName := d.Get("service_provider_name").(string)
|
||||
peeringLocation := d.Get("peering_location").(string)
|
||||
bandwidthInMbps := int32(d.Get("bandwidth_in_mbps").(int))
|
||||
sku := expandExpressRouteCircuitSku(d)
|
||||
allowRdfeOps := d.Get("allow_classic_operations").(bool)
|
||||
tags := d.Get("tags").(map[string]interface{})
|
||||
expandedTags := expandTags(tags)
|
||||
|
||||
erc := network.ExpressRouteCircuit{
|
||||
Name: &name,
|
||||
Location: &location,
|
||||
Sku: sku,
|
||||
ExpressRouteCircuitPropertiesFormat: &network.ExpressRouteCircuitPropertiesFormat{
|
||||
AllowClassicOperations: &allowRdfeOps,
|
||||
ServiceProviderProperties: &network.ExpressRouteCircuitServiceProviderProperties{
|
||||
ServiceProviderName: &serviceProviderName,
|
||||
PeeringLocation: &peeringLocation,
|
||||
BandwidthInMbps: &bandwidthInMbps,
|
||||
},
|
||||
},
|
||||
Tags: expandedTags,
|
||||
}
|
||||
|
||||
_, err := ercClient.CreateOrUpdate(resGroup, name, erc, make(chan struct{}))
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error Creating/Updating ExpressRouteCircuit {{err}}", err)
|
||||
}
|
||||
|
||||
read, err := ercClient.Get(resGroup, name)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error Getting ExpressRouteCircuit {{err}}", err)
|
||||
}
|
||||
if read.ID == nil {
|
||||
return fmt.Errorf("Cannot read ExpressRouteCircuit %s (resource group %s) ID", name, resGroup)
|
||||
}
|
||||
|
||||
d.SetId(*read.ID)
|
||||
|
||||
return resourceArmExpressRouteCircuitRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceArmExpressRouteCircuitRead(d *schema.ResourceData, meta interface{}) error {
|
||||
erc, resGroup, err := retrieveErcByResourceId(d.Id(), meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if erc == nil {
|
||||
d.SetId("")
|
||||
log.Printf("[INFO] Express Route Circuit %q not found. Removing from state", d.Get("name").(string))
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Set("name", erc.Name)
|
||||
d.Set("resource_group_name", resGroup)
|
||||
d.Set("location", erc.Location)
|
||||
|
||||
if erc.ServiceProviderProperties != nil {
|
||||
d.Set("service_provider_name", erc.ServiceProviderProperties.ServiceProviderName)
|
||||
d.Set("peering_location", erc.ServiceProviderProperties.PeeringLocation)
|
||||
d.Set("bandwidth_in_mbps", erc.ServiceProviderProperties.BandwidthInMbps)
|
||||
}
|
||||
|
||||
if erc.Sku != nil {
|
||||
d.Set("sku", schema.NewSet(resourceArmExpressRouteCircuitSkuHash, flattenExpressRouteCircuitSku(erc.Sku)))
|
||||
}
|
||||
|
||||
d.Set("service_provider_provisioning_state", string(erc.ServiceProviderProvisioningState))
|
||||
d.Set("service_key", erc.ServiceKey)
|
||||
d.Set("allow_classic_operations", erc.AllowClassicOperations)
|
||||
|
||||
flattenAndSetTags(d, erc.Tags)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceArmExpressRouteCircuitDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
ercClient := meta.(*ArmClient).expressRouteCircuitClient
|
||||
|
||||
resGroup, name, err := extractResourceGroupAndErcName(d.Id())
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("Error Parsing Azure Resource ID {{err}}", err)
|
||||
}
|
||||
|
||||
_, err = ercClient.Delete(resGroup, name, make(chan struct{}))
|
||||
return err
|
||||
}
|
||||
|
||||
func expandExpressRouteCircuitSku(d *schema.ResourceData) *network.ExpressRouteCircuitSku {
|
||||
skuSettings := d.Get("sku").(*schema.Set)
|
||||
v := skuSettings.List()[0].(map[string]interface{}) // [0] is guarded by MinItems in schema.
|
||||
tier := v["tier"].(string)
|
||||
family := v["family"].(string)
|
||||
name := fmt.Sprintf("%s_%s", tier, family)
|
||||
|
||||
return &network.ExpressRouteCircuitSku{
|
||||
Name: &name,
|
||||
Tier: network.ExpressRouteCircuitSkuTier(tier),
|
||||
Family: network.ExpressRouteCircuitSkuFamily(family),
|
||||
}
|
||||
}
|
||||
|
||||
func flattenExpressRouteCircuitSku(sku *network.ExpressRouteCircuitSku) []interface{} {
|
||||
return []interface{}{
|
||||
map[string]interface{}{
|
||||
"tier": string(sku.Tier),
|
||||
"family": string(sku.Family),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceArmExpressRouteCircuitSkuHash(v interface{}) int {
|
||||
var buf bytes.Buffer
|
||||
m := v.(map[string]interface{})
|
||||
buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["tier"].(string))))
|
||||
buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["family"].(string))))
|
||||
|
||||
return hashcode.String(buf.String())
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package azurerm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/arm/network"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAzureRMExpressRouteCircuit_basic(t *testing.T) {
|
||||
var erc network.ExpressRouteCircuit
|
||||
ri := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testCheckAzureRMExpressRouteCircuitDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccAzureRMExpressRouteCircuit_basic(ri),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testCheckAzureRMExpressRouteCircuitExists("azurerm_express_route_circuit.test", &erc),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testCheckAzureRMExpressRouteCircuitExists(name string, erc *network.ExpressRouteCircuit) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", name)
|
||||
}
|
||||
|
||||
expressRouteCircuitName := rs.Primary.Attributes["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: %s", expressRouteCircuitName)
|
||||
}
|
||||
|
||||
conn := testAccProvider.Meta().(*ArmClient).expressRouteCircuitClient
|
||||
|
||||
resp, err := conn.Get(resourceGroup, expressRouteCircuitName)
|
||||
if err != nil {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return fmt.Errorf("Bad: Express Route Circuit %q (resource group: %q) does not exist", expressRouteCircuitName, resourceGroup)
|
||||
}
|
||||
|
||||
return fmt.Errorf("Bad: Get on expressRouteCircuitClient: %s", err)
|
||||
}
|
||||
|
||||
*erc = resp
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testCheckAzureRMExpressRouteCircuitDestroy(s *terraform.State) error {
|
||||
conn := testAccProvider.Meta().(*ArmClient).expressRouteCircuitClient
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "azurerm_express_route_circuit" {
|
||||
continue
|
||||
}
|
||||
|
||||
name := rs.Primary.Attributes["name"]
|
||||
resourceGroup := rs.Primary.Attributes["resource_group_name"]
|
||||
|
||||
resp, err := conn.Get(resourceGroup, name)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
return fmt.Errorf("Express Route Circuit still exists:\n%#v", resp.ExpressRouteCircuitPropertiesFormat)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccAzureRMExpressRouteCircuit_basic(rInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "azurerm_resource_group" "test" {
|
||||
name = "acctestrg-%d"
|
||||
location = "West US"
|
||||
}
|
||||
|
||||
resource "azurerm_express_route_circuit" "test" {
|
||||
name = "acctest-erc-%[1]d"
|
||||
location = "West US"
|
||||
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"
|
||||
}
|
||||
allow_classic_operations = false
|
||||
|
||||
tags {
|
||||
Environment = "production"
|
||||
Purpose = "AcceptanceTests"
|
||||
}
|
||||
}`, rInt)
|
||||
}
|
|
@ -111,6 +111,7 @@ To make a resource importable, please see the
|
|||
### Azure (Resource Manager)
|
||||
|
||||
* azurerm_availability_set
|
||||
* azurerm_express_route_circuit
|
||||
* azurerm_dns_zone
|
||||
* azurerm_local_network_gateway
|
||||
* azurerm_network_security_group
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
layout: "azurerm"
|
||||
page_title: "Azure Resource Manager: azurerm_express_route_circuit"
|
||||
sidebar_current: "docs-azurerm-resource-express-route-circuit"
|
||||
description: |-
|
||||
Creates an ExpressRoute circuit.
|
||||
---
|
||||
|
||||
# azurerm\_express\_route\_circuit
|
||||
|
||||
Creates an ExpressRoute circuit.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```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 = "West US"
|
||||
service_provider_name = "Equinix"
|
||||
peering_location = "Silicon Valley"
|
||||
bandwidth_in_mbps = 50
|
||||
sku {
|
||||
tier = "Standard"
|
||||
family = "MeteredData"
|
||||
}
|
||||
allow_classic_operations = false
|
||||
|
||||
tags {
|
||||
environment = "Production"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
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.
|
||||
|
||||
* `location` - (Required) Specifies the supported Azure location where the resource exists.
|
||||
Changing this forces a new resource to be created.
|
||||
|
||||
* `service_provider_name` - (Required) The name of the ExpressRoute Service Provider.
|
||||
|
||||
* `peering_location` - (Required) The name of the peering location and not the ARM resource location.
|
||||
|
||||
* `bandwidth_in_mbps` - (Required) The bandwidth in Mbps of the circuit being created. Once you increase your bandwidth,
|
||||
you will not be able to decrease it to its previous value.
|
||||
|
||||
* `sku` - (Required) Chosen SKU of ExpressRoute circuit as documented below.
|
||||
|
||||
* `allow_classic_operations` - (Optional) Allow the circuit to interact with classic (RDFE) resources.
|
||||
The default value is false.
|
||||
|
||||
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
||||
|
||||
`sku` supports the following:
|
||||
|
||||
* `tier` - (Required) The service tier. Value must be either "Premium" or "Standard".
|
||||
|
||||
* `family` - (Required) The billing mode. Value must be either "MeteredData" or "UnlimitedData".
|
||||
Once you set the billing model to "UnlimitedData", you will not be able to switch to "MeteredData".
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `id` - The Resource ID of the ExpressRoute circuit.
|
||||
* `service_provider_provisioning_state` - The ExpressRoute circuit provisioning state from your chosen service provider.
|
||||
Possible values are "NotProvisioned", "Provisioning", "Provisioned", and "Deprovisioning".
|
||||
* `service_key` - The string needed by the service provider to provision the ExpressRoute circuit.
|
||||
|
||||
## Import
|
||||
|
||||
ExpressRoute circuits can be imported using the `resource id`, e.g.
|
||||
|
||||
```
|
||||
terraform import azurerm_express_route_circuit.myExpressRoute /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Network/expressRouteCircuits/myExpressRoute
|
||||
```
|
|
@ -218,6 +218,9 @@
|
|||
<a href="/docs/providers/azurerm/r/traffic_manager_endpoint.html">azurerm_traffic_manager_endpoint</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-azurerm-resource-express-route-circuit") %>>
|
||||
<a href="/docs/providers/azurerm/r/express_route_circuit.html">azurerm_express_route_circuit</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
|
Loading…
Reference in New Issue