provider/azurerm: Clean up work for base provider
- Add documentation for resources - Rename files to match standard patterns - Add acceptance tests for resource groups - Add acceptance tests for vnets - Remove ARM_CREDENTIALS file - as discussed this does not appear to be an Azure standard, and there is scope for confusion with the azureProfile.json file which the CLI generates. If a standard emerges we can reconsider this. - Validate credentials in the schema - Remove storage testing artefacts - Use ARM IDs as Terraform IDs - Use autorest hooks for logging
This commit is contained in:
parent
63bc8e9852
commit
805c4896bd
|
@ -1 +0,0 @@
|
||||||
package main
|
|
|
@ -1,8 +1,8 @@
|
||||||
package azurerm
|
package azurerm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"log"
|
||||||
"fmt"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Azure/azure-sdk-for-go/Godeps/_workspace/src/github.com/Azure/go-autorest/autorest/azure"
|
"github.com/Azure/azure-sdk-for-go/Godeps/_workspace/src/github.com/Azure/go-autorest/autorest/azure"
|
||||||
"github.com/Azure/azure-sdk-for-go/arm/compute"
|
"github.com/Azure/azure-sdk-for-go/arm/compute"
|
||||||
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/Azure/azure-sdk-for-go/arm/resources"
|
"github.com/Azure/azure-sdk-for-go/arm/resources"
|
||||||
"github.com/Azure/azure-sdk-for-go/arm/scheduler"
|
"github.com/Azure/azure-sdk-for-go/arm/scheduler"
|
||||||
"github.com/Azure/azure-sdk-for-go/arm/storage"
|
"github.com/Azure/azure-sdk-for-go/arm/storage"
|
||||||
"github.com/hashicorp/terraform/helper/pathorcontents"
|
"github.com/Azure/go-autorest/autorest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArmClient contains the handles to all the specific Azure Resource Manager
|
// ArmClient contains the handles to all the specific Azure Resource Manager
|
||||||
|
@ -46,14 +46,20 @@ type ArmClient struct {
|
||||||
storageUsageClient storage.UsageOperationsClient
|
storageUsageClient storage.UsageOperationsClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withRequestLogging() autorest.SendDecorator {
|
||||||
|
return func(s autorest.Sender) autorest.Sender {
|
||||||
|
return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
|
||||||
|
log.Printf("[DEBUG] Sending Azure RM Request %s to %s\n", r.Method, r.URL)
|
||||||
|
resp, err := s.Do(r)
|
||||||
|
log.Printf("[DEBUG] Received Azure RM Request status code %s for %s\n", resp.Status, r.URL)
|
||||||
|
return resp, err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getArmClient is a helper method which returns a fully instantiated
|
// getArmClient is a helper method which returns a fully instantiated
|
||||||
// *ArmClient based on the Config's current settings.
|
// *ArmClient based on the Config's current settings.
|
||||||
func (c *Config) getArmClient() (*ArmClient, error) {
|
func (c *Config) getArmClient() (*ArmClient, error) {
|
||||||
// first; check that all the necessary credentials were provided:
|
|
||||||
if !c._armCredentialsProvided() {
|
|
||||||
return nil, fmt.Errorf("Not all ARM-required fields have been provided.")
|
|
||||||
}
|
|
||||||
|
|
||||||
spt, err := azure.NewServicePrincipalToken(c.ClientID, c.ClientSecret, c.TenantID, azure.AzureResourceManagerScope)
|
spt, err := azure.NewServicePrincipalToken(c.ClientID, c.ClientSecret, c.TenantID, azure.AzureResourceManagerScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -66,149 +72,118 @@ func (c *Config) getArmClient() (*ArmClient, error) {
|
||||||
// clients be wished to be configured with custom Responders/PollingModess etc...
|
// clients be wished to be configured with custom Responders/PollingModess etc...
|
||||||
asc := compute.NewAvailabilitySetsClient(c.SubscriptionID)
|
asc := compute.NewAvailabilitySetsClient(c.SubscriptionID)
|
||||||
asc.Authorizer = spt
|
asc.Authorizer = spt
|
||||||
|
asc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.availSetClient = asc
|
client.availSetClient = asc
|
||||||
|
|
||||||
uoc := compute.NewUsageOperationsClient(c.SubscriptionID)
|
uoc := compute.NewUsageOperationsClient(c.SubscriptionID)
|
||||||
uoc.Authorizer = spt
|
uoc.Authorizer = spt
|
||||||
|
uoc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.usageOpsClient = uoc
|
client.usageOpsClient = uoc
|
||||||
|
|
||||||
vmeic := compute.NewVirtualMachineExtensionImagesClient(c.SubscriptionID)
|
vmeic := compute.NewVirtualMachineExtensionImagesClient(c.SubscriptionID)
|
||||||
vmeic.Authorizer = spt
|
vmeic.Authorizer = spt
|
||||||
|
vmeic.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.vmExtensionImageClient = vmeic
|
client.vmExtensionImageClient = vmeic
|
||||||
|
|
||||||
vmec := compute.NewVirtualMachineExtensionsClient(c.SubscriptionID)
|
vmec := compute.NewVirtualMachineExtensionsClient(c.SubscriptionID)
|
||||||
vmec.Authorizer = spt
|
vmec.Authorizer = spt
|
||||||
|
vmec.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.vmExtensionClient = vmec
|
client.vmExtensionClient = vmec
|
||||||
|
|
||||||
vmic := compute.NewVirtualMachineImagesClient(c.SubscriptionID)
|
vmic := compute.NewVirtualMachineImagesClient(c.SubscriptionID)
|
||||||
vmic.Authorizer = spt
|
vmic.Authorizer = spt
|
||||||
|
vmic.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.vmImageClient = vmic
|
client.vmImageClient = vmic
|
||||||
|
|
||||||
vmc := compute.NewVirtualMachinesClient(c.SubscriptionID)
|
vmc := compute.NewVirtualMachinesClient(c.SubscriptionID)
|
||||||
vmc.Authorizer = spt
|
vmc.Authorizer = spt
|
||||||
|
vmc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.vmClient = vmc
|
client.vmClient = vmc
|
||||||
|
|
||||||
agc := network.NewApplicationGatewaysClient(c.SubscriptionID)
|
agc := network.NewApplicationGatewaysClient(c.SubscriptionID)
|
||||||
agc.Authorizer = spt
|
agc.Authorizer = spt
|
||||||
|
agc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.appGatewayClient = agc
|
client.appGatewayClient = agc
|
||||||
|
|
||||||
ifc := network.NewInterfacesClient(c.SubscriptionID)
|
ifc := network.NewInterfacesClient(c.SubscriptionID)
|
||||||
ifc.Authorizer = spt
|
ifc.Authorizer = spt
|
||||||
|
ifc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.ifaceClient = ifc
|
client.ifaceClient = ifc
|
||||||
|
|
||||||
lbc := network.NewLoadBalancersClient(c.SubscriptionID)
|
lbc := network.NewLoadBalancersClient(c.SubscriptionID)
|
||||||
lbc.Authorizer = spt
|
lbc.Authorizer = spt
|
||||||
|
lbc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.loadBalancerClient = lbc
|
client.loadBalancerClient = lbc
|
||||||
|
|
||||||
lgc := network.NewLocalNetworkGatewaysClient(c.SubscriptionID)
|
lgc := network.NewLocalNetworkGatewaysClient(c.SubscriptionID)
|
||||||
lgc.Authorizer = spt
|
lgc.Authorizer = spt
|
||||||
|
lgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.localNetConnClient = lgc
|
client.localNetConnClient = lgc
|
||||||
|
|
||||||
pipc := network.NewPublicIPAddressesClient(c.SubscriptionID)
|
pipc := network.NewPublicIPAddressesClient(c.SubscriptionID)
|
||||||
pipc.Authorizer = spt
|
pipc.Authorizer = spt
|
||||||
|
pipc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.publicIPClient = pipc
|
client.publicIPClient = pipc
|
||||||
|
|
||||||
sgc := network.NewSecurityGroupsClient(c.SubscriptionID)
|
sgc := network.NewSecurityGroupsClient(c.SubscriptionID)
|
||||||
sgc.Authorizer = spt
|
sgc.Authorizer = spt
|
||||||
|
sgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.secGroupClient = sgc
|
client.secGroupClient = sgc
|
||||||
|
|
||||||
src := network.NewSecurityRulesClient(c.SubscriptionID)
|
src := network.NewSecurityRulesClient(c.SubscriptionID)
|
||||||
src.Authorizer = spt
|
src.Authorizer = spt
|
||||||
|
src.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.secRuleClient = src
|
client.secRuleClient = src
|
||||||
|
|
||||||
snc := network.NewSubnetsClient(c.SubscriptionID)
|
snc := network.NewSubnetsClient(c.SubscriptionID)
|
||||||
snc.Authorizer = spt
|
snc.Authorizer = spt
|
||||||
|
snc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.subnetClient = snc
|
client.subnetClient = snc
|
||||||
|
|
||||||
vgcc := network.NewVirtualNetworkGatewayConnectionsClient(c.SubscriptionID)
|
vgcc := network.NewVirtualNetworkGatewayConnectionsClient(c.SubscriptionID)
|
||||||
vgcc.Authorizer = spt
|
vgcc.Authorizer = spt
|
||||||
|
vgcc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.vnetGatewayConnectionsClient = vgcc
|
client.vnetGatewayConnectionsClient = vgcc
|
||||||
|
|
||||||
vgc := network.NewVirtualNetworkGatewaysClient(c.SubscriptionID)
|
vgc := network.NewVirtualNetworkGatewaysClient(c.SubscriptionID)
|
||||||
vgc.Authorizer = spt
|
vgc.Authorizer = spt
|
||||||
|
vgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.vnetGatewayClient = vgc
|
client.vnetGatewayClient = vgc
|
||||||
|
|
||||||
vnc := network.NewVirtualNetworksClient(c.SubscriptionID)
|
vnc := network.NewVirtualNetworksClient(c.SubscriptionID)
|
||||||
vnc.Authorizer = spt
|
vnc.Authorizer = spt
|
||||||
|
vnc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.vnetClient = vnc
|
client.vnetClient = vnc
|
||||||
|
|
||||||
rgc := resources.NewGroupsClient(c.SubscriptionID)
|
rgc := resources.NewGroupsClient(c.SubscriptionID)
|
||||||
rgc.Authorizer = spt
|
rgc.Authorizer = spt
|
||||||
|
rgc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.resourceGroupClient = rgc
|
client.resourceGroupClient = rgc
|
||||||
|
|
||||||
tc := resources.NewTagsClient(c.SubscriptionID)
|
tc := resources.NewTagsClient(c.SubscriptionID)
|
||||||
tc.Authorizer = spt
|
tc.Authorizer = spt
|
||||||
|
tc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.tagsClient = tc
|
client.tagsClient = tc
|
||||||
|
|
||||||
jc := scheduler.NewJobsClient(c.SubscriptionID)
|
jc := scheduler.NewJobsClient(c.SubscriptionID)
|
||||||
jc.Authorizer = spt
|
jc.Authorizer = spt
|
||||||
|
jc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.jobsClient = jc
|
client.jobsClient = jc
|
||||||
|
|
||||||
jcc := scheduler.NewJobCollectionsClient(c.SubscriptionID)
|
jcc := scheduler.NewJobCollectionsClient(c.SubscriptionID)
|
||||||
jcc.Authorizer = spt
|
jcc.Authorizer = spt
|
||||||
|
jcc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.jobsCollectionsClient = jcc
|
client.jobsCollectionsClient = jcc
|
||||||
|
|
||||||
ssc := storage.NewAccountsClient(c.SubscriptionID)
|
ssc := storage.NewAccountsClient(c.SubscriptionID)
|
||||||
ssc.Authorizer = spt
|
ssc.Authorizer = spt
|
||||||
|
ssc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.storageServiceClient = ssc
|
client.storageServiceClient = ssc
|
||||||
|
|
||||||
suc := storage.NewUsageOperationsClient(c.SubscriptionID)
|
suc := storage.NewUsageOperationsClient(c.SubscriptionID)
|
||||||
suc.Authorizer = spt
|
suc.Authorizer = spt
|
||||||
|
suc.Sender = autorest.CreateSender(withRequestLogging())
|
||||||
client.storageUsageClient = suc
|
client.storageUsageClient = suc
|
||||||
|
|
||||||
return &client, nil
|
return &client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// armCredentialsProvided is a helper method which indicates whether or not the
|
|
||||||
// credentials required for authenticating against the ARM APIs were provided.
|
|
||||||
func (c *Config) armCredentialsProvided() bool {
|
|
||||||
return c.ArmConfig != "" || c._armCredentialsProvided()
|
|
||||||
}
|
|
||||||
func (c *Config) _armCredentialsProvided() bool {
|
|
||||||
return !(c.SubscriptionID == "" || c.ClientID == "" || c.ClientSecret == "" || c.TenantID == "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// readArmSettings is a helper method which; given the contents of the ARM
|
|
||||||
// credentials file, loads all the data into the Config.
|
|
||||||
func (c *Config) readArmSettings(contents string) error {
|
|
||||||
data := &armConfigData{}
|
|
||||||
err := json.Unmarshal([]byte(contents), data)
|
|
||||||
|
|
||||||
c.SubscriptionID = data.SubscriptionID
|
|
||||||
c.ClientID = data.ClientID
|
|
||||||
c.ClientSecret = data.ClientSecret
|
|
||||||
c.TenantID = data.TenantID
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateArmConfigFile is a helper function which verifies that
|
|
||||||
// the provided ARM configuration file is valid.
|
|
||||||
func validateArmConfigFile(v interface{}, _ string) (ws []string, es []error) {
|
|
||||||
value := v.(string)
|
|
||||||
if value == "" {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pathOrContents, _, err := pathorcontents.Read(v.(string))
|
|
||||||
if err != nil {
|
|
||||||
es = append(es, fmt.Errorf("Error reading 'arm_config_file': %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
data := armConfigData{}
|
|
||||||
err = json.Unmarshal([]byte(pathOrContents), &data)
|
|
||||||
if err != nil {
|
|
||||||
es = append(es, fmt.Errorf("Error unmarshalling the provided 'arm_config_file': %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// armConfigData is a private struct which represents the expected layout of
|
|
||||||
// an ARM configuration file. It is used for unmarshalling purposes.
|
|
||||||
type armConfigData struct {
|
|
||||||
ClientID string `json:"clientID"`
|
|
||||||
ClientSecret string `json:"clientSecret"`
|
|
||||||
SubscriptionID string `json:"subscriptionID"`
|
|
||||||
TenantID string `json:"tenantID"`
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package azurerm
|
package azurerm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
@ -12,35 +11,27 @@ import (
|
||||||
func Provider() terraform.ResourceProvider {
|
func Provider() terraform.ResourceProvider {
|
||||||
return &schema.Provider{
|
return &schema.Provider{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"arm_config_file": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
Default: "",
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ARM_CONFIG_FILE", nil),
|
|
||||||
ValidateFunc: validateArmConfigFile,
|
|
||||||
},
|
|
||||||
|
|
||||||
"subscription_id": &schema.Schema{
|
"subscription_id": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Required: true,
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""),
|
DefaultFunc: schema.EnvDefaultFunc("ARM_SUBSCRIPTION_ID", ""),
|
||||||
},
|
},
|
||||||
|
|
||||||
"client_id": &schema.Schema{
|
"client_id": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Required: true,
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""),
|
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_ID", ""),
|
||||||
},
|
},
|
||||||
|
|
||||||
"client_secret": &schema.Schema{
|
"client_secret": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Required: true,
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""),
|
DefaultFunc: schema.EnvDefaultFunc("ARM_CLIENT_SECRET", ""),
|
||||||
},
|
},
|
||||||
|
|
||||||
"tenant_id": &schema.Schema{
|
"tenant_id": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Required: true,
|
||||||
DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
|
DefaultFunc: schema.EnvDefaultFunc("ARM_TENANT_ID", ""),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -59,19 +50,12 @@ func Provider() terraform.ResourceProvider {
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ManagementURL string
|
ManagementURL string
|
||||||
|
|
||||||
ArmConfig string
|
|
||||||
|
|
||||||
SubscriptionID string
|
SubscriptionID string
|
||||||
ClientID string
|
ClientID string
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
TenantID string
|
TenantID string
|
||||||
}
|
}
|
||||||
|
|
||||||
const noConfigError = `Credentials must be provided either via arm_config_file, or via
|
|
||||||
subscription_id, client_id, client_secret and tenant_id. Please see
|
|
||||||
the provider documentation for more information on how to obtain these
|
|
||||||
credentials.`
|
|
||||||
|
|
||||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||||
config := Config{
|
config := Config{
|
||||||
SubscriptionID: d.Get("subscription_id").(string),
|
SubscriptionID: d.Get("subscription_id").(string),
|
||||||
|
@ -80,20 +64,6 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if credentials file is provided:
|
|
||||||
armConfig := d.Get("arm_config_file").(string)
|
|
||||||
if armConfig != "" {
|
|
||||||
// then, load the settings from that:
|
|
||||||
if err := config.readArmSettings(armConfig); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// then; check whether the ARM credentials were provided:
|
|
||||||
if !config.armCredentialsProvided() {
|
|
||||||
return nil, fmt.Errorf(noConfigError)
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := config.getArmClient()
|
client, err := config.getArmClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -11,21 +11,6 @@ import (
|
||||||
var testAccProviders map[string]terraform.ResourceProvider
|
var testAccProviders map[string]terraform.ResourceProvider
|
||||||
var testAccProvider *schema.Provider
|
var testAccProvider *schema.Provider
|
||||||
|
|
||||||
const (
|
|
||||||
testAccSecurityGroupName = "terraform-security-group"
|
|
||||||
testAccHostedServiceName = "terraform-testing-service"
|
|
||||||
)
|
|
||||||
|
|
||||||
// testAccStorageServiceName is used as the name for the Storage Service
|
|
||||||
// created in all storage-related tests.
|
|
||||||
// It is much more convenient to provide a Storage Service which
|
|
||||||
// has been created beforehand as the creation of one takes a lot
|
|
||||||
// and would greatly impede the multitude of tests which rely on one.
|
|
||||||
// NOTE: the storage container should be located in `West US`.
|
|
||||||
var testAccStorageServiceName = os.Getenv("AZURE_STORAGE")
|
|
||||||
|
|
||||||
const testAccStorageContainerName = "terraform-testing-container"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
testAccProvider = Provider().(*schema.Provider)
|
testAccProvider = Provider().(*schema.Provider)
|
||||||
testAccProviders = map[string]terraform.ResourceProvider{
|
testAccProviders = map[string]terraform.ResourceProvider{
|
||||||
|
@ -44,19 +29,12 @@ func TestProvider_impl(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccPreCheck(t *testing.T) {
|
func testAccPreCheck(t *testing.T) {
|
||||||
if v := os.Getenv("ARM_CREDENTIALS_FILE"); v == "" {
|
subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID")
|
||||||
subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID")
|
clientID := os.Getenv("ARM_CLIENT_ID")
|
||||||
clientID := os.Getenv("ARM_CLIENT_ID")
|
clientSecret := os.Getenv("ARM_CLIENT_SECRET")
|
||||||
clientSecret := os.Getenv("ARM_CLIENT_SECRET")
|
tenantID := os.Getenv("ARM_TENANT_ID")
|
||||||
tenantID := os.Getenv("ARM_TENANT_ID")
|
|
||||||
|
|
||||||
if subscriptionID == "" || clientID == "" || clientSecret == "" || tenantID == "" {
|
if subscriptionID == "" || clientID == "" || clientSecret == "" || tenantID == "" {
|
||||||
t.Fatal("Either ARM_CREDENTIALS_FILE or ARM_SUBSCRIPTION_ID, ARM_CLIENT_ID, " +
|
t.Fatal("ARM_SUBSCRIPTION_ID, ARM_CLIENT_ID, ARM_CLIENT_SECRET and ARM_TENANT_ID must be set for acceptance tests")
|
||||||
"ARM_CLIENT_SECRET and ARM_TENANT_ID must be set for acceptance tests")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v := os.Getenv("AZURE_STORAGE"); v == "" {
|
|
||||||
t.Fatal("AZURE_STORAGE must be set for acceptance tests")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package azurerm
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -12,8 +13,6 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// resourceArmResourceGroup returns the *schema.Resource
|
|
||||||
// associated to resource group resources on ARM.
|
|
||||||
func resourceArmResourceGroup() *schema.Resource {
|
func resourceArmResourceGroup() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Create: resourceArmResourceGroupCreate,
|
Create: resourceArmResourceGroupCreate,
|
||||||
|
@ -38,8 +37,6 @@ func resourceArmResourceGroup() *schema.Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateArmResourceGroupName validates inputs to the name argument against the requirements
|
|
||||||
// documented in the ARM REST API guide: http://bit.ly/1NEXclG
|
|
||||||
func validateArmResourceGroupName(v interface{}, k string) (ws []string, es []error) {
|
func validateArmResourceGroupName(v interface{}, k string) (ws []string, es []error) {
|
||||||
value := v.(string)
|
value := v.(string)
|
||||||
|
|
||||||
|
@ -51,14 +48,13 @@ func validateArmResourceGroupName(v interface{}, k string) (ws []string, es []er
|
||||||
es = append(es, fmt.Errorf("%q may not end with a period", k))
|
es = append(es, fmt.Errorf("%q may not end with a period", k))
|
||||||
}
|
}
|
||||||
|
|
||||||
if matched := regexp.MustCompile(`^[\(\)\.a-zA-Z0-9_-]$`).Match([]byte(value)); !matched {
|
if matched := regexp.MustCompile(`[\(\)\.a-zA-Z0-9_-]`).Match([]byte(value)); !matched {
|
||||||
es = append(es, fmt.Errorf("%q may only contain alphanumeric characters, dash, underscores, parentheses and periods", k))
|
es = append(es, fmt.Errorf("%q may only contain alphanumeric characters, dash, underscores, parentheses and periods", k))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceArmResourceGroupCreate goes ahead and creates the specified ARM resource group.
|
|
||||||
func resourceArmResourceGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceArmResourceGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*ArmClient)
|
client := meta.(*ArmClient)
|
||||||
resGroupClient := client.resourceGroupClient
|
resGroupClient := client.resourceGroupClient
|
||||||
|
@ -66,64 +62,67 @@ func resourceArmResourceGroupCreate(d *schema.ResourceData, meta interface{}) er
|
||||||
name := d.Get("name").(string)
|
name := d.Get("name").(string)
|
||||||
location := d.Get("location").(string)
|
location := d.Get("location").(string)
|
||||||
|
|
||||||
log.Printf("[INFO] Issuing Azure ARM creation request for resource group '%s'.", name)
|
|
||||||
|
|
||||||
rg := resources.ResourceGroup{
|
rg := resources.ResourceGroup{
|
||||||
Name: &name,
|
Name: &name,
|
||||||
Location: &location,
|
Location: &location,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := resGroupClient.CreateOrUpdate(name, rg)
|
resp, err := resGroupClient.CreateOrUpdate(name, rg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error issuing Azure ARM create request for resource group '%s': %s", name, err)
|
return fmt.Errorf("Error issuing Azure ARM create request for resource group '%s': %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.SetId(*rg.Name)
|
d.SetId(*resp.ID)
|
||||||
|
|
||||||
// Wait for the resource group to become available
|
log.Printf("[DEBUG] Waiting for Resource Group (%s) to become available", name)
|
||||||
// TODO(jen20): Is there any need for this?
|
|
||||||
log.Printf("[DEBUG] Waiting for Resource Group (%s) to become available", d.Id())
|
|
||||||
stateConf := &resource.StateChangeConf{
|
stateConf := &resource.StateChangeConf{
|
||||||
Pending: []string{"Accepted"},
|
Pending: []string{"Accepted"},
|
||||||
Target: "Succeeded",
|
Target: "Succeeded",
|
||||||
Refresh: resourceGroupStateRefreshFunc(client, d.Id()),
|
Refresh: resourceGroupStateRefreshFunc(client, name),
|
||||||
Timeout: 10 * time.Minute,
|
Timeout: 10 * time.Minute,
|
||||||
}
|
}
|
||||||
if _, err := stateConf.WaitForState(); err != nil {
|
if _, err := stateConf.WaitForState(); err != nil {
|
||||||
return fmt.Errorf("Error waiting for Resource Group (%s) to become available: %s", d.Id(), err)
|
return fmt.Errorf("Error waiting for Resource Group (%s) to become available: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceArmResourceGroupRead(d, meta)
|
return resourceArmResourceGroupRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceArmResourceGroupRead goes ahead and reads the state of the corresponding ARM resource group.
|
|
||||||
func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceArmResourceGroupRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
resGroupClient := meta.(*ArmClient).resourceGroupClient
|
resGroupClient := meta.(*ArmClient).resourceGroupClient
|
||||||
|
|
||||||
name := d.Id()
|
id, err := parseAzureResourceID(d.Id())
|
||||||
log.Printf("[INFO] Issuing read request to Azure ARM for resource group '%s'.", name)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := id.ResourceGroup
|
||||||
|
|
||||||
res, err := resGroupClient.Get(name)
|
res, err := resGroupClient.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if res.StatusCode == http.StatusNotFound {
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return fmt.Errorf("Error issuing read request to Azure ARM for resource group '%s': %s", name, err)
|
return fmt.Errorf("Error issuing read request to Azure ARM for resource group '%s': %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.Set("name", *res.Name)
|
d.Set("name", res.Name)
|
||||||
d.Set("location", *res.Location)
|
d.Set("location", res.Location)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceArmResourceGroupExists goes ahead and checks for the existence of the correspoding ARM resource group.
|
|
||||||
func resourceArmResourceGroupExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
func resourceArmResourceGroupExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||||||
resGroupClient := meta.(*ArmClient).resourceGroupClient
|
resGroupClient := meta.(*ArmClient).resourceGroupClient
|
||||||
|
|
||||||
name := d.Id()
|
id, err := parseAzureResourceID(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
name := id.ResourceGroup
|
||||||
|
|
||||||
resp, err := resGroupClient.CheckExistence(name)
|
resp, err := resGroupClient.CheckExistence(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(aznashwan): implement some error switching helpers in the SDK
|
|
||||||
// to avoid HTTP error checks such as the below:
|
|
||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -134,13 +133,16 @@ func resourceArmResourceGroupExists(d *schema.ResourceData, meta interface{}) (b
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceArmResourceGroupDelete deletes the specified ARM resource group.
|
|
||||||
func resourceArmResourceGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceArmResourceGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
resGroupClient := meta.(*ArmClient).resourceGroupClient
|
resGroupClient := meta.(*ArmClient).resourceGroupClient
|
||||||
|
|
||||||
name := d.Id()
|
id, err := parseAzureResourceID(d.Id())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := id.ResourceGroup
|
||||||
|
|
||||||
_, err := resGroupClient.Delete(name)
|
_, err = resGroupClient.Delete(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -148,8 +150,6 @@ func resourceArmResourceGroupDelete(d *schema.ResourceData, meta interface{}) er
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceGroupStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
|
|
||||||
// a resource group.
|
|
||||||
func resourceGroupStateRefreshFunc(client *ArmClient, id string) resource.StateRefreshFunc {
|
func resourceGroupStateRefreshFunc(client *ArmClient, id string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
res, err := client.resourceGroupClient.Get(id)
|
res, err := client.resourceGroupClient.Get(id)
|
|
@ -0,0 +1,82 @@
|
||||||
|
package azurerm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/core/http"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAzureRMResourceGroup_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testCheckAzureRMResourceGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAzureRMResourceGroup_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testCheckAzureRMResourceGroupExists("azurerm_resource_group.test"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCheckAzureRMResourceGroupExists(name string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
// Ensure we have enough information in state to look up in API
|
||||||
|
rs, ok := s.RootModule().Resources[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceGroup := rs.Primary.Attributes["name"]
|
||||||
|
|
||||||
|
// Ensure resource group exists in API
|
||||||
|
conn := testAccProvider.Meta().(*ArmClient).resourceGroupClient
|
||||||
|
|
||||||
|
resp, err := conn.Get(resourceGroup)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Bad: Get on resourceGroupClient: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return fmt.Errorf("Bad: Virtual Network %q (resource group: %q) does not exist", name, resourceGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCheckAzureRMResourceGroupDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*ArmClient).resourceGroupClient
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "azurerm_resource_group" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceGroup := rs.Primary.ID
|
||||||
|
|
||||||
|
resp, err := conn.Get(resourceGroup)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusNotFound {
|
||||||
|
return fmt.Errorf("Resource Group still exists:\n%#v", resp.Properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAzureRMResourceGroup_basic = `
|
||||||
|
resource "azurerm_resource_group" "test" {
|
||||||
|
name = "acceptanceTestResourceGroup1_basic"
|
||||||
|
location = "West US"
|
||||||
|
}
|
||||||
|
`
|
|
@ -16,7 +16,7 @@ func resourceArmVirtualNetwork() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Create: resourceArmVirtualNetworkCreate,
|
Create: resourceArmVirtualNetworkCreate,
|
||||||
Read: resourceArmVirtualNetworkRead,
|
Read: resourceArmVirtualNetworkRead,
|
||||||
Update: resourceArmVirtualNetworkUpdate,
|
Update: resourceArmVirtualNetworkCreate,
|
||||||
Delete: resourceArmVirtualNetworkDelete,
|
Delete: resourceArmVirtualNetworkDelete,
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
@ -78,7 +78,6 @@ func resourceArmVirtualNetwork() *schema.Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceArmVirtualNetworkCreate creates the specified ARM virtual network.
|
|
||||||
func resourceArmVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceArmVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
client := meta.(*ArmClient)
|
client := meta.(*ArmClient)
|
||||||
vnetClient := client.vnetClient
|
vnetClient := client.vnetClient
|
||||||
|
@ -95,22 +94,14 @@ func resourceArmVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) e
|
||||||
Properties: getVirtualNetworkProperties(d),
|
Properties: getVirtualNetworkProperties(d),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] Sending virtual network create request to ARM.")
|
resp, err := vnetClient.CreateOrUpdate(resGroup, name, vnet)
|
||||||
_, err := vnetClient.CreateOrUpdate(resGroup, name, vnet)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if res.Response.StatusCode != http.StatusAccepted {
|
d.SetId(*resp.ID)
|
||||||
// return fmt.Errorf("Creation request was denies: code: %d", res.Response.StatusCode)
|
|
||||||
// }
|
|
||||||
|
|
||||||
d.SetId(name)
|
log.Printf("[DEBUG] Waiting for Virtual Network (%s) to become available", name)
|
||||||
d.Set("resGroup", resGroup)
|
|
||||||
|
|
||||||
// Wait for the resource group to become available
|
|
||||||
// TODO(jen20): Is there any need for this?
|
|
||||||
log.Printf("[DEBUG] Waiting for Virtual Network (%s) to become available", d.Id())
|
|
||||||
stateConf := &resource.StateChangeConf{
|
stateConf := &resource.StateChangeConf{
|
||||||
Pending: []string{"Accepted", "Updating"},
|
Pending: []string{"Accepted", "Updating"},
|
||||||
Target: "Succeeded",
|
Target: "Succeeded",
|
||||||
|
@ -118,25 +109,24 @@ func resourceArmVirtualNetworkCreate(d *schema.ResourceData, meta interface{}) e
|
||||||
Timeout: 10 * time.Minute,
|
Timeout: 10 * time.Minute,
|
||||||
}
|
}
|
||||||
if _, err := stateConf.WaitForState(); err != nil {
|
if _, err := stateConf.WaitForState(); err != nil {
|
||||||
return fmt.Errorf("Error waiting for Virtual Network (%s) to become available: %s", d.Id(), err)
|
return fmt.Errorf("Error waiting for Virtual Network (%s) to become available: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceArmVirtualNetworkRead(d, meta)
|
return resourceArmVirtualNetworkRead(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceArmVirtualNetworkRead goes ahead and reads the state of the corresponding ARM virtual network.
|
|
||||||
func resourceArmVirtualNetworkRead(d *schema.ResourceData, meta interface{}) error {
|
func resourceArmVirtualNetworkRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
vnetClient := meta.(*ArmClient).vnetClient
|
vnetClient := meta.(*ArmClient).vnetClient
|
||||||
|
|
||||||
name := d.Get("name").(string)
|
id, err := parseAzureResourceID(d.Id())
|
||||||
resGroup := d.Get("resource_group_name").(string)
|
if err != nil {
|
||||||
|
return err
|
||||||
log.Printf("[INFO] Sending virtual network read request to ARM.")
|
}
|
||||||
|
resGroup := id.ResourceGroup
|
||||||
|
name := id.Path["virtualNetworks"]
|
||||||
|
|
||||||
resp, err := vnetClient.Get(resGroup, name)
|
resp, err := vnetClient.Get(resGroup, name)
|
||||||
if resp.StatusCode == http.StatusNotFound {
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
// it means the virtual network has been deleted in the meantime;
|
|
||||||
// so we must go ahead and remove it here:
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -145,10 +135,9 @@ func resourceArmVirtualNetworkRead(d *schema.ResourceData, meta interface{}) err
|
||||||
}
|
}
|
||||||
vnet := *resp.Properties
|
vnet := *resp.Properties
|
||||||
|
|
||||||
// update all the appropriate values:
|
// update appropriate values
|
||||||
d.Set("address_space", vnet.AddressSpace.AddressPrefixes)
|
d.Set("address_space", vnet.AddressSpace.AddressPrefixes)
|
||||||
|
|
||||||
// read state of subnets:
|
|
||||||
subnets := &schema.Set{
|
subnets := &schema.Set{
|
||||||
F: resourceAzureSubnetHash,
|
F: resourceAzureSubnetHash,
|
||||||
}
|
}
|
||||||
|
@ -166,7 +155,6 @@ func resourceArmVirtualNetworkRead(d *schema.ResourceData, meta interface{}) err
|
||||||
}
|
}
|
||||||
d.Set("subnet", subnets)
|
d.Set("subnet", subnets)
|
||||||
|
|
||||||
// now; dns servers:
|
|
||||||
dnses := []string{}
|
dnses := []string{}
|
||||||
for _, dns := range *vnet.DhcpOptions.DNSServers {
|
for _, dns := range *vnet.DhcpOptions.DNSServers {
|
||||||
dnses = append(dnses, dns)
|
dnses = append(dnses, dns)
|
||||||
|
@ -176,26 +164,21 @@ func resourceArmVirtualNetworkRead(d *schema.ResourceData, meta interface{}) err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resourceArmVirtualNetworkUpdate goes ahead and updates the corresponding ARM virtual network.
|
|
||||||
func resourceArmVirtualNetworkUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
||||||
// considering Create's idempotency, Update is simply a proxy for it...
|
|
||||||
// Update has been left as a separate function here for utmost clarity:
|
|
||||||
return resourceArmVirtualNetworkCreate(d, meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
// resourceArmVirtualNetworkDelete deletes the specified ARM virtual network.
|
|
||||||
func resourceArmVirtualNetworkDelete(d *schema.ResourceData, meta interface{}) error {
|
func resourceArmVirtualNetworkDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
vnetClient := meta.(*ArmClient).vnetClient
|
vnetClient := meta.(*ArmClient).vnetClient
|
||||||
|
|
||||||
name := d.Get("name").(string)
|
id, err := parseAzureResourceID(d.Id())
|
||||||
resGroup := d.Get("resource_group_name").(string)
|
if err != nil {
|
||||||
_, err := vnetClient.Delete(resGroup, name)
|
return err
|
||||||
|
}
|
||||||
|
resGroup := id.ResourceGroup
|
||||||
|
name := id.Path["virtualNetworks"]
|
||||||
|
|
||||||
|
_, err = vnetClient.Delete(resGroup, name)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getVirtualNetworkProperties is a helper function which returns the
|
|
||||||
// VirtualNetworkPropertiesFormat of the network resource.
|
|
||||||
func getVirtualNetworkProperties(d *schema.ResourceData) *network.VirtualNetworkPropertiesFormat {
|
func getVirtualNetworkProperties(d *schema.ResourceData) *network.VirtualNetworkPropertiesFormat {
|
||||||
// first; get address space prefixes:
|
// first; get address space prefixes:
|
||||||
prefixes := []string{}
|
prefixes := []string{}
|
||||||
|
@ -255,8 +238,6 @@ func resourceAzureSubnetHash(v interface{}) int {
|
||||||
return hashcode.String(subnet)
|
return hashcode.String(subnet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtualNetworkStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
|
|
||||||
// a virtual network.
|
|
||||||
func virtualNetworkStateRefreshFunc(client *ArmClient, resourceGroupName string, networkName string) resource.StateRefreshFunc {
|
func virtualNetworkStateRefreshFunc(client *ArmClient, resourceGroupName string, networkName string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
res, err := client.vnetClient.Get(resourceGroupName, networkName)
|
res, err := client.vnetClient.Get(resourceGroupName, networkName)
|
|
@ -0,0 +1,100 @@
|
||||||
|
package azurerm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/core/http"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAzureRMVirtualNetwork_basic(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testCheckAzureRMVirtualNetworkDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAzureRMVirtualNetwork_basic,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testCheckAzureRMVirtualNetworkExists("azurerm_virtual_network.test"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCheckAzureRMVirtualNetworkExists(name string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
// Ensure we have enough information in state to look up in API
|
||||||
|
rs, ok := s.RootModule().Resources[name]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
virtualNetworkName := 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 virtual network: %s", virtualNetworkName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure resource group/virtual network combination exists in API
|
||||||
|
conn := testAccProvider.Meta().(*ArmClient).vnetClient
|
||||||
|
|
||||||
|
resp, err := conn.Get(resourceGroup, virtualNetworkName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Bad: Get on vnetClient: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return fmt.Errorf("Bad: Virtual Network %q (resource group: %q) does not exist", name, resourceGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCheckAzureRMVirtualNetworkDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*ArmClient).vnetClient
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "azurerm_virtual_network" {
|
||||||
|
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("Virtual Network sitll exists:\n%#v", resp.Properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAzureRMVirtualNetwork_basic = `
|
||||||
|
resource "azurerm_resource_group" "test" {
|
||||||
|
name = "acceptanceTestResourceGroup1"
|
||||||
|
location = "West US"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "azurerm_virtual_network" "test" {
|
||||||
|
name = "acceptanceTestVirtualNetwork1"
|
||||||
|
address_space = ["10.0.0.0/16"]
|
||||||
|
location = "West US"
|
||||||
|
resource_group_name = "${azurerm_resource_group.test.name}"
|
||||||
|
|
||||||
|
subnet {
|
||||||
|
name = "subnet1"
|
||||||
|
address_prefix = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -0,0 +1,82 @@
|
||||||
|
package azurerm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceID represents a parsed long-form Azure Resource Manager ID
|
||||||
|
// with the Subscription ID, Resource Group and the Provider as top-
|
||||||
|
// level fields, and other key-value pairs available via a map in the
|
||||||
|
// Path field.
|
||||||
|
type ResourceID struct {
|
||||||
|
SubscriptionID string
|
||||||
|
ResourceGroup string
|
||||||
|
Provider string
|
||||||
|
Path map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseAzureResourceID converts a long-form Azure Resource Manager ID
|
||||||
|
// into a ResourceID. We make assumptions about the structure of URLs,
|
||||||
|
// which is obviously not good, but the best thing available given the
|
||||||
|
// SDK.
|
||||||
|
func parseAzureResourceID(id string) (*ResourceID, error) {
|
||||||
|
idURL, err := url.ParseRequestURI(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Cannot parse Azure Id: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := idURL.Path
|
||||||
|
|
||||||
|
path = strings.TrimSpace(path)
|
||||||
|
if strings.HasPrefix(path, "/") {
|
||||||
|
path = path[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(path, "/") {
|
||||||
|
path = path[:len(path)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
components := strings.Split(path, "/")
|
||||||
|
|
||||||
|
// We should have an even number of key-value pairs.
|
||||||
|
if len(components)%2 != 0 {
|
||||||
|
return nil, fmt.Errorf("The number of path segments is not divisible by 2 in %q", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the constituent key-value pairs into a map
|
||||||
|
componentMap := make(map[string]string, len(components)/2)
|
||||||
|
for current := 0; current < len(components); current += 2 {
|
||||||
|
key := components[current]
|
||||||
|
value := components[current+1]
|
||||||
|
|
||||||
|
componentMap[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build up a ResourceID from the map
|
||||||
|
idObj := &ResourceID{}
|
||||||
|
idObj.Path = componentMap
|
||||||
|
|
||||||
|
if subscription, ok := componentMap["subscriptions"]; ok {
|
||||||
|
idObj.SubscriptionID = subscription
|
||||||
|
delete(componentMap, "subscriptions")
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("No subscription ID found in: %q", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resourceGroup, ok := componentMap["resourceGroups"]; ok {
|
||||||
|
idObj.ResourceGroup = resourceGroup
|
||||||
|
delete(componentMap, "resourceGroups")
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("No resource group name found in: %q", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is OK not to have a provider in the case of a resource group
|
||||||
|
if provider, ok := componentMap["providers"]; ok {
|
||||||
|
idObj.Provider = provider
|
||||||
|
delete(componentMap, "providers")
|
||||||
|
}
|
||||||
|
|
||||||
|
return idObj, nil
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package azurerm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseAzureResourceID(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
id string
|
||||||
|
expectedResourceID *ResourceID
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"random",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038/resourceGroups/testGroup1",
|
||||||
|
&ResourceID{
|
||||||
|
SubscriptionID: "6d74bdd2-9f84-11e5-9bd9-7831c1c4c038",
|
||||||
|
ResourceGroup: "testGroup1",
|
||||||
|
Provider: "",
|
||||||
|
Path: map[string]string{},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038/resourceGroups/testGroup1/providers/Microsoft.Network",
|
||||||
|
&ResourceID{
|
||||||
|
SubscriptionID: "6d74bdd2-9f84-11e5-9bd9-7831c1c4c038",
|
||||||
|
ResourceGroup: "testGroup1",
|
||||||
|
Provider: "Microsoft.Network",
|
||||||
|
Path: map[string]string{},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Missing leading /
|
||||||
|
"subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038/resourceGroups/testGroup1/providers/Microsoft.Network/virtualNetworks/virtualNetwork1/",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038/resourceGroups/testGroup1/providers/Microsoft.Network/virtualNetworks/virtualNetwork1",
|
||||||
|
&ResourceID{
|
||||||
|
SubscriptionID: "6d74bdd2-9f84-11e5-9bd9-7831c1c4c038",
|
||||||
|
ResourceGroup: "testGroup1",
|
||||||
|
Provider: "Microsoft.Network",
|
||||||
|
Path: map[string]string{
|
||||||
|
"virtualNetworks": "virtualNetwork1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038/resourceGroups/testGroup1/providers/Microsoft.Network/virtualNetworks/virtualNetwork1?api-version=2006-01-02-preview",
|
||||||
|
&ResourceID{
|
||||||
|
SubscriptionID: "6d74bdd2-9f84-11e5-9bd9-7831c1c4c038",
|
||||||
|
ResourceGroup: "testGroup1",
|
||||||
|
Provider: "Microsoft.Network",
|
||||||
|
Path: map[string]string{
|
||||||
|
"virtualNetworks": "virtualNetwork1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"/subscriptions/6d74bdd2-9f84-11e5-9bd9-7831c1c4c038/resourceGroups/testGroup1/providers/Microsoft.Network/virtualNetworks/virtualNetwork1/subnets/publicInstances1?api-version=2006-01-02-preview",
|
||||||
|
&ResourceID{
|
||||||
|
SubscriptionID: "6d74bdd2-9f84-11e5-9bd9-7831c1c4c038",
|
||||||
|
ResourceGroup: "testGroup1",
|
||||||
|
Provider: "Microsoft.Network",
|
||||||
|
Path: map[string]string{
|
||||||
|
"virtualNetworks": "virtualNetwork1",
|
||||||
|
"subnets": "publicInstances1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
parsed, err := parseAzureResourceID(test.id)
|
||||||
|
if test.expectError && err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(test.expectedResourceID, parsed) {
|
||||||
|
t.Fatalf("Unexpected resource ID:\nExpected: %+v\nGot: %+v\n", test.expectedResourceID, parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ bundle exec middleman server &
|
||||||
SCRIPT
|
SCRIPT
|
||||||
|
|
||||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
config.vm.box = "chef/ubuntu-12.04"
|
config.vm.box = "bento/ubuntu-12.04"
|
||||||
config.vm.network "private_network", ip: "33.33.30.10"
|
config.vm.network "private_network", ip: "33.33.30.10"
|
||||||
config.vm.provision "shell", inline: $script, privileged: false
|
config.vm.provision "shell", inline: $script, privileged: false
|
||||||
config.vm.synced_folder ".", "/vagrant", type: "rsync"
|
config.vm.synced_folder ".", "/vagrant", type: "rsync"
|
||||||
|
|
|
@ -10,6 +10,7 @@ body.layout-atlas,
|
||||||
body.layout-aws,
|
body.layout-aws,
|
||||||
body.layout-azure,
|
body.layout-azure,
|
||||||
body.layout-chef,
|
body.layout-chef,
|
||||||
|
body.layout-azurerm,
|
||||||
body.layout-cloudflare,
|
body.layout-cloudflare,
|
||||||
body.layout-cloudstack,
|
body.layout-cloudstack,
|
||||||
body.layout-consul,
|
body.layout-consul,
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
layout: "azurerm"
|
||||||
|
page_title: "Provider: Azure Resource Manager"
|
||||||
|
sidebar_current: "docs-azurerm-index"
|
||||||
|
description: |-
|
||||||
|
The Azure Resource Manager provider is used to interact with the many resources supported by Azure, via the ARM API. This supercedes the Azure provider, which interacts with Azure using the Service Management API. The provider needs to be configured with a credentials file, or credentials needed to generate OAuth tokens for the ARM API.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Azure Resource Manager Provider
|
||||||
|
|
||||||
|
The Azure Resource Manager provider is used to interact with the many resources
|
||||||
|
supported by Azure, via the ARM API. This supercedes the Azure provider, which
|
||||||
|
interacts with Azure using the Service Management API. The provider needs to be
|
||||||
|
configured with the credentials needed to generate OAuth tokens for the ARM API.
|
||||||
|
|
||||||
|
Use the navigation to the left to read about the available resources.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Configure the Azure Resource Manager Provider
|
||||||
|
provider "azurerm" {
|
||||||
|
subscription_id = "..."
|
||||||
|
client_id = "..."
|
||||||
|
client_secret = "..."
|
||||||
|
tenant_id = "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a resource group
|
||||||
|
resource "azurerm_resource_group" "production" {
|
||||||
|
name = "production"
|
||||||
|
location = "West US"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a virtual network in the web_servers resource group
|
||||||
|
resource "azurerm_virtual_network" "network" {
|
||||||
|
name = "productionNetwork"
|
||||||
|
address_space = ["10.0.0.0/16"]
|
||||||
|
location = "West US"
|
||||||
|
resource_group_name = "${azurerm_resource_group.production.name}"
|
||||||
|
|
||||||
|
subnet {
|
||||||
|
name = "subnet1"
|
||||||
|
address_prefix = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet {
|
||||||
|
name = "subnet2"
|
||||||
|
address_prefix = "10.0.2.0/24"
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet {
|
||||||
|
name = "subnet3"
|
||||||
|
address_prefix = "10.0.3.0/24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `subscription_id` - (Optional) The subscription ID to use. It can also
|
||||||
|
be sourced from the `ARM_SUBSCRIPTION_ID` environment variable.
|
||||||
|
|
||||||
|
* `client_id` - (Optional) The client ID to use. It can also be sourced from
|
||||||
|
the `ARM_CLIENT_ID` environment variable.
|
||||||
|
|
||||||
|
* `client_secret` - (Optional) The client secret to use. It can also be sourced from
|
||||||
|
the `ARM_CLIENT_SECRET` environment variable.
|
||||||
|
|
||||||
|
* `tenant_id` - (Optional) The tenant ID to use. It can also be sourced from the
|
||||||
|
`ARM_TENANT_ID` environment variable.
|
||||||
|
|
||||||
|
## Testing:
|
||||||
|
|
||||||
|
Credentials must be provided via the `ARM_SUBSCRIPTION_ID`, `ARM_CLIENT_ID`,
|
||||||
|
`ARM_CLIENT_SECRET` and `ARM_TENANT_ID` environment variables in order to run
|
||||||
|
acceptance tests.
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
layout: "azurerm"
|
||||||
|
page_title: "Azure Resource Manager: azurerm_resource_group"
|
||||||
|
sidebar_current: "docs-azurerm-resource-resource-group"
|
||||||
|
description: |-
|
||||||
|
Creates a new resource group on Azure.
|
||||||
|
---
|
||||||
|
|
||||||
|
# azurerm\_resource\_group
|
||||||
|
|
||||||
|
Creates a new resource group on Azure.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "azurerm_resource_group" "test" {
|
||||||
|
name = "testResourceGroup1"
|
||||||
|
location = "West US"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) The name of the resource group. Must be unique on your
|
||||||
|
Azure subscription.
|
||||||
|
|
||||||
|
* `location` - (Required) The location where the resource group should be created.
|
||||||
|
For a list of all Azure locations, please consult [this link](http://azure.microsoft.com/en-us/regions/).
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The resource group ID.
|
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
layout: "azurerm"
|
||||||
|
page_title: "Azure Resource Manager: azure_virtual_network"
|
||||||
|
sidebar_current: "docs-azurerm-resource-virtual-network"
|
||||||
|
description: |-
|
||||||
|
Creates a new virtual network including any configured subnets. Each subnet can optionally be configured with a security group to be associated with the subnet.
|
||||||
|
---
|
||||||
|
|
||||||
|
# azurerm\_virtual\_network
|
||||||
|
|
||||||
|
Creates a new virtual network including any configured subnets. Each subnet can
|
||||||
|
optionally be configured with a security group to be associated with the subnet.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "azurerm_virtual_network" "test" {
|
||||||
|
name = "virtualNetwork1"
|
||||||
|
resource_group_name = "${azurerm_resource_group.test.name}"
|
||||||
|
address_space = ["10.0.0.0/16"]
|
||||||
|
location = "West US"
|
||||||
|
|
||||||
|
subnet {
|
||||||
|
name = "subnet1"
|
||||||
|
address_prefix = "10.0.1.0/24"
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet {
|
||||||
|
name = "subnet2"
|
||||||
|
address_prefix = "10.0.2.0/24"
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet {
|
||||||
|
name = "subnet3"
|
||||||
|
address_prefix = "10.0.3.0/24"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `name` - (Required) The name of the virtual network. Changing this forces a
|
||||||
|
new resource to be created.
|
||||||
|
|
||||||
|
* `resource_group_name` - (Required) The name of the resource group in which to
|
||||||
|
create the virtual network.
|
||||||
|
|
||||||
|
* `address_space` - (Required) The address space that is used the virtual
|
||||||
|
network. You can supply more than one address space. Changing this forces
|
||||||
|
a new resource to be created.
|
||||||
|
|
||||||
|
* `location` - (Required) The location/region where the virtual network is
|
||||||
|
created. Changing this forces a new resource to be created.
|
||||||
|
|
||||||
|
* `dns_servers` - (Optional) List of names of DNS servers previously registered
|
||||||
|
on Azure.
|
||||||
|
|
||||||
|
* `subnet` - (Required) Can be specified multiple times to define multiple
|
||||||
|
subnets. Each `subnet` block supports fields documented below.
|
||||||
|
|
||||||
|
The `subnet` block supports:
|
||||||
|
|
||||||
|
* `name` - (Required) The name of the subnet.
|
||||||
|
|
||||||
|
* `address_prefix` - (Required) The address prefix to use for the subnet.
|
||||||
|
|
||||||
|
* `security_group` - (Optional) The Network Security Group to associate with
|
||||||
|
the subnet.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `id` - The virtual NetworkConfiguration ID.
|
|
@ -0,0 +1,30 @@
|
||||||
|
<% wrap_layout :inner do %>
|
||||||
|
<% content_for :sidebar do %>
|
||||||
|
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||||
|
<ul class="nav docs-sidenav">
|
||||||
|
<li<%= sidebar_current("docs-home") %>>
|
||||||
|
<a href="/docs/providers/index.html">« Documentation Home</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-azurerm-index") %>>
|
||||||
|
<a href="/docs/providers/azurerm/index.html">Azure Resource Manager Provider</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current(/^docs-azurerm-resource/) %>>
|
||||||
|
<a href="#">Resources</a>
|
||||||
|
<ul class="nav nav-visible">
|
||||||
|
<li<%= sidebar_current("docs-azure-resource-resource-group") %>>
|
||||||
|
<a href="/docs/providers/azurerm/r/resource_group.html">azurerm_resource_group</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-azure-resource-virtual-network") %>>
|
||||||
|
<a href="/docs/providers/azurerm/r/virtual_network.html">azurerm_virtual_network</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= yield %>
|
||||||
|
<% end %>
|
|
@ -130,7 +130,11 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-providers-azure") %>>
|
<li<%= sidebar_current("docs-providers-azure") %>>
|
||||||
<a href="/docs/providers/azure/index.html">Azure</a>
|
<a href="/docs/providers/azure/index.html">Azure (Service Management)</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-providers-azurerm") %>>
|
||||||
|
<a href="/docs/providers/azurerm/index.html">Azure (Resource Manager)</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-providers-chef") %>>
|
<li<%= sidebar_current("docs-providers-chef") %>>
|
||||||
|
|
Loading…
Reference in New Issue