Merge pull request #2052 from aznashwan/azure

provider/azure: added a number of storage and networking resources.
This commit is contained in:
Paul Hinze 2015-06-12 16:49:36 -05:00
commit 1ebe117085
45 changed files with 3342 additions and 651 deletions

View File

@ -5,7 +5,7 @@ import (
"os"
"sync"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management"
)
// Config is the configuration structure used to instantiate a

View File

@ -0,0 +1,48 @@
package azure
const (
// terraformAzureLabel is used as the label for the hosted service created
// by Terraform on Azure.
terraformAzureLabel = "terraform-on-azure"
// terraformAzureDescription is the description used for the hosted service
// created by Terraform on Azure.
terraformAzureDescription = "Hosted service automatically created by terraform."
)
// parameterDescriptions holds a list of descriptions for all the available
// parameters of an Azure configuration.
var parameterDescriptions = map[string]string{
// provider descriptions:
"management_url": "The URL of the management API all requests should be sent to.\n" +
"Defaults to 'https://management.core.windows.net/', which is the default Azure API URL.\n" +
"This should be filled in only if you have your own datacenter with its own hosted management API.",
"management_certificate": "The certificate for connecting to the management API specified with 'management_url'",
"subscription_id": "The subscription ID to be used when connecting to the management API.",
"publish_settings_file": "The publish settings file, either created by you or downloaded from 'https://manage.windowsazure.com/publishsettings'",
// general resource descriptions:
"name": "Name of the resource to be created as it will appear in the Azure dashboard.",
"service_name": "Name of the hosted service within Azure. Will have a DNS entry as dns-name.cloudapp.net",
"location": "The Azure location where the resource will be located.\n" +
"A list of Azure locations can be found here: http://azure.microsoft.com/en-us/regions/",
"reverse_dns_fqdn": "The reverse of the fully qualified domain name. Optional.",
"label": "Label by which the resource will be identified by. Optional.",
"description": "Brief description of the resource. Optional.",
// hosted service descriptions:
"ephemeral_contents": "Sets whether the associated contents of this resource should also be\n" +
"deleted upon this resource's deletion. Default is false.",
// instance descriptions:
"image": "The image the new VM will be booted from. Mandatory.",
"size": "The size in GB of the disk to be created. Mandatory.",
"os_type": "The OS type of the VM. Either Windows or Linux. Mandatory.",
"storage_account": "The storage account (pool) name. Mandatory.",
"storage_container": "The storage container name from the storage pool given with 'storage_pool'.",
"user_name": "The user name to be configured on the new VM.",
"user_password": "The user password to be configured on the new VM.",
"default_certificate_thumbprint": "The thumbprint of the WinRM Certificate to be used as a default.",
// local network descriptions:
"vpn_gateway_address": "The IP address of the VPN gateway bridged through this virtual network.",
"address_space_prefixes": "List of address space prefixes in the format '<IP>/netmask'",
// dns descriptions:
"dns_address": "Address of the DNS server. Required.",
}

View File

@ -32,10 +32,18 @@ func Provider() terraform.ResourceProvider {
},
ResourcesMap: map[string]*schema.Resource{
"azure_data_disk": resourceAzureDataDisk(),
"azure_instance": resourceAzureInstance(),
"azure_security_group": resourceAzureSecurityGroup(),
"azure_virtual_network": resourceAzureVirtualNetwork(),
"azure_instance": resourceAzureInstance(),
"azure_data_disk": resourceAzureDataDisk(),
"azure_hosted_service": resourceAzureHostedService(),
"azure_storage_service": resourceAzureStorageService(),
"azure_storage_container": resourceAzureStorageContainer(),
"azure_storage_blob": resourceAzureStorageBlob(),
"azure_storage_queue": resourceAzureStorageQueue(),
"azure_virtual_network": resourceAzureVirtualNetwork(),
"azure_dns_server": resourceAzureDnsServer(),
"azure_local_network_connection": resourceAzureLocalNetworkConnection(),
"azure_security_group": resourceAzureSecurityGroup(),
"azure_security_group_rule": resourceAzureSecurityGroupRule(),
},
ConfigureFunc: providerConfigure,

View File

@ -11,6 +11,18 @@ import (
var testAccProviders map[string]terraform.ResourceProvider
var testAccProvider *schema.Provider
const testAccSecurityGroupName = "terraform-security-group"
// 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() {
testAccProvider = Provider().(*schema.Provider)
testAccProviders = map[string]terraform.ResourceProvider{

View File

@ -5,9 +5,9 @@ import (
"log"
"time"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/virtualmachinedisk"
"github.com/hashicorp/terraform/helper/schema"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualmachinedisk"
)
const dataDiskBlobStorageURL = "http://%s.blob.core.windows.net/disks/%s.vhd"
@ -50,7 +50,7 @@ func resourceAzureDataDisk() *schema.Resource {
Default: "None",
},
"storage": &schema.Schema{
"storage_service_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
@ -321,7 +321,7 @@ func mediaLink(d *schema.ResourceData) string {
name = fmt.Sprintf("%s-%d", d.Get("virtual_machine").(string), d.Get("lun").(int))
}
return fmt.Sprintf(dataDiskBlobStorageURL, d.Get("storage").(string), name.(string))
return fmt.Sprintf(dataDiskBlobStorageURL, d.Get("storage_service_name").(string), name.(string))
}
func verifyDataDiskParameters(d *schema.ResourceData) error {
@ -332,7 +332,7 @@ func verifyDataDiskParameters(d *schema.ResourceData) error {
}
if _, ok := d.GetOk("media_link"); !ok {
if _, ok := d.GetOk("storage"); !ok {
if _, ok := d.GetOk("storage_service_name"); !ok {
return fmt.Errorf("If not supplying 'media_link', you must supply 'storage'.")
}
}

View File

@ -2,14 +2,13 @@ package azure
import (
"fmt"
"os"
"strconv"
"testing"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/virtualmachinedisk"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualmachinedisk"
)
func TestAccAzureDataDisk_basic(t *testing.T) {
@ -174,7 +173,7 @@ resource "azure_instance" "foo" {
name = "terraform-test"
image = "Ubuntu Server 14.04 LTS"
size = "Basic_A1"
storage = "%s"
storage_service_name = "%s"
location = "West US"
username = "terraform"
password = "Pass!admin123"
@ -183,16 +182,16 @@ resource "azure_instance" "foo" {
resource "azure_data_disk" "foo" {
lun = 0
size = 10
storage = "${azure_instance.foo.storage}"
storage_service_name = "${azure_instance.foo.storage_service_name}"
virtual_machine = "${azure_instance.foo.id}"
}`, os.Getenv("AZURE_STORAGE"))
}`, testAccStorageServiceName)
var testAccAzureDataDisk_advanced = fmt.Sprintf(`
resource "azure_instance" "foo" {
name = "terraform-test1"
image = "Ubuntu Server 14.04 LTS"
size = "Basic_A1"
storage = "%s"
storage_service_name = "%s"
location = "West US"
username = "terraform"
password = "Pass!admin123"
@ -202,16 +201,16 @@ resource "azure_data_disk" "foo" {
lun = 1
size = 10
caching = "ReadOnly"
storage = "${azure_instance.foo.storage}"
storage_service_name = "${azure_instance.foo.storage_service_name}"
virtual_machine = "${azure_instance.foo.id}"
}`, os.Getenv("AZURE_STORAGE"))
}`, testAccStorageServiceName)
var testAccAzureDataDisk_update = fmt.Sprintf(`
resource "azure_instance" "foo" {
name = "terraform-test1"
image = "Ubuntu Server 14.04 LTS"
size = "Basic_A1"
storage = "%s"
storage_service_name = "%s"
location = "West US"
username = "terraform"
password = "Pass!admin123"
@ -221,7 +220,7 @@ resource "azure_instance" "bar" {
name = "terraform-test2"
image = "Ubuntu Server 14.04 LTS"
size = "Basic_A1"
storage = "${azure_instance.foo.storage}"
storage_service_name = "${azure_instance.foo.storage_service_name}"
location = "West US"
username = "terraform"
password = "Pass!admin123"
@ -231,6 +230,6 @@ resource "azure_data_disk" "foo" {
lun = 2
size = 20
caching = "ReadWrite"
storage = "${azure_instance.bar.storage}"
storage_service_name = "${azure_instance.bar.storage_service_name}"
virtual_machine = "${azure_instance.bar.id}"
}`, os.Getenv("AZURE_STORAGE"))
}`, testAccStorageServiceName)

View File

@ -0,0 +1,246 @@
package azure
import (
"fmt"
"log"
"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureDnsServer returns the *schema.Resource associated
// to an Azure hosted service.
func resourceAzureDnsServer() *schema.Resource {
return &schema.Resource{
Create: resourceAzureDnsServerCreate,
Read: resourceAzureDnsServerRead,
Update: resourceAzureDnsServerUpdate,
Exists: resourceAzureDnsServerExists,
Delete: resourceAzureDnsServerDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Required: true,
Description: parameterDescriptions["name"],
},
"dns_address": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["dns_address"],
},
},
}
}
// resourceAzureDnsServerCreate does all the necessary API calls
// to create a new DNS server definition on Azure.
func resourceAzureDnsServerCreate(d *schema.ResourceData, meta interface{}) error {
// first; check for the existence of the resource:
exists, err := resourceAzureDnsServerExists(d, meta)
if err != nil {
return err
}
if exists {
return fmt.Errorf("Azure DNS server definition already exists.")
}
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
azureClient.mutex.Lock()
defer azureClient.mutex.Unlock()
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
log.Println("[DEBUG] Adding new DNS server definition to Azure.")
name := d.Get("name").(string)
address := d.Get("dns_address").(string)
netConf.Configuration.DNS.DNSServers = append(
netConf.Configuration.DNS.DNSServers,
virtualnetwork.DNSServer{
Name: name,
IPAddress: address,
})
// send the configuration back to Azure:
log.Println("[INFO] Sending updated network configuration back to Azure.")
reqID, err := networkClient.SetVirtualNetworkConfiguration(netConf)
if err != nil {
return fmt.Errorf("Failed issuing update to network configuration: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error setting network configuration: %s", err)
}
d.SetId(name)
return nil
}
// resourceAzureDnsServerRead does all the necessary API calls to read
// the state of the DNS server off Azure.
func resourceAzureDnsServerRead(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
var found bool
name := d.Get("name").(string)
// search for our DNS and update it if the IP has been changed:
for _, dns := range netConf.Configuration.DNS.DNSServers {
if dns.Name == name {
found = true
d.Set("dns_address", dns.IPAddress)
break
}
}
// remove the resource from the state if it has been deleted in the meantime:
if !found {
d.SetId("")
}
return nil
}
// resourceAzureDnsServerUpdate does all the necessary API calls
// to update the DNS definition on Azure.
func resourceAzureDnsServerUpdate(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
var found bool
name := d.Get("name").(string)
if d.HasChange("dns_address") {
log.Println("[DEBUG] DNS server address has changes; updating it on Azure.")
log.Println("[INFO] Fetching current network configuration from Azure.")
azureClient.mutex.Lock()
defer azureClient.mutex.Unlock()
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
// search for our DNS and update its address value:
for i, dns := range netConf.Configuration.DNS.DNSServers {
if dns.Name == name {
found = true
netConf.Configuration.DNS.DNSServers[i].IPAddress = d.Get("dns_address").(string)
break
}
}
// if the config has changes, send the configuration back to Azure:
if found {
log.Println("[INFO] Sending updated network configuration back to Azure.")
reqID, err := networkClient.SetVirtualNetworkConfiguration(netConf)
if err != nil {
return fmt.Errorf("Failed issuing update to network configuration: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error setting network configuration: %s", err)
}
return nil
}
}
// remove the resource from the state if it has been deleted in the meantime:
if !found {
d.SetId("")
}
return nil
}
// resourceAzureDnsServerExists does all the necessary API calls to
// check if the DNS server definition alredy exists on Azure.
func resourceAzureDnsServerExists(d *schema.ResourceData, meta interface{}) (bool, error) {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return false, fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
name := d.Get("name").(string)
// search for the DNS server's definition:
for _, dns := range netConf.Configuration.DNS.DNSServers {
if dns.Name == name {
return true, nil
}
}
// if we reached this point; the resource must have been deleted; and we must untrack it:
d.SetId("")
return false, nil
}
// resourceAzureDnsServerDelete does all the necessary API calls
// to delete the DNS server definition from Azure.
func resourceAzureDnsServerDelete(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
azureClient.mutex.Lock()
defer azureClient.mutex.Unlock()
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
name := d.Get("name").(string)
// search for the DNS server's definition and remove it:
var found bool
for i, dns := range netConf.Configuration.DNS.DNSServers {
if dns.Name == name {
found = true
netConf.Configuration.DNS.DNSServers = append(
netConf.Configuration.DNS.DNSServers[:i],
netConf.Configuration.DNS.DNSServers[i+1:]...,
)
break
}
}
// if not found; don't bother re-sending the natwork config:
if !found {
return nil
}
// send the configuration back to Azure:
log.Println("[INFO] Sending updated network configuration back to Azure.")
reqID, err := networkClient.SetVirtualNetworkConfiguration(netConf)
if err != nil {
return fmt.Errorf("Failed issuing update to network configuration: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error setting network configuration: %s", err)
}
return nil
}

View File

@ -0,0 +1,128 @@
package azure
import (
"fmt"
"testing"
"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureDnsServerBasic(t *testing.T) {
name := "azure_dns_server.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureDnsServerDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureDnsServerBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureDnsServerExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-dns-server"),
resource.TestCheckResourceAttr(name, "dns_address", "8.8.8.8"),
),
},
},
})
}
func TestAccAzureDnsServerUpdate(t *testing.T) {
name := "azure_dns_server.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureDnsServerDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureDnsServerBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureDnsServerExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-dns-server"),
resource.TestCheckResourceAttr(name, "dns_address", "8.8.8.8"),
),
},
resource.TestStep{
Config: testAccAzureDnsServerUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureDnsServerExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-dns-server"),
resource.TestCheckResourceAttr(name, "dns_address", "8.8.4.4"),
),
},
},
})
}
func testAccCheckAzureDnsServerExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Resource not found: %s", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("No DNS Server ID set.")
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
netConf, err := virtualnetwork.NewClient(mgmtClient).GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed fetching networking configuration: %s", err)
}
for _, dns := range netConf.Configuration.DNS.DNSServers {
if dns.Name == resource.Primary.ID {
return nil
}
}
return fmt.Errorf("Azure DNS Server not found.")
}
}
func testAccCheckAzureDnsServerDestroy(s *terraform.State) error {
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_dns_server" {
continue
}
if resource.Primary.ID == "" {
return fmt.Errorf("No DNS Server ID is set.")
}
networkClient := virtualnetwork.NewClient(mgmtClient)
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Error retrieving networking configuration from Azure: %s", err)
}
for _, dns := range netConf.Configuration.DNS.DNSServers {
if dns.Name == resource.Primary.ID {
return fmt.Errorf("Azure DNS Server still exists.")
}
}
}
return nil
}
const testAccAzureDnsServerBasic = `
resource "azure_dns_server" "foo" {
name = "terraform-dns-server"
dns_address = "8.8.8.8"
}
`
const testAccAzureDnsServerUpdate = `
resource "azure_dns_server" "foo" {
name = "terraform-dns-server"
dns_address = "8.8.4.4"
}
`

View File

@ -0,0 +1,171 @@
package azure
import (
"encoding/base64"
"fmt"
"log"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/hostedservice"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureHostedService returns the schema.Resource associated to an
// Azure hosted service.
func resourceAzureHostedService() *schema.Resource {
return &schema.Resource{
Create: resourceAzureHostedServiceCreate,
Read: resourceAzureHostedServiceRead,
Update: resourceAzureHostedServiceUpdate,
Delete: resourceAzureHostedServiceDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["name"],
},
"location": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["location"],
},
"ephemeral_contents": &schema.Schema{
Type: schema.TypeBool,
Required: true,
Description: parameterDescriptions["ephemeral_contents"],
},
"url": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"status": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"reverse_dns_fqdn": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: parameterDescriptions["reverse_dns_fqdn"],
},
"label": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Default: "Made by Terraform.",
Description: parameterDescriptions["label"],
},
"description": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Optional: true,
Description: parameterDescriptions["description"],
},
"default_certificate_thumbprint": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Optional: true,
Description: parameterDescriptions["default_certificate_thumbprint"],
},
},
}
}
// resourceAzureHostedServiceCreate does all the necessary API calls
// to create a hosted service on Azure.
func resourceAzureHostedServiceCreate(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
hostedServiceClient := hostedservice.NewClient(mgmtClient)
serviceName := d.Get("name").(string)
location := d.Get("location").(string)
reverseDNS := d.Get("reverse_dns_fqdn").(string)
description := d.Get("description").(string)
label := base64.StdEncoding.EncodeToString([]byte(d.Get("label").(string)))
err := hostedServiceClient.CreateHostedService(
hostedservice.CreateHostedServiceParameters{
ServiceName: serviceName,
Location: location,
Label: label,
Description: description,
ReverseDNSFqdn: reverseDNS,
},
)
if err != nil {
return fmt.Errorf("Failed defining new Azure hosted service: %s", err)
}
d.SetId(serviceName)
return nil
}
// resourceAzureHostedServiceRead does all the necessary API calls
// to read the state of a hosted service from Azure.
func resourceAzureHostedServiceRead(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
hostedServiceClient := hostedservice.NewClient(azureClient.mgmtClient)
log.Println("[INFO] Querying for hosted service info.")
serviceName := d.Get("name").(string)
hostedService, err := hostedServiceClient.GetHostedService(serviceName)
if err != nil {
if management.IsResourceNotFoundError(err) {
// it means the hosted service was deleted in the meantime,
// so we must remove it here:
d.SetId("")
return nil
} else {
return fmt.Errorf("Failed to get hosted service: %s", err)
}
}
log.Println("[DEBUG] Reading hosted service query result data.")
d.Set("name", hostedService.ServiceName)
d.Set("url", hostedService.URL)
d.Set("location", hostedService.Location)
d.Set("description", hostedService.Description)
d.Set("label", hostedService.Label)
d.Set("status", hostedService.Status)
d.Set("reverse_dns_fqdn", hostedService.ReverseDNSFqdn)
d.Set("default_certificate_thumbprint", hostedService.DefaultWinRmCertificateThumbprint)
return nil
}
// resourceAzureHostedServiceUpdate does all the necessary API calls to
// update some settings of a hosted service on Azure.
func resourceAzureHostedServiceUpdate(d *schema.ResourceData, meta interface{}) error {
// NOTE: although no-op; this is still required in order for updates to
// ephemeral_contents to be possible.
// check if the service still exists:
return resourceAzureHostedServiceRead(d, meta)
}
// resourceAzureHostedServiceDelete does all the necessary API calls to
// delete a hosted service from Azure.
func resourceAzureHostedServiceDelete(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
hostedServiceClient := hostedservice.NewClient(mgmtClient)
log.Println("[INFO] Issuing hosted service deletion.")
serviceName := d.Get("name").(string)
ephemeral := d.Get("ephemeral_contents").(bool)
reqID, err := hostedServiceClient.DeleteHostedService(serviceName, ephemeral)
if err != nil {
return fmt.Errorf("Failed issuing hosted service deletion request: %s", err)
}
log.Println("[DEBUG] Awaiting confirmation on hosted service deletion.")
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error on hosted service deletion: %s", err)
}
return nil
}

View File

@ -0,0 +1,124 @@
package azure
import (
"fmt"
"testing"
"github.com/Azure/azure-sdk-for-go/management/hostedservice"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureHostedServiceBasic(t *testing.T) {
name := "azure_hosted_service.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureHostedServiceDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureHostedServiceBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureHostedServiceExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-testing-service"),
resource.TestCheckResourceAttr(name, "location", "North Europe"),
resource.TestCheckResourceAttr(name, "ephemeral_contents", "false"),
resource.TestCheckResourceAttr(name, "description", "very discriptive"),
resource.TestCheckResourceAttr(name, "label", "very identifiable"),
),
},
},
})
}
func TestAccAzureHostedServiceUpdate(t *testing.T) {
name := "azure_hosted_service.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureHostedServiceDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureHostedServiceBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureHostedServiceExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-testing-service"),
resource.TestCheckResourceAttr(name, "location", "North Europe"),
resource.TestCheckResourceAttr(name, "ephemeral_contents", "false"),
resource.TestCheckResourceAttr(name, "description", "very discriptive"),
resource.TestCheckResourceAttr(name, "label", "very identifiable"),
),
},
resource.TestStep{
Config: testAccAzureHostedServiceUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureHostedServiceExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-testing-service"),
resource.TestCheckResourceAttr(name, "location", "North Europe"),
resource.TestCheckResourceAttr(name, "ephemeral_contents", "true"),
resource.TestCheckResourceAttr(name, "description", "very discriptive"),
resource.TestCheckResourceAttr(name, "label", "very identifiable"),
),
},
},
})
}
func testAccCheckAzureHostedServiceExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Hosted Service resource not found.")
}
if resource.Primary.ID == "" {
return fmt.Errorf("Resource's ID is not set.")
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
_, err := hostedservice.NewClient(mgmtClient).GetHostedService(resource.Primary.ID)
return err
}
}
func testAccCheckAzureHostedServiceDestroyed(s *terraform.State) error {
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_hosted_service" {
continue
}
if resource.Primary.ID == "" {
return fmt.Errorf("No Azure Hosted Service Resource found.")
}
_, err := hostedservice.NewClient(mgmtClient).GetHostedService(resource.Primary.ID)
return testAccResourceDestroyedErrorFilter("Hosted Service", err)
}
return nil
}
const testAccAzureHostedServiceBasic = `
resource "azure_hosted_service" "foo" {
name = "terraform-testing-service"
location = "North Europe"
ephemeral_contents = false
description = "very discriptive"
label = "very identifiable"
}
`
const testAccAzureHostedServiceUpdate = `
resource "azure_hosted_service" "foo" {
name = "terraform-testing-service"
location = "North Europe"
ephemeral_contents = true
description = "very discriptive"
label = "very identifiable"
}
`

View File

@ -7,14 +7,14 @@ import (
"log"
"strings"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/hostedservice"
"github.com/Azure/azure-sdk-for-go/management/osimage"
"github.com/Azure/azure-sdk-for-go/management/virtualmachine"
"github.com/Azure/azure-sdk-for-go/management/virtualmachineimage"
"github.com/Azure/azure-sdk-for-go/management/vmutils"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/hostedservice"
"github.com/svanharmelen/azure-sdk-for-go/management/osimage"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualmachine"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualmachineimage"
"github.com/svanharmelen/azure-sdk-for-go/management/vmutils"
)
const (
@ -68,7 +68,7 @@ func resourceAzureInstance() *schema.Resource {
ForceNew: true,
},
"storage": &schema.Schema{
"storage_service_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
@ -183,7 +183,7 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
mc,
d.Get("image").(string),
name,
d.Get("storage").(string),
d.Get("storage_service_name").(string),
)
if err != nil {
return err

View File

@ -2,14 +2,13 @@ package azure
import (
"fmt"
"os"
"testing"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/hostedservice"
"github.com/Azure/azure-sdk-for-go/management/virtualmachine"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/hostedservice"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualmachine"
)
func TestAccAzureInstance_basic(t *testing.T) {
@ -313,7 +312,7 @@ resource "azure_instance" "foo" {
name = "terraform-test"
image = "Ubuntu Server 14.04 LTS"
size = "Basic_A1"
storage = "%s"
storage_service_name = "%s"
location = "West US"
username = "terraform"
password = "Pass!admin123"
@ -324,7 +323,7 @@ resource "azure_instance" "foo" {
public_port = 22
private_port = 22
}
}`, os.Getenv("AZURE_STORAGE"))
}`, testAccStorageServiceName)
var testAccAzureInstance_advanced = fmt.Sprintf(`
resource "azure_virtual_network" "foo" {
@ -346,23 +345,26 @@ resource "azure_virtual_network" "foo" {
resource "azure_security_group" "foo" {
name = "terraform-security-group1"
location = "West US"
}
rule {
name = "rdp"
priority = 101
source_cidr = "*"
source_port = "*"
destination_cidr = "*"
destination_port = 3389
protocol = "TCP"
}
resource "azure_security_group_rule" "foo" {
name = "rdp"
security_group_name = "${azure_security_group.foo.name}"
priority = 101
source_address_prefix = "*"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "3389"
action = "Deny"
type = "Inbound"
protocol = "TCP"
}
resource "azure_instance" "foo" {
name = "terraform-test1"
image = "Windows Server 2012 R2 Datacenter, April 2015"
size = "Basic_A1"
storage = "%s"
storage_service_name = "%s"
location = "West US"
time_zone = "America/Los_Angeles"
subnet = "subnet1"
@ -377,7 +379,7 @@ resource "azure_instance" "foo" {
public_port = 3389
private_port = 3389
}
}`, os.Getenv("AZURE_STORAGE"))
}`, testAccStorageServiceName)
var testAccAzureInstance_update = fmt.Sprintf(`
resource "azure_virtual_network" "foo" {
@ -385,52 +387,58 @@ resource "azure_virtual_network" "foo" {
address_space = ["10.1.2.0/24"]
location = "West US"
subnet {
subnet {
name = "subnet1"
address_prefix = "10.1.2.0/25"
}
address_prefix = "10.1.2.0/25"
}
subnet {
subnet {
name = "subnet2"
address_prefix = "10.1.2.128/25"
address_prefix = "10.1.2.128/25"
}
}
resource "azure_security_group" "foo" {
name = "terraform-security-group1"
location = "West US"
}
rule {
name = "rdp"
priority = 101
source_cidr = "*"
source_port = "*"
destination_cidr = "*"
destination_port = 3389
protocol = "TCP"
}
resource "azure_security_group_rule" "foo" {
name = "rdp"
security_group_name = "${azure_security_group.foo.name}"
priority = 101
source_address_prefix = "*"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "3389"
type = "Inbound"
action = "Deny"
protocol = "TCP"
}
resource "azure_security_group" "bar" {
name = "terraform-security-group2"
location = "West US"
}
rule {
name = "rdp"
priority = 101
source_cidr = "192.168.0.0/24"
source_port = "*"
destination_cidr = "*"
destination_port = 3389
protocol = "TCP"
}
resource "azure_security_group_rule" "bar" {
name = "rdp"
security_group_name = "${azure_security_group.bar.name}"
priority = 101
source_address_prefix = "192.168.0.0/24"
source_port_range = "*"
destination_address_prefix = "*"
destination_port_range = "3389"
type = "Inbound"
action = "Deny"
protocol = "TCP"
}
resource "azure_instance" "foo" {
name = "terraform-test1"
image = "Windows Server 2012 R2 Datacenter, April 2015"
size = "Basic_A2"
storage = "%s"
storage_service_name = "%s"
location = "West US"
time_zone = "America/Los_Angeles"
subnet = "subnet1"
@ -452,4 +460,4 @@ resource "azure_instance" "foo" {
public_port = 5985
private_port = 5985
}
}`, os.Getenv("AZURE_STORAGE"))
}`, testAccStorageServiceName)

View File

@ -0,0 +1,250 @@
package azure
import (
"fmt"
"log"
"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureLocalNetworkConnetion returns the schema.Resource associated to an
// Azure hosted service.
func resourceAzureLocalNetworkConnection() *schema.Resource {
return &schema.Resource{
Create: resourceAzureLocalNetworkConnectionCreate,
Read: resourceAzureLocalNetworkConnectionRead,
Update: resourceAzureLocalNetworkConnectionUpdate,
Exists: resourceAzureLocalNetworkConnectionExists,
Delete: resourceAzureLocalNetworkConnectionDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["name"],
},
"vpn_gateway_address": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["vpn_gateway_address"],
},
"address_space_prefixes": &schema.Schema{
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: parameterDescriptions["address_space_prefixes"],
},
},
}
}
// sourceAzureLocalNetworkConnectionCreate issues all the necessary API calls
// to create a virtual network on Azure.
func resourceAzureLocalNetworkConnectionCreate(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
azureClient.mutex.Lock()
defer azureClient.mutex.Unlock()
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
// get provided configuration:
name := d.Get("name").(string)
vpnGateway := d.Get("vpn_gateway_address").(string)
var prefixes []string
for _, prefix := range d.Get("address_space_prefixes").([]interface{}) {
prefixes = append(prefixes, prefix.(string))
}
// add configuration to network config:
netConf.Configuration.LocalNetworkSites = append(netConf.Configuration.LocalNetworkSites,
virtualnetwork.LocalNetworkSite{
Name: name,
VPNGatewayAddress: vpnGateway,
AddressSpace: virtualnetwork.AddressSpace{
AddressPrefix: prefixes,
},
})
// send the configuration back to Azure:
log.Println("[INFO] Sending updated network configuration back to Azure.")
reqID, err := networkClient.SetVirtualNetworkConfiguration(netConf)
if err != nil {
return fmt.Errorf("Failed setting updated network configuration: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Failed updating the network configuration: %s", err)
}
d.SetId(name)
return nil
}
// resourceAzureLocalNetworkConnectionRead does all the necessary API calls to
// read the state of our local natwork from Azure.
func resourceAzureLocalNetworkConnectionRead(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
var found bool
name := d.Get("name").(string)
// browsing for our network config:
for _, lnet := range netConf.Configuration.LocalNetworkSites {
if lnet.Name == name {
found = true
d.Set("vpn_gateway_address", lnet.VPNGatewayAddress)
d.Set("address_space_prefixes", lnet.AddressSpace.AddressPrefix)
break
}
}
// remove the resource from the state of it has been deleted in the meantime:
if !found {
log.Println(fmt.Printf("[INFO] Azure local network '%s' has been deleted remotely. Removimg from Terraform.", name))
d.SetId("")
}
return nil
}
// resourceAzureLocalNetworkConnectionUpdate does all the necessary API calls
// update the settings of our Local Network on Azure.
func resourceAzureLocalNetworkConnectionUpdate(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
azureClient.mutex.Lock()
defer azureClient.mutex.Unlock()
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
name := d.Get("name").(string)
cvpn := d.HasChange("vpn_gateway_address")
cprefixes := d.HasChange("address_space_prefixes")
var found bool
for i, lnet := range netConf.Configuration.LocalNetworkSites {
if lnet.Name == name {
found = true
if cvpn {
netConf.Configuration.LocalNetworkSites[i].VPNGatewayAddress = d.Get("vpn_gateway_address").(string)
}
if cprefixes {
var prefixes []string
for _, prefix := range d.Get("address_space_prefixes").([]interface{}) {
prefixes = append(prefixes, prefix.(string))
}
netConf.Configuration.LocalNetworkSites[i].AddressSpace.AddressPrefix = prefixes
}
break
}
}
// remove the resource from the state of it has been deleted in the meantime:
if !found {
log.Println(fmt.Printf("[INFO] Azure local network '%s' has been deleted remotely. Removimg from Terraform.", name))
d.SetId("")
} else if cvpn || cprefixes {
// else, send the configuration back to Azure:
log.Println("[INFO] Sending updated network configuration back to Azure.")
reqID, err := networkClient.SetVirtualNetworkConfiguration(netConf)
if err != nil {
return fmt.Errorf("Failed setting updated network configuration: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Failed updating the network configuration: %s", err)
}
}
return nil
}
// resourceAzureLocalNetworkConnectionExists does all the necessary API calls
// to check if the local network already exists on Azure.
func resourceAzureLocalNetworkConnectionExists(d *schema.ResourceData, meta interface{}) (bool, error) {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return false, fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
name := d.Get("name")
for _, lnet := range netConf.Configuration.LocalNetworkSites {
if lnet.Name == name {
return true, nil
}
}
return false, nil
}
// resourceAzureLocalNetworkConnectionDelete does all the necessary API calls
// to delete a local network off Azure.
func resourceAzureLocalNetworkConnectionDelete(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
networkClient := virtualnetwork.NewClient(mgmtClient)
log.Println("[INFO] Fetching current network configuration from Azure.")
azureClient.mutex.Lock()
defer azureClient.mutex.Unlock()
netConf, err := networkClient.GetVirtualNetworkConfiguration()
if err != nil {
return fmt.Errorf("Failed to get the current network configuration from Azure: %s", err)
}
name := d.Get("name").(string)
// search for our local network and remove it if found:
for i, lnet := range netConf.Configuration.LocalNetworkSites {
if lnet.Name == name {
netConf.Configuration.LocalNetworkSites = append(
netConf.Configuration.LocalNetworkSites[:i],
netConf.Configuration.LocalNetworkSites[i+1:]...,
)
break
}
}
// send the configuration back to Azure:
log.Println("[INFO] Sending updated network configuration back to Azure.")
reqID, err := networkClient.SetVirtualNetworkConfiguration(netConf)
if err != nil {
return fmt.Errorf("Failed setting updated network configuration: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Failed updating the network configuration: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,140 @@
package azure
import (
"fmt"
"testing"
"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureLocalNetworkConnectionBasic(t *testing.T) {
name := "azure_local_network_connection.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccAzureLocalNetworkConnectionDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureLocalNetworkConnectionBasic,
Check: resource.ComposeTestCheckFunc(
testAccAzureLocalNetworkConnectionExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-local-network-connection"),
resource.TestCheckResourceAttr(name, "vpn_gateway_address", "10.11.12.13"),
resource.TestCheckResourceAttr(name, "address_space_prefixes.0", "10.10.10.0/31"),
resource.TestCheckResourceAttr(name, "address_space_prefixes.1", "10.10.10.1/31"),
),
},
},
})
}
func TestAccAzureLocalNetworkConnectionUpdate(t *testing.T) {
name := "azure_local_network_connection.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccAzureLocalNetworkConnectionDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureLocalNetworkConnectionBasic,
Check: resource.ComposeTestCheckFunc(
testAccAzureLocalNetworkConnectionExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-local-network-connection"),
resource.TestCheckResourceAttr(name, "vpn_gateway_address", "10.11.12.13"),
resource.TestCheckResourceAttr(name, "address_space_prefixes.0", "10.10.10.0/31"),
resource.TestCheckResourceAttr(name, "address_space_prefixes.1", "10.10.10.1/31"),
),
},
resource.TestStep{
Config: testAccAzureLocalNetworkConnectionUpdate,
Check: resource.ComposeTestCheckFunc(
testAccAzureLocalNetworkConnectionExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-local-network-connection"),
resource.TestCheckResourceAttr(name, "vpn_gateway_address", "10.11.12.14"),
resource.TestCheckResourceAttr(name, "address_space_prefixes.0", "10.10.10.2/30"),
resource.TestCheckResourceAttr(name, "address_space_prefixes.1", "10.10.10.3/30"),
),
},
},
})
}
// testAccAzureLocalNetworkConnectionExists checks whether the given local network
// connection exists on Azure.
func testAccAzureLocalNetworkConnectionExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Azure Local Network Connection not found: %s", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Local Network Connection ID not set.")
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
netConf, err := virtualnetwork.NewClient(mgmtClient).GetVirtualNetworkConfiguration()
if err != nil {
return err
}
for _, lnet := range netConf.Configuration.LocalNetworkSites {
if lnet.Name == resource.Primary.ID {
return nil
}
break
}
return fmt.Errorf("Local Network Connection not found: %s", name)
}
}
// testAccAzureLocalNetworkConnectionDestroyed checks whether the local network
// connection has been destroyed on Azure or not.
func testAccAzureLocalNetworkConnectionDestroyed(s *terraform.State) error {
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_local_network_connection" {
continue
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Local Network Connection ID not set.")
}
netConf, err := virtualnetwork.NewClient(mgmtClient).GetVirtualNetworkConfiguration()
if err != nil {
return err
}
for _, lnet := range netConf.Configuration.LocalNetworkSites {
if lnet.Name == resource.Primary.ID {
return fmt.Errorf("Azure Local Network Connection still exists.")
}
}
}
return nil
}
const testAccAzureLocalNetworkConnectionBasic = `
resource "azure_local_network_connection" "foo" {
name = "terraform-local-network-connection"
vpn_gateway_address = "10.11.12.13"
address_space_prefixes = ["10.10.10.0/31", "10.10.10.1/31"]
}
`
const testAccAzureLocalNetworkConnectionUpdate = `
resource "azure_local_network_connection" "foo" {
name = "terraform-local-network-connection"
vpn_gateway_address = "10.11.12.14"
address_space_prefixes = ["10.10.10.2/30", "10.10.10.3/30"]
}
`

View File

@ -1,22 +1,18 @@
package azure
import (
"bytes"
"fmt"
"log"
"strconv"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
"github.com/hashicorp/terraform/helper/schema"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/networksecuritygroup"
)
func resourceAzureSecurityGroup() *schema.Resource {
return &schema.Resource{
Create: resourceAzureSecurityGroupCreate,
Read: resourceAzureSecurityGroupRead,
Update: resourceAzureSecurityGroupUpdate,
Delete: resourceAzureSecurityGroupDelete,
Schema: map[string]*schema.Schema{
@ -38,63 +34,6 @@ func resourceAzureSecurityGroup() *schema.Resource {
Required: true,
ForceNew: true,
},
"rule": &schema.Schema{
Type: schema.TypeSet,
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "Inbound",
},
"priority": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"action": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "Allow",
},
"source_cidr": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"source_port": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"destination_cidr": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"destination_port": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "TCP",
},
},
},
Set: resourceAzureSecurityGroupRuleHash,
},
},
}
}
@ -126,70 +65,9 @@ func resourceAzureSecurityGroupCreate(d *schema.ResourceData, meta interface{})
d.SetId(name)
// Create all rules that are configured
if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
// Create an empty schema.Set to hold all rules
rules := &schema.Set{
F: resourceAzureSecurityGroupRuleHash,
}
for _, rule := range rs.List() {
// Create a single rule
err := resourceAzureSecurityGroupRuleCreate(d, meta, rule.(map[string]interface{}))
// We need to update this first to preserve the correct state
rules.Add(rule)
d.Set("rule", rules)
if err != nil {
return err
}
}
}
return resourceAzureSecurityGroupRead(d, meta)
}
func resourceAzureSecurityGroupRuleCreate(
d *schema.ResourceData,
meta interface{},
rule map[string]interface{}) error {
mc := meta.(*Client).mgmtClient
// Make sure all required parameters are there
if err := verifySecurityGroupRuleParams(rule); err != nil {
return err
}
name := rule["name"].(string)
// Create the rule
req, err := networksecuritygroup.NewClient(mc).SetNetworkSecurityGroupRule(d.Id(),
networksecuritygroup.RuleRequest{
Name: name,
Type: networksecuritygroup.RuleType(rule["type"].(string)),
Priority: rule["priority"].(int),
Action: networksecuritygroup.RuleAction(rule["action"].(string)),
SourceAddressPrefix: rule["source_cidr"].(string),
SourcePortRange: rule["source_port"].(string),
DestinationAddressPrefix: rule["destination_cidr"].(string),
DestinationPortRange: rule["destination_port"].(string),
Protocol: networksecuritygroup.RuleProtocol(rule["protocol"].(string)),
},
)
if err != nil {
return fmt.Errorf("Error creating Network Security Group rule %s: %s", name, err)
}
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for Network Security Group rule %s to be created: %s", name, err)
}
return nil
}
func resourceAzureSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
mc := meta.(*Client).mgmtClient
@ -205,70 +83,9 @@ func resourceAzureSecurityGroupRead(d *schema.ResourceData, meta interface{}) er
d.Set("label", sg.Label)
d.Set("location", sg.Location)
// Create an empty schema.Set to hold all rules
rules := &schema.Set{
F: resourceAzureSecurityGroupRuleHash,
}
for _, r := range sg.Rules {
if !r.IsDefault {
rule := map[string]interface{}{
"name": r.Name,
"type": string(r.Type),
"priority": r.Priority,
"action": string(r.Action),
"source_cidr": r.SourceAddressPrefix,
"source_port": r.SourcePortRange,
"destination_cidr": r.DestinationAddressPrefix,
"destination_port": r.DestinationPortRange,
"protocol": string(r.Protocol),
}
rules.Add(rule)
}
}
d.Set("rule", rules)
return nil
}
func resourceAzureSecurityGroupUpdate(d *schema.ResourceData, meta interface{}) error {
// Check if the rule set as a whole has changed
if d.HasChange("rule") {
o, n := d.GetChange("rule")
ors := o.(*schema.Set).Difference(n.(*schema.Set))
nrs := n.(*schema.Set).Difference(o.(*schema.Set))
// Now first loop through all the old rules and delete any obsolete ones
for _, rule := range ors.List() {
// Delete the rule as it no longer exists in the config
err := resourceAzureSecurityGroupRuleDelete(d, meta, rule.(map[string]interface{}))
if err != nil {
return err
}
}
// Make sure we save the state of the currently configured rules
rules := o.(*schema.Set).Intersection(n.(*schema.Set))
d.Set("rule", rules)
// Then loop through al the currently configured rules and create the new ones
for _, rule := range nrs.List() {
err := resourceAzureSecurityGroupRuleCreate(d, meta, rule.(map[string]interface{}))
// We need to update this first to preserve the correct state
rules.Add(rule)
d.Set("rule", rules)
if err != nil {
return err
}
}
}
return resourceAzureSecurityGroupRead(d, meta)
}
func resourceAzureSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
mc := meta.(*Client).mgmtClient
@ -288,66 +105,3 @@ func resourceAzureSecurityGroupDelete(d *schema.ResourceData, meta interface{})
return nil
}
func resourceAzureSecurityGroupRuleDelete(
d *schema.ResourceData, meta interface{}, rule map[string]interface{}) error {
mc := meta.(*Client).mgmtClient
name := rule["name"].(string)
// Delete the rule
req, err := networksecuritygroup.NewClient(mc).DeleteNetworkSecurityGroupRule(d.Id(), name)
if err != nil {
if management.IsResourceNotFoundError(err) {
return nil
}
return fmt.Errorf("Error deleting Network Security Group rule %s: %s", name, err)
}
if err := mc.WaitForOperation(req, nil); err != nil {
return fmt.Errorf(
"Error waiting for Network Security Group rule %s to be deleted: %s", name, err)
}
return nil
}
func resourceAzureSecurityGroupRuleHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf(
"%s-%d-%s-%s-%s-%s-%s-%s",
m["type"].(string),
m["priority"].(int),
m["action"].(string),
m["source_cidr"].(string),
m["source_port"].(string),
m["destination_cidr"].(string),
m["destination_port"].(string),
m["protocol"].(string)))
return hashcode.String(buf.String())
}
func verifySecurityGroupRuleParams(rule map[string]interface{}) error {
typ := rule["type"].(string)
if typ != "Inbound" && typ != "Outbound" {
return fmt.Errorf("Parameter type only accepts 'Inbound' or 'Outbound' as values")
}
action := rule["action"].(string)
if action != "Allow" && action != "Deny" {
return fmt.Errorf("Parameter action only accepts 'Allow' or 'Deny' as values")
}
protocol := rule["protocol"].(string)
if protocol != "TCP" && protocol != "UDP" && protocol != "*" {
_, err := strconv.ParseInt(protocol, 0, 0)
if err != nil {
return fmt.Errorf(
"Parameter type only accepts 'TCP', 'UDP' or '*' as values")
}
}
return nil
}

View File

@ -0,0 +1,315 @@
package azure
import (
"fmt"
"log"
"github.com/Azure/azure-sdk-for-go/management"
netsecgroup "github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureSecurityGroupRule returns the *schema.Resource for
// a network security group rule on Azure.
func resourceAzureSecurityGroupRule() *schema.Resource {
return &schema.Resource{
Create: resourceAzureSecurityGroupRuleCreate,
Read: resourceAzureSecurityGroupRuleRead,
Update: resourceAzureSecurityGroupRuleUpdate,
Exists: resourceAzureSecurityGroupRuleExists,
Delete: resourceAzureSecurityGroupRuleDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["name"],
},
"security_group_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["netsecgroup_secgroup_name"],
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_type"],
},
"priority": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Description: parameterDescriptions["netsecgroup_priority"],
},
"action": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_action"],
},
"source_address_prefix": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_src_addr_prefix"],
},
"source_port_range": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_src_port_range"],
},
"destination_address_prefix": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_dest_addr_prefix"],
},
"destination_port_range": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_dest_port_range"],
},
"protocol": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_protocol"],
},
},
}
}
// resourceAzureSecurityGroupRuleCreate does all the necessary API calls to
// create a new network security group rule on Azure.
func resourceAzureSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
netSecClient := netsecgroup.NewClient(mgmtClient)
// create and configure the RuleResponse:
name := d.Get("name").(string)
rule := netsecgroup.RuleRequest{
Name: name,
Type: netsecgroup.RuleType(d.Get("type").(string)),
Priority: d.Get("priority").(int),
Action: netsecgroup.RuleAction(d.Get("action").(string)),
SourceAddressPrefix: d.Get("source_address_prefix").(string),
SourcePortRange: d.Get("source_port_range").(string),
DestinationAddressPrefix: d.Get("destination_address_prefix").(string),
DestinationPortRange: d.Get("destination_port_range").(string),
Protocol: netsecgroup.RuleProtocol(d.Get("protocol").(string)),
}
// send the create request to Azure:
log.Println("[INFO] Sending network security group rule creation request to Azure.")
reqID, err := netSecClient.SetNetworkSecurityGroupRule(
d.Get("security_group_name").(string),
rule,
)
if err != nil {
return fmt.Errorf("Error sending network security group rule creation request to Azure: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error creating network security group rule on Azure: %s", err)
}
d.SetId(name)
return nil
}
// resourceAzureSecurityGroupRuleRead does all the necessary API calls to
// read the state of a network security group ruke off Azure.
func resourceAzureSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
netSecClient := netsecgroup.NewClient(mgmtClient)
secGroupName := d.Get("security_group_name").(string)
// get info on the network security group and check its rules for this one:
log.Println("[INFO] Sending network security group rule query to Azure.")
secgroup, err := netSecClient.GetNetworkSecurityGroup(secGroupName)
if err != nil {
if !management.IsResourceNotFoundError(err) {
return fmt.Errorf("Error issuing network security group rules query: %s", err)
} else {
// it meants that the network security group this rule belonged to has
// been deleted; so we must remove this resource from the schema:
d.SetId("")
return nil
}
}
// find our security rule:
var found bool
name := d.Get("name").(string)
for _, rule := range secgroup.Rules {
if rule.Name == name {
found = true
log.Println("[DEBUG] Reading state of Azure network security group rule.")
d.Set("type", rule.Type)
d.Set("priority", rule.Priority)
d.Set("action", rule.Action)
d.Set("source_address_prefix", rule.SourceAddressPrefix)
d.Set("source_port_range", rule.SourcePortRange)
d.Set("destination_address_prefix", rule.DestinationAddressPrefix)
d.Set("destination_port_range", rule.DestinationPortRange)
d.Set("protocol", rule.Protocol)
break
}
}
// check if the rule still exists, and is not, remove the resource:
if !found {
d.SetId("")
}
return nil
}
// resourceAzureSecurityGroupRuleUpdate does all the necessary API calls to
// update the state of a network security group ruke off Azure.
func resourceAzureSecurityGroupRuleUpdate(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
netSecClient := netsecgroup.NewClient(mgmtClient)
secGroupName := d.Get("security_group_name").(string)
// get info on the network security group and check its rules for this one:
log.Println("[INFO] Sending network security group rule query for update to Azure.")
secgroup, err := netSecClient.GetNetworkSecurityGroup(secGroupName)
if err != nil {
if !management.IsResourceNotFoundError(err) {
return fmt.Errorf("Error issuing network security group rules query: %s", err)
} else {
// it meants that the network security group this rule belonged to has
// been deleted; so we must remove this resource from the schema:
d.SetId("")
return nil
}
}
// try and find our security group rule:
var found bool
name := d.Get("name").(string)
for _, rule := range secgroup.Rules {
if rule.Name == name {
found = true
}
}
// check is the resource has not been deleted in the meantime:
if !found {
// if not; remove the resource:
d.SetId("")
return nil
}
// else, start building up the rule request struct:
newRule := netsecgroup.RuleRequest{
Name: d.Get("name").(string),
Type: netsecgroup.RuleType(d.Get("type").(string)),
Priority: d.Get("priority").(int),
Action: netsecgroup.RuleAction(d.Get("action").(string)),
SourceAddressPrefix: d.Get("source_address_prefix").(string),
SourcePortRange: d.Get("source_port_range").(string),
DestinationAddressPrefix: d.Get("destination_address_prefix").(string),
DestinationPortRange: d.Get("destination_port_range").(string),
Protocol: netsecgroup.RuleProtocol(d.Get("protocol").(string)),
}
// send the create request to Azure:
log.Println("[INFO] Sending network security group rule update request to Azure.")
reqID, err := netSecClient.SetNetworkSecurityGroupRule(
secGroupName,
newRule,
)
if err != nil {
return fmt.Errorf("Error sending network security group rule update request to Azure: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error updating network security group rule on Azure: %s", err)
}
return nil
}
// resourceAzureSecurityGroupRuleExists does all the necessary API calls to
// check for the existence of the network security group rule on Azure.
func resourceAzureSecurityGroupRuleExists(d *schema.ResourceData, meta interface{}) (bool, error) {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
netSecClient := netsecgroup.NewClient(mgmtClient)
secGroupName := d.Get("security_group_name").(string)
// get info on the network security group and search for our rule:
log.Println("[INFO] Sending network security group rule query for existence check to Azure.")
secgroup, err := netSecClient.GetNetworkSecurityGroup(secGroupName)
if err != nil {
if !management.IsResourceNotFoundError(err) {
return false, fmt.Errorf("Error issuing network security group rules query: %s", err)
} else {
// it meants that the network security group this rule belonged to has
// been deleted; so we must remove this resource from the schema:
d.SetId("")
return false, nil
}
}
// try and find our security group rule:
name := d.Get("name").(string)
for _, rule := range secgroup.Rules {
if rule.Name == name {
return true, nil
}
}
// if here; it means the resource has been deleted in the
// meantime and must be removed from the schema:
d.SetId("")
return false, nil
}
// resourceAzureSecurityGroupRuleDelete does all the necessary API calls to
// delete a network security group rule off Azure.
func resourceAzureSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
netSecClient := netsecgroup.NewClient(mgmtClient)
secGroupName := d.Get("security_group_name").(string)
// get info on the network security group and search for our rule:
log.Println("[INFO] Sending network security group rule query for deletion to Azure.")
secgroup, err := netSecClient.GetNetworkSecurityGroup(secGroupName)
if err != nil {
if management.IsResourceNotFoundError(err) {
// it meants that the network security group this rule belonged to has
// been deleted; so we need do nothing more but stop tracking the resource:
d.SetId("")
return nil
} else {
return fmt.Errorf("Error issuing network security group rules query: %s", err)
}
}
// check is the resource has not been deleted in the meantime:
name := d.Get("name").(string)
for _, rule := range secgroup.Rules {
if rule.Name == name {
// if not; we shall issue the delete:
reqID, err := netSecClient.DeleteNetworkSecurityGroupRule(secGroupName, name)
if err != nil {
return fmt.Errorf("Error sending network security group rule delete request to Azure: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error deleting network security group rule off Azure: %s", err)
}
}
}
return nil
}

View File

@ -0,0 +1,109 @@
package azure
import (
"fmt"
"testing"
netsecgroup "github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureSecurityGroupRule(t *testing.T) {
name := "azure_security_group_rule.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureSecurityGroupRuleDeleted,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureSecurityGroupRule,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureSecurityGroupRuleExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-secgroup-rule"),
resource.TestCheckResourceAttr(name, "security_group_name", testAccSecurityGroupName),
resource.TestCheckResourceAttr(name, "type", "Inbound"),
resource.TestCheckResourceAttr(name, "action", "Deny"),
resource.TestCheckResourceAttr(name, "priority", "200"),
resource.TestCheckResourceAttr(name, "source_address_prefix", "100.0.0.0/32"),
resource.TestCheckResourceAttr(name, "source_port_range", "1000"),
resource.TestCheckResourceAttr(name, "destination_address_prefix", "10.0.0.0/32"),
resource.TestCheckResourceAttr(name, "protocol", "TCP"),
),
},
},
})
}
func testAccCheckAzureSecurityGroupRuleExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Azure security group rule not found: %s", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure network security group rule ID not set: %s", name)
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
secGroupClient := netsecgroup.NewClient(mgmtClient)
secGroup, err := secGroupClient.GetNetworkSecurityGroup(testAccSecurityGroupName)
if err != nil {
return fmt.Errorf("Failed getting network security group details: %s", err)
}
for _, rule := range secGroup.Rules {
if rule.Name == resource.Primary.ID {
return nil
}
}
return fmt.Errorf("Azure security group rule doesn't exist: %s", name)
}
}
func testAccCheckAzureSecurityGroupRuleDeleted(s *terraform.State) error {
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_security_group_rule" {
continue
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure network security group ID not set.")
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
secGroupClient := netsecgroup.NewClient(mgmtClient)
secGroup, err := secGroupClient.GetNetworkSecurityGroup(testAccSecurityGroupName)
if err != nil {
return fmt.Errorf("Failed getting network security group details: %s", err)
}
for _, rule := range secGroup.Rules {
if rule.Name == resource.Primary.ID {
return fmt.Errorf("Azure network security group rule still exists!")
}
}
}
return nil
}
var testAccAzureSecurityGroupRule = testAccAzureSecurityGroupConfig + `
resource "azure_security_group_rule" "foo" {
name = "terraform-secgroup-rule"
security_group_name = "${azure_security_group.foo.name}"
type = "Inbound"
action = "Deny"
priority = 200
source_address_prefix = "100.0.0.0/32"
source_port_range = "1000"
destination_address_prefix = "10.0.0.0/32"
destination_port_range = "1000"
protocol = "TCP"
}
`

View File

@ -4,10 +4,10 @@ import (
"fmt"
"testing"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/networksecuritygroup"
)
func TestAccAzureSecurityGroup_basic(t *testing.T) {
@ -19,72 +19,16 @@ func TestAccAzureSecurityGroup_basic(t *testing.T) {
CheckDestroy: testAccCheckAzureSecurityGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureSecurityGroup_basic,
Config: testAccAzureSecurityGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureSecurityGroupExists(
"azure_security_group.foo", &group),
testAccCheckAzureSecurityGroupBasicAttributes(&group),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "name", "terraform-security-group"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "location", "West US"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.936204579.name", "RDP"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.936204579.source_port", "*"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.936204579.destination_port", "3389"),
),
},
},
})
}
func TestAccAzureSecurityGroup_update(t *testing.T) {
var group networksecuritygroup.SecurityGroupResponse
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureSecurityGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureSecurityGroup_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureSecurityGroupExists(
"azure_security_group.foo", &group),
testAccCheckAzureSecurityGroupBasicAttributes(&group),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "name", "terraform-security-group"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "location", "West US"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.936204579.name", "RDP"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.936204579.source_cidr", "*"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.936204579.destination_port", "3389"),
),
},
resource.TestStep{
Config: testAccAzureSecurityGroup_update,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureSecurityGroupExists(
"azure_security_group.foo", &group),
testAccCheckAzureSecurityGroupUpdatedAttributes(&group),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.3322523298.name", "RDP"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.3322523298.source_cidr", "192.168.0.0/24"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.3322523298.destination_port", "3389"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.3929353075.name", "WINRM"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.3929353075.source_cidr", "192.168.0.0/24"),
resource.TestCheckResourceAttr(
"azure_security_group.foo", "rule.3929353075.destination_port", "5985"),
"azure_security_group.foo", "label", "terraform testing security group"),
),
},
},
@ -120,89 +64,6 @@ func testAccCheckAzureSecurityGroupExists(
}
}
func testAccCheckAzureSecurityGroupBasicAttributes(
group *networksecuritygroup.SecurityGroupResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
if group.Name != "terraform-security-group" {
return fmt.Errorf("Bad name: %s", group.Name)
}
for _, r := range group.Rules {
if !r.IsDefault {
if r.Name != "RDP" {
return fmt.Errorf("Bad rule name: %s", r.Name)
}
if r.Priority != 101 {
return fmt.Errorf("Bad rule priority: %d", r.Priority)
}
if r.SourceAddressPrefix != "*" {
return fmt.Errorf("Bad source CIDR: %s", r.SourceAddressPrefix)
}
if r.DestinationAddressPrefix != "*" {
return fmt.Errorf("Bad destination CIDR: %s", r.DestinationAddressPrefix)
}
if r.DestinationPortRange != "3389" {
return fmt.Errorf("Bad destination port: %s", r.DestinationPortRange)
}
}
}
return nil
}
}
func testAccCheckAzureSecurityGroupUpdatedAttributes(
group *networksecuritygroup.SecurityGroupResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
if group.Name != "terraform-security-group" {
return fmt.Errorf("Bad name: %s", group.Name)
}
foundRDP := false
foundWINRM := false
for _, r := range group.Rules {
if !r.IsDefault {
if r.Name == "RDP" {
if r.SourceAddressPrefix != "192.168.0.0/24" {
return fmt.Errorf("Bad source CIDR: %s", r.SourceAddressPrefix)
}
foundRDP = true
}
if r.Name == "WINRM" {
if r.Priority != 102 {
return fmt.Errorf("Bad rule priority: %d", r.Priority)
}
if r.SourceAddressPrefix != "192.168.0.0/24" {
return fmt.Errorf("Bad source CIDR: %s", r.SourceAddressPrefix)
}
if r.DestinationAddressPrefix != "*" {
return fmt.Errorf("Bad destination CIDR: %s", r.DestinationAddressPrefix)
}
if r.DestinationPortRange != "5985" {
return fmt.Errorf("Bad destination port: %s", r.DestinationPortRange)
}
foundWINRM = true
}
}
}
if !foundRDP {
return fmt.Errorf("RDP rule not found")
}
if !foundWINRM {
return fmt.Errorf("WINRM rule not found")
}
return nil
}
}
func testAccCheckAzureSecurityGroupDestroy(s *terraform.State) error {
mc := testAccProvider.Meta().(*Client).mgmtClient
@ -228,44 +89,9 @@ func testAccCheckAzureSecurityGroupDestroy(s *terraform.State) error {
return nil
}
const testAccAzureSecurityGroup_basic = `
var testAccAzureSecurityGroupConfig = fmt.Sprintf(`
resource "azure_security_group" "foo" {
name = "terraform-security-group"
name = "%s"
location = "West US"
rule {
name = "RDP"
priority = 101
source_cidr = "*"
source_port = "*"
destination_cidr = "*"
destination_port = "3389"
protocol = "TCP"
}
}`
const testAccAzureSecurityGroup_update = `
resource "azure_security_group" "foo" {
name = "terraform-security-group"
location = "West US"
rule {
name = "RDP"
priority = 101
source_cidr = "192.168.0.0/24"
source_port = "*"
destination_cidr = "*"
destination_port = "3389"
protocol = "TCP"
}
rule {
name = "WINRM"
priority = 102
source_cidr = "192.168.0.0/24"
source_port = "*"
destination_cidr = "*"
destination_port = "5985"
protocol = "TCP"
}
}`
label = "terraform testing security group"
}`, testAccSecurityGroupName)

View File

@ -0,0 +1,183 @@
package azure
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureStorageBlob returns the *schema.Resource associated
// with a storage blob on Azure.
func resourceAzureStorageBlob() *schema.Resource {
return &schema.Resource{
Create: resourceAzureStorageBlobCreate,
Read: resourceAzureStorageBlobRead,
Update: resourceAzureStorageBlobUpdate,
Exists: resourceAzureStorageBlobExists,
Delete: resourceAzureStorageBlobDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["name"],
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["type"],
},
"size": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
DefaultFunc: func() (interface{}, error) {
return int64(0), nil
},
},
"storage_container_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["storage_container_name"],
},
"storage_service_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["storage_service_name"],
},
"url": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: parameterDescriptions["url"],
},
},
}
}
// resourceAzureStorageBlobCreate does all the necessary API calls to
// create the storage blob on Azure.
func resourceAzureStorageBlobCreate(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return err
}
log.Println("[INFO] Issuing create on Azure storage blob.")
name := d.Get("name").(string)
blobType := d.Get("type").(string)
cont := d.Get("storage_container_name").(string)
switch blobType {
case "BlockBlob":
err = blobClient.CreateBlockBlob(cont, name)
case "PageBlob":
size := int64(d.Get("size").(int))
err = blobClient.PutPageBlob(cont, name, size)
default:
err = fmt.Errorf("Invalid blob type specified; see parameter desciptions for more info.")
}
if err != nil {
return fmt.Errorf("Error creating storage blob on Azure: %s", err)
}
d.SetId(name)
return resourceAzureStorageBlobRead(d, meta)
}
// resourceAzureStorageBlobRead does all the necessary API calls to
// read the status of the storage blob off Azure.
func resourceAzureStorageBlobRead(d *schema.ResourceData, meta interface{}) error {
// check for it's existence:
exists, err := resourceAzureStorageBlobExists(d, meta)
if err != nil {
return err
}
// if it exists; read relevant information:
if exists {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return err
}
name := d.Get("name").(string)
cont := d.Get("storage_container_name").(string)
url := blobClient.GetBlobURL(cont, name)
d.Set("url", url)
}
// NOTE: no need to unset the ID here, as resourceAzureStorageBlobExists
// already should have done so if it were required.
return nil
}
// resourceAzureStorageBlobUpdate does all the necessary API calls to
// update a blob on Azure.
func resourceAzureStorageBlobUpdate(d *schema.ResourceData, meta interface{}) error {
// NOTE: although empty as most paramters have ForceNew set; this is
// still required in case of changes to the storage_service_key
// run the ExistsFunc beforehand to ensure the resource's existence nonetheless:
_, err := resourceAzureStorageBlobExists(d, meta)
return err
}
// resourceAzureStorageBlobExists does all the necessary API calls to
// check for the existence of the blob on Azure.
func resourceAzureStorageBlobExists(d *schema.ResourceData, meta interface{}) (bool, error) {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return false, err
}
log.Println("[INFO] Querying Azure for storage blob's existence.")
name := d.Get("name").(string)
cont := d.Get("storage_container_name").(string)
exists, err := blobClient.BlobExists(cont, name)
if err != nil {
return false, fmt.Errorf("Error whilst checking for Azure storage blob's existence: %s", err)
}
// if not found; it means it was deleted in the meantime and
// we must remove it from the schema.
if !exists {
d.SetId("")
}
return exists, nil
}
// resourceAzureStorageBlobDelete does all the necessary API calls to
// delete the blob off Azure.
func resourceAzureStorageBlobDelete(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return err
}
log.Println("[INFO] Issuing storage blob delete command off Azure.")
name := d.Get("name").(string)
cont := d.Get("storage_container_name").(string)
if _, err = blobClient.DeleteBlobIfExists(cont, name); err != nil {
return fmt.Errorf("Error whilst deleting storage blob: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,153 @@
package azure
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureStorageBlockBlob(t *testing.T) {
name := "azure_storage_blob.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureStorageBlobDeleted("block"),
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureStorageBlockBlobConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureStorageBlobExists(name, "block"),
resource.TestCheckResourceAttr(name, "name", "tftesting-blob"),
resource.TestCheckResourceAttr(name, "type", "BlockBlob"),
resource.TestCheckResourceAttr(name, "storage_container_name",
fmt.Sprintf("%s-block", testAccStorageContainerName)),
resource.TestCheckResourceAttr(name, "storage_service_name", testAccStorageServiceName),
),
},
},
})
}
func TestAccAzureStoragePageBlob(t *testing.T) {
name := "azure_storage_blob.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureStorageBlobDeleted("page"),
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureStoragePageBlobConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureStorageBlobExists(name, "page"),
resource.TestCheckResourceAttr(name, "name", "tftesting-blob"),
resource.TestCheckResourceAttr(name, "type", "PageBlob"),
resource.TestCheckResourceAttr(name, "size", "512"),
resource.TestCheckResourceAttr(name, "storage_container_name",
fmt.Sprintf("%s-page", testAccStorageContainerName)),
resource.TestCheckResourceAttr(name, "storage_service_name", testAccStorageServiceName),
),
},
},
})
}
func testAccCheckAzureStorageBlobExists(name, typ string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Azure Storage Container resource not found: %s", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Storage Container ID not set: %s", name)
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
blobClient, err := getStorageServiceBlobClient(mgmtClient, testAccStorageServiceName)
if err != nil {
return err
}
exists, err := blobClient.BlobExists(fmt.Sprintf("%s-%s", testAccStorageContainerName, typ),
resource.Primary.ID)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("Azure Storage Blob %s doesn't exist.", name)
}
return nil
}
}
func testAccCheckAzureStorageBlobDeleted(typ string) resource.TestCheckFunc {
return func(s *terraform.State) error {
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_storage_blob" {
continue
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
blobClient, err := getStorageServiceBlobClient(mgmtClient, testAccStorageServiceName)
if err != nil {
return err
}
exists, err := blobClient.BlobExists(fmt.Sprintf("%s-%s", testAccStorageContainerName,
typ), resource.Primary.ID)
if err != nil {
return err
}
if exists {
return fmt.Errorf("Azure Storage Blob still exists.")
}
}
return nil
}
}
var testAccAzureStorageBlockBlobConfig = fmt.Sprintf(`
resource "azure_storage_container" "foo" {
name = "%s-block"
container_access_type = "blob"
# NOTE: A pre-existing Storage Service is used here so as to avoid
# the huge wait for creation of one.
storage_service_name = "%s"
}
resource "azure_storage_blob" "foo" {
name = "tftesting-blob"
type = "BlockBlob"
# NOTE: A pre-existing Storage Service is used here so as to avoid
# the huge wait for creation of one.
storage_service_name = "${azure_storage_container.foo.storage_service_name}"
storage_container_name = "${azure_storage_container.foo.name}"
}
`, testAccStorageContainerName, testAccStorageServiceName)
var testAccAzureStoragePageBlobConfig = fmt.Sprintf(`
resource "azure_storage_container" "foo" {
name = "%s-page"
container_access_type = "blob"
# NOTE: A pre-existing Storage Service is used here so as to avoid
# the huge wait for creation of one.
storage_service_name = "%s"
}
resource "azure_storage_blob" "foo" {
name = "tftesting-blob"
type = "PageBlob"
# NOTE: A pre-existing Storage Service is used here so as to avoid
# the huge wait for creation of one.
storage_service_name = "${azure_storage_container.foo.storage_service_name}"
storage_container_name = "${azure_storage_container.foo.name}"
# NOTE: must be a multiple of 512:
size = 512
}
`, testAccStorageContainerName, testAccStorageServiceName)

View File

@ -0,0 +1,163 @@
package azure
import (
"fmt"
"log"
"github.com/Azure/azure-sdk-for-go/storage"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureStorageContainer returns the *schema.Resource associated
// to a storage container on Azure.
func resourceAzureStorageContainer() *schema.Resource {
return &schema.Resource{
Create: resourceAzureStorageContainerCreate,
Read: resourceAzureStorageContainerRead,
Exists: resourceAzureStorageContainerExists,
Delete: resourceAzureStorageContainerDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["name"],
},
"storage_service_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["storage_service_name"],
},
"container_access_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["container_access_type"],
},
"properties": &schema.Schema{
Type: schema.TypeMap,
Computed: true,
Elem: schema.TypeString,
Description: parameterDescriptions["properties"],
},
},
}
}
// resourceAzureStorageContainerCreate does all the necessary API calls to
// create the storage container on Azure.
func resourceAzureStorageContainerCreate(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return err
}
log.Println("[INFO] Creating storage container on Azure.")
name := d.Get("name").(string)
accessType := storage.ContainerAccessType(d.Get("container_access_type").(string))
err = blobClient.CreateContainer(name, accessType)
if err != nil {
return fmt.Errorf("Failed to create storage container on Azure: %s", err)
}
d.SetId(name)
return resourceAzureStorageContainerRead(d, meta)
}
// resourceAzureStorageContainerRead does all the necessary API calls to
// read the status of the storage container off Azure.
func resourceAzureStorageContainerRead(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return err
}
log.Println("[INFO] Querying Azure for storage containers.")
name := d.Get("name").(string)
containers, err := blobClient.ListContainers(storage.ListContainersParameters{
Prefix: name,
Timeout: 90,
})
if err != nil {
return fmt.Errorf("Failed to query Azure for its storage containers: %s", err)
}
// search for our storage container and update its stats:
var found bool
// loop just to make sure we got the right container:
for _, cont := range containers.Containers {
if cont.Name == name {
found = true
props := make(map[string]interface{})
props["last_modified"] = cont.Properties.LastModified
props["lease_status"] = cont.Properties.LeaseStatus
props["lease_state"] = cont.Properties.LeaseState
props["lease_duration"] = cont.Properties.LeaseDuration
d.Set("properties", props)
}
}
// if not found; it means the resource has been deleted
// in the meantime; so we must untrack it:
if !found {
d.SetId("")
}
return nil
}
// resourceAzureStorageContainerExists does all the necessary API calls to
// check if the storage container already exists on Azure.
func resourceAzureStorageContainerExists(d *schema.ResourceData, meta interface{}) (bool, error) {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return false, err
}
log.Println("[INFO] Checking existence of storage container on Azure.")
name := d.Get("name").(string)
exists, err := blobClient.ContainerExists(name)
if err != nil {
return false, fmt.Errorf("Failed to query for Azure storage container existence: %s", err)
}
// if it does not exist; untrack the resource:
if !exists {
d.SetId("")
}
return exists, nil
}
// resourceAzureStorageContainerDelete does all the necessary API calls to
// delete a storage container off Azure.
func resourceAzureStorageContainerDelete(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storName := d.Get("storage_service_name").(string)
blobClient, err := getStorageServiceBlobClient(mgmtClient, storName)
if err != nil {
return err
}
log.Println("[INFO] Issuing Azure storage container deletion call.")
name := d.Get("name").(string)
if _, err := blobClient.DeleteContainerIfExists(name); err != nil {
return fmt.Errorf("Failed deleting storage container off Azure: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,97 @@
package azure
import (
"fmt"
"testing"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureStorageContainer(t *testing.T) {
name := "azure_storage_container.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureStorageContainerDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureStorageContainerConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureStorageContainerExists(name),
resource.TestCheckResourceAttr(name, "name", testAccStorageContainerName),
resource.TestCheckResourceAttr(name, "storage_service_name", testAccStorageServiceName),
resource.TestCheckResourceAttr(name, "container_access_type", "blob"),
),
},
},
})
// because containers take a while to get deleted, sleep for one minute:
time.Sleep(3 * time.Minute)
}
func testAccCheckAzureStorageContainerExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Azure Storage Container resource not found: %s", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Storage Container ID not set: %s", name)
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
blobClient, err := getStorageServiceBlobClient(mgmtClient, testAccStorageServiceName)
if err != nil {
return err
}
exists, err := blobClient.ContainerExists(resource.Primary.ID)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("Azure Storage Container %s doesn't exist.", name)
}
return nil
}
}
func testAccCheckAzureStorageContainerDestroyed(s *terraform.State) error {
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_storage_container" {
continue
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
blobClient, err := getStorageServiceBlobClient(mgmtClient, testAccStorageServiceName)
if err != nil {
return err
}
exists, err := blobClient.ContainerExists(resource.Primary.ID)
if err != nil {
return err
}
if exists {
return fmt.Errorf("Azure Storage Container still exists.")
}
}
return nil
}
var testAccAzureStorageContainerConfig = fmt.Sprintf(`
resource "azure_storage_container" "foo" {
name = "%s"
container_access_type = "blob"
# NOTE: A pre-existing Storage Service is used here so as to avoid
# the huge wait for creation of one.
storage_service_name = "%s"
}
`, testAccStorageContainerName, testAccStorageServiceName)

View File

@ -0,0 +1,103 @@
package azure
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureStorageQueue returns the *schema.Resource associated
// to a storage queue on Azure.
func resourceAzureStorageQueue() *schema.Resource {
return &schema.Resource{
Create: resourceAzureStorageQueueCreate,
Read: resourceAzureStorageQueueRead,
Delete: resourceAzureStorageQueueDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["name"],
},
"storage_service_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["storage_service_name"],
},
},
}
}
// resourceAzureStorageQueueCreate does all the necessary API calls to
// create a storage queue on Azure.
func resourceAzureStorageQueueCreate(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storServName := d.Get("storage_service_name").(string)
queueClient, err := getStorageServiceQueueClient(mgmtClient, storServName)
if err != nil {
return err
}
// create the queue:
log.Println("Sending Storage Queue creation request to Azure.")
name := d.Get("name").(string)
err = queueClient.CreateQueue(name)
if err != nil {
return fmt.Errorf("Error creation Storage Queue on Azure: %s", err)
}
d.SetId(name)
return nil
}
// resourceAzureStorageQueueRead does all the necessary API calls to
// read the state of the storage queue off Azure.
func resourceAzureStorageQueueRead(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storServName := d.Get("storage_service_name").(string)
queueClient, err := getStorageServiceQueueClient(mgmtClient, storServName)
if err != nil {
return err
}
// check for queue's existence:
log.Println("[INFO] Sending Storage Queue existence query to Azure.")
name := d.Get("name").(string)
exists, err := queueClient.QueueExists(name)
if err != nil {
return fmt.Errorf("Error checking for Storage Queue existence: %s", err)
}
// If the queue has been deleted in the meantime;
// untrack the resource from the schema.
if !exists {
d.SetId("")
}
return nil
}
// resourceAzureStorageQueueDelete does all the necessary API calls to
// delete the storage queue off Azure.
func resourceAzureStorageQueueDelete(d *schema.ResourceData, meta interface{}) error {
mgmtClient := meta.(*Client).mgmtClient
storServName := d.Get("storage_service_name").(string)
queueClient, err := getStorageServiceQueueClient(mgmtClient, storServName)
if err != nil {
return err
}
// issue the deletion of the storage queue:
log.Println("[INFO] Sending Storage Queue deletion request to Azure.")
name := d.Get("name").(string)
err = queueClient.DeleteQueue(name)
if err != nil {
return fmt.Errorf("Error deleting Storage queue off Azure: %s", err)
}
return nil
}

View File

@ -0,0 +1,93 @@
package azure
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureStorageQueue(t *testing.T) {
name := "azure_storage_queue.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureStorageQueueDeleted,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureStorageQueueConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureStorageQueueExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-queue"),
resource.TestCheckResourceAttr(name, "storage_service_name", testAccStorageServiceName),
),
},
},
})
}
func testAccCheckAzureStorageQueueExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Azure Storage Queue resource '%s' is missing.", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Storage Service Queue ID %s is missing.", name)
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
queueClient, err := getStorageServiceQueueClient(mgmtClient, testAccStorageServiceName)
if err != nil {
return err
}
exists, err := queueClient.QueueExists(resource.Primary.ID)
if err != nil {
return fmt.Errorf("Error querying Azure for Storage Queue existence: %s", err)
}
if !exists {
return fmt.Errorf("Azure Storage Queue %s doesn't exist!", resource.Primary.ID)
}
return nil
}
}
func testAccCheckAzureStorageQueueDeleted(s *terraform.State) error {
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_storage_queue" {
continue
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Storage Service Queue ID %s is missing.", resource.Primary.ID)
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
queueClient, err := getStorageServiceQueueClient(mgmtClient, testAccStorageServiceName)
if err != nil {
return err
}
exists, err := queueClient.QueueExists(resource.Primary.ID)
if err != nil {
return fmt.Errorf("Error querying Azure for Storage Queue existence: %s", err)
}
if exists {
return fmt.Errorf("Azure Storage Queue %s still exists!", resource.Primary.ID)
}
}
return nil
}
var testAccAzureStorageQueueConfig = fmt.Sprintf(`
resource "azure_storage_queue" "foo" {
name = "terraform-queue"
storage_service_name = "%s"
}
`, testAccStorageServiceName)

View File

@ -0,0 +1,227 @@
package azure
import (
"encoding/base64"
"fmt"
"log"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/storageservice"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureStorageService returns the *schema.Resource associated
// to an Azure hosted service.
func resourceAzureStorageService() *schema.Resource {
return &schema.Resource{
Create: resourceAzureStorageServiceCreate,
Read: resourceAzureStorageServiceRead,
Exists: resourceAzureStorageServiceExists,
Delete: resourceAzureStorageServiceDelete,
Schema: map[string]*schema.Schema{
// General attributes:
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
// TODO(aznashwan): constrain name in description
Description: parameterDescriptions["name"],
},
"location": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["location"],
},
"label": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "Made by Terraform.",
Description: parameterDescriptions["label"],
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: parameterDescriptions["description"],
},
// Functional attributes:
"account_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: parameterDescriptions["account_type"],
},
"affinity_group": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Description: parameterDescriptions["affinity_group"],
},
"properties": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
ForceNew: true,
Elem: schema.TypeString,
},
// Computed attributes:
"url": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"primary_key": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"secondary_key": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
// resourceAzureStorageServiceCreate does all the necessary API calls to
// create a new Azure storage service.
func resourceAzureStorageServiceCreate(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
storageServiceClient := storageservice.NewClient(mgmtClient)
// get all the values:
log.Println("[INFO] Creating Azure Storage Service creation parameters.")
name := d.Get("name").(string)
location := d.Get("location").(string)
accountType := storageservice.AccountType(d.Get("account_type").(string))
affinityGroup := d.Get("affinity_group").(string)
description := d.Get("description").(string)
label := base64.StdEncoding.EncodeToString([]byte(d.Get("label").(string)))
var props []storageservice.ExtendedProperty
if given := d.Get("properties").(map[string]interface{}); len(given) > 0 {
props = []storageservice.ExtendedProperty{}
for k, v := range given {
props = append(props, storageservice.ExtendedProperty{
Name: k,
Value: v.(string),
})
}
}
// create parameters and send request:
log.Println("[INFO] Sending Storage Service creation request to Azure.")
reqID, err := storageServiceClient.CreateStorageService(
storageservice.StorageAccountCreateParameters{
ServiceName: name,
Location: location,
Description: description,
Label: label,
AffinityGroup: affinityGroup,
AccountType: accountType,
ExtendedProperties: storageservice.ExtendedPropertyList{
ExtendedProperty: props,
},
})
if err != nil {
return fmt.Errorf("Failed to create Azure storage service %s: %s", name, err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Failed creating storage service %s: %s", name, err)
}
d.SetId(name)
return resourceAzureStorageServiceRead(d, meta)
}
// resourceAzureStorageServiceRead does all the necessary API calls to
// read the state of the storage service off Azure.
func resourceAzureStorageServiceRead(d *schema.ResourceData, meta interface{}) error {
azureClient := meta.(*Client)
mgmtClient := azureClient.mgmtClient
storageServiceClient := storageservice.NewClient(mgmtClient)
// get our storage service:
log.Println("[INFO] Sending query about storage service to Azure.")
name := d.Get("name").(string)
storsvc, err := storageServiceClient.GetStorageService(name)
if err != nil {
if !management.IsResourceNotFoundError(err) {
return fmt.Errorf("Failed to query about Azure about storage service: %s", err)
} else {
// it means that the resource has been deleted from Azure
// in the meantime and we must remove its associated Resource.
d.SetId("")
return nil
}
}
// read values:
d.Set("url", storsvc.URL)
log.Println("[INFO] Querying keys of Azure storage service.")
keys, err := storageServiceClient.GetStorageServiceKeys(name)
if err != nil {
return fmt.Errorf("Failed querying keys for Azure storage service: %s", err)
}
d.Set("primary_key", keys.PrimaryKey)
d.Set("secondary_key", keys.SecondaryKey)
return nil
}
// resourceAzureStorageServiceExists does all the necessary API calls to
// check if the storage service exists on Azure.
func resourceAzureStorageServiceExists(d *schema.ResourceData, meta interface{}) (bool, error) {
azureClient, ok := meta.(*Client)
if !ok {
return false, fmt.Errorf("Failed to convert to *Client, got: %T", meta)
}
mgmtClient := azureClient.mgmtClient
storageServiceClient := storageservice.NewClient(mgmtClient)
// get our storage service:
log.Println("[INFO] Sending query about storage service to Azure.")
name := d.Get("name").(string)
_, err := storageServiceClient.GetStorageService(name)
if err != nil {
if !management.IsResourceNotFoundError(err) {
return false, fmt.Errorf("Failed to query about Azure about storage service: %s", err)
} else {
// it means that the resource has been deleted from Azure
// in the meantime and we must remove its associated Resource.
d.SetId("")
return false, nil
}
}
return true, nil
}
// resourceAzureStorageServiceDelete does all the necessary API calls to
// delete the storage service off Azure.
func resourceAzureStorageServiceDelete(d *schema.ResourceData, meta interface{}) error {
azureClient, ok := meta.(*Client)
if !ok {
return fmt.Errorf("Failed to convert to *Client, got: %T", meta)
}
mgmtClient := azureClient.mgmtClient
storageClient := storageservice.NewClient(mgmtClient)
// issue the deletion:
name := d.Get("name").(string)
log.Println("[INFO] Issuing delete of storage service off Azure.")
reqID, err := storageClient.DeleteStorageService(name)
if err != nil {
return fmt.Errorf("Error whilst issuing deletion of storage service off Azure: %s", err)
}
err = mgmtClient.WaitForOperation(reqID, nil)
if err != nil {
return fmt.Errorf("Error whilst deleting storage service off Azure: %s", err)
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,50 @@
package azure
import (
"fmt"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/storageservice"
"github.com/Azure/azure-sdk-for-go/storage"
)
// getStorageClientForStorageService is helper function which returns the
// storage.Client associated to the given storage service name.
func getStorageClientForStorageService(mgmtClient management.Client, serviceName string) (storage.Client, error) {
var storageClient storage.Client
storageServiceClient := storageservice.NewClient(mgmtClient)
keys, err := storageServiceClient.GetStorageServiceKeys(serviceName)
if err != nil {
return storageClient, fmt.Errorf("Failed getting Storage Service keys for %s: %s", serviceName, err)
}
storageClient, err = storage.NewBasicClient(serviceName, keys.PrimaryKey)
if err != nil {
return storageClient, fmt.Errorf("Failed creating Storage Service client for %s: %s", serviceName, err)
}
return storageClient, err
}
// getStorageServiceBlobClient is a helper function which returns the
// storage.BlobStorageClient associated to the given storage service name.
func getStorageServiceBlobClient(mgmtClient management.Client, serviceName string) (storage.BlobStorageClient, error) {
storageClient, err := getStorageClientForStorageService(mgmtClient, serviceName)
if err != nil {
return storage.BlobStorageClient{}, err
}
return storageClient.GetBlobService(), nil
}
// getStorageServiceQueueClient is a helper function which returns the
// storage.QueueServiceClient associated to the given storage service name.
func getStorageServiceQueueClient(mgmtClient management.Client, serviceName string) (storage.QueueServiceClient, error) {
storageClient, err := getStorageClientForStorageService(mgmtClient, serviceName)
if err != nil {
return storage.QueueServiceClient{}, err
}
return storageClient.GetQueueService(), err
}

View File

@ -0,0 +1,79 @@
package azure
import (
"fmt"
"testing"
"github.com/Azure/azure-sdk-for-go/management/storageservice"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureStorageService(t *testing.T) {
name := "azure_storage_service.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccAzureStorageServiceDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureStorageServiceConfig,
Check: resource.ComposeTestCheckFunc(
testAccAzureStorageServiceExists(name),
resource.TestCheckResourceAttr(name, "name", "tftesting"),
resource.TestCheckResourceAttr(name, "location", "North Europe"),
resource.TestCheckResourceAttr(name, "description", "very descriptive"),
resource.TestCheckResourceAttr(name, "account_type", "Standard_LRS"),
),
},
},
})
}
func testAccAzureStorageServiceExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Azure Storage Service Resource not found: %s", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Storage Service ID not set.")
}
mgmtClient := testAccProvider.Meta().(*Client).mgmtClient
_, err := storageservice.NewClient(mgmtClient).GetStorageService(resource.Primary.ID)
return err
}
}
func testAccAzureStorageServiceDestroyed(s *terraform.State) error {
mgmgClient := testAccProvider.Meta().(*Client).mgmtClient
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_storage_service" {
continue
}
if resource.Primary.ID == "" {
return fmt.Errorf("Azure Storage Service ID not set.")
}
_, err := storageservice.NewClient(mgmgClient).GetStorageService(resource.Primary.ID)
return testAccResourceDestroyedErrorFilter("Storage Service", err)
}
return nil
}
var testAccAzureStorageServiceConfig = `
resource "azure_storage_service" "foo" {
# NOTE: storage service names constrained to lowercase letters only.
name = "tftesting"
location = "West US"
description = "very descriptive"
account_type = "Standard_LRS"
}
`

View File

@ -5,12 +5,11 @@ import (
"log"
"strings"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/mapstructure"
"github.com/svanharmelen/azure-sdk-for-go/management"
"github.com/svanharmelen/azure-sdk-for-go/management/networksecuritygroup"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualnetwork"
)
const (
@ -37,6 +36,14 @@ func resourceAzureVirtualNetwork() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
},
"dns_servers_names": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"subnet": &schema.Schema{
Type: schema.TypeSet,
Required: true,
@ -94,11 +101,7 @@ func resourceAzureVirtualNetworkCreate(d *schema.ResourceData, meta interface{})
}
}
network, err := createVirtualNetwork(d)
if err != nil {
return err
}
network := createVirtualNetwork(d)
nc.Configuration.VirtualNetworkSites = append(nc.Configuration.VirtualNetworkSites, network)
req, err := virtualnetwork.NewClient(mc).SetVirtualNetworkConfiguration(nc)
@ -187,11 +190,7 @@ func resourceAzureVirtualNetworkUpdate(d *schema.ResourceData, meta interface{})
found := false
for i, n := range nc.Configuration.VirtualNetworkSites {
if n.Name == d.Id() {
network, err := createVirtualNetwork(d)
if err != nil {
return err
}
network := createVirtualNetwork(d)
nc.Configuration.VirtualNetworkSites[i] = network
found = true
@ -263,15 +262,19 @@ func resourceAzureSubnetHash(v interface{}) int {
return hashcode.String(subnet)
}
func createVirtualNetwork(d *schema.ResourceData) (virtualnetwork.VirtualNetworkSite, error) {
var addressPrefix []string
err := mapstructure.WeakDecode(d.Get("address_space"), &addressPrefix)
if err != nil {
return virtualnetwork.VirtualNetworkSite{}, fmt.Errorf("Error decoding address_space: %s", err)
func createVirtualNetwork(d *schema.ResourceData) virtualnetwork.VirtualNetworkSite {
// fetch address spaces:
var prefixes []string
for _, prefix := range d.Get("address_space").([]interface{}) {
prefixes = append(prefixes, prefix.(string))
}
addressSpace := virtualnetwork.AddressSpace{
AddressPrefix: addressPrefix,
// fetch DNS references:
var dnsRefs []virtualnetwork.DNSServerRef
for _, dns := range d.Get("dns_servers_names").([]interface{}) {
dnsRefs = append(dnsRefs, virtualnetwork.DNSServerRef{
Name: dns.(string),
})
}
// Add all subnets that are configured
@ -287,11 +290,14 @@ func createVirtualNetwork(d *schema.ResourceData) (virtualnetwork.VirtualNetwork
}
return virtualnetwork.VirtualNetworkSite{
Name: d.Get("name").(string),
Location: d.Get("location").(string),
AddressSpace: addressSpace,
Subnets: subnets,
}, nil
Name: d.Get("name").(string),
Location: d.Get("location").(string),
AddressSpace: virtualnetwork.AddressSpace{
AddressPrefix: prefixes,
},
DNSServersRef: dnsRefs,
Subnets: subnets,
}
}
func associateSecurityGroups(d *schema.ResourceData, meta interface{}) error {

View File

@ -4,9 +4,9 @@ import (
"fmt"
"testing"
"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/svanharmelen/azure-sdk-for-go/management/virtualnetwork"
)
func TestAccAzureVirtualNetwork_basic(t *testing.T) {
@ -214,16 +214,19 @@ const testAccAzureVirtualNetwork_advanced = `
resource "azure_security_group" "foo" {
name = "terraform-security-group1"
location = "West US"
}
rule {
name = "RDP"
priority = 101
source_cidr = "*"
source_port = "*"
destination_cidr = "*"
destination_port = "3389"
protocol = "TCP"
}
resource "azure_security_group_rule" "foo" {
name = "terraform-secgroup-rule"
security_group_name = "${azure_security_group.foo.name}"
type = "Inbound"
action = "Deny"
priority = 200
source_address_prefix = "100.0.0.0/32"
source_port_range = "1000"
destination_address_prefix = "10.0.0.0/32"
destination_port_range = "1000"
protocol = "TCP"
}
resource "azure_virtual_network" "foo" {
@ -242,31 +245,24 @@ const testAccAzureVirtualNetwork_update = `
resource "azure_security_group" "foo" {
name = "terraform-security-group1"
location = "West US"
}
rule {
name = "RDP"
priority = 101
source_cidr = "*"
source_port = "*"
destination_cidr = "*"
destination_port = "3389"
protocol = "TCP"
}
resource "azure_security_group_rule" "foo" {
name = "terraform-secgroup-rule"
security_group_name = "${azure_security_group.foo.name}"
type = "Inbound"
action = "Deny"
priority = 200
source_address_prefix = "100.0.0.0/32"
source_port_range = "1000"
destination_address_prefix = "10.0.0.0/32"
destination_port_range = "1000"
protocol = "TCP"
}
resource "azure_security_group" "bar" {
name = "terraform-security-group2"
location = "West US"
rule {
name = "SSH"
priority = 101
source_cidr = "*"
source_port = "*"
destination_cidr = "*"
destination_port = "22"
protocol = "TCP"
}
}
resource "azure_virtual_network" "foo" {

View File

@ -0,0 +1,20 @@
package azure
import (
"fmt"
"github.com/Azure/azure-sdk-for-go/management"
)
// testAccResourceDestroyedErrorFilter tests whether the given error is an azure ResourceNotFound
// error and properly annotates it if otherwise:
func testAccResourceDestroyedErrorFilter(resource string, err error) error {
switch {
case err == nil:
return fmt.Errorf("Azure %s still exists.", resource)
case err != nil && management.IsResourceNotFoundError(err):
return nil
default:
return err
}
}

View File

@ -46,3 +46,14 @@ The following arguments are supported:
* `certificate` - (Optional) The certificate used to authenticate with the
Azure API. If a `settings_file` is not provided `certificate` is required.
It can also be sourced from the `AZURE_CERTIFICATE` environment variable.
## Testing:
The following environment variables must be set for the running of the
acceptance test suite:
* A valid combination of the above which are required for authentification.
* `AZURE_STORAGE` - The name of a storage account to be used in tests which
require a storage backend. The storage account needs to be located in
the Western US Azure region.

View File

@ -17,7 +17,7 @@ it will attach that disk. Otherwise it will create and attach a new empty disk.
resource "azure_data_disk" "data" {
lun = 0
size = 10
storage = "yourstorage"
storage_service_name = "yourstorage"
virtual_machine = "server1"
}
```
@ -43,10 +43,10 @@ The following arguments are supported:
* `caching` - (Optional) The caching behavior of data disk. Valid options are:
`None`, `ReadOnly` and `ReadWrite` (defaults `None`)
* `storage ` - (Optional) The name of an existing storage account within the
subscription which will be used to store the VHD of this disk. Required
if no value is supplied for `media_link`. Changing this forces a new
resource to be created.
* `storage_service_name` - (Optional) The name of an existing storage account
within the subscription which will be used to store the VHD of this disk.
Required if no value is supplied for `media_link`. Changing this forces
a new resource to be created.
* `media_link` - (Optional) The location of the blob in storage where the VHD
of this disk will be created. The storage account where must be associated

View File

@ -0,0 +1,36 @@
---
layout: "azure"
page_title: "Azure: azure_dns_server"
sidebar_current: "docs-azure-resource-dns-server"
description: |-
Creates a new DNS server definition to be used internally in Azure.
---
# azure\_dns\_server
Creates a new DNS server definition to be used internally in Azure.
## Example Usage
```
resource "azure_dns_server" "google-dns" {
name = "google"
dns_address = "8.8.8.8"
}
`
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the DNS server reference. Changing this
forces a new resource to be created.
* `dns_address` - (Required) The IP address of the DNS server.
## Attributes Reference
The following attributes are exported:
* `id` - The DNS server definition ID. Coincides with the given `name`.

View File

@ -0,0 +1,50 @@
---
layout: "azure"
page_title: "Azure: azure_hosted_service"
sidebar_current: "docs-azure-hosted-service"
description: |-
Creates a new hosted service on Azure with its own .cloudapp.net domain.
---
# azure\_hosted\_service
Creates a new hosted service on Azure with its own .cloudapp.net domain.
## Example Usage
```
resource "azure_hosted_service" "terraform-service" {
name = "terraform-service"
location = "North Europe"
ephemeral_contents = false
description = "Hosted service created by Terraform."
label = "tf-hs-01"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the hosted service. Must be unique on Azure.
* `location` - (Required) The location where the hosted service should be created.
For a list of all Azure locations, please consult [this link](http://azure.microsoft.com/en-us/regions/).
* `ephemeral_contents` - (Required) A boolean value (true|false), specifying
whether all the resources present in the hosted hosted service should be
destroyed following the hosted service's destruction.
* `reverse_dns_fqdn` - (Optional) The reverse of the fully qualified domain name
for the hosted service.
* `label` - (Optional) A label to be used for tracking purposes. Must be
non-void. Defaults to `Made by Terraform.`.
* `description` - (Optional) A description for the hosted service.
## Attributes Reference
The following attributes are exported:
* `id` - The hosted service ID. Coincides with the given `name`.

View File

@ -18,7 +18,7 @@ resource "azure_instance" "web" {
name = "terraform-test"
image = "Ubuntu Server 14.04 LTS"
size = "Basic_A1"
storage = "yourstorage"
storage_service_name = "yourstorage"
location = "West US"
username = "terraform"
password = "Pass!admin123"
@ -56,9 +56,9 @@ The following arguments are supported:
belongs to. If a value is supplied `subnet` is required. Changing this
forces a new resource to be created.
* `storage` - (Optional) The name of an existing storage account within the
subscription which will be used to store the VHDs of this instance.
Changing this forces a new resource to be created.
* `storage_service_name` - (Optional) The name of an existing storage account
within the subscription which will be used to store the VHDs of this
instance. Changing this forces a new resource to be created.
* `reverse_dns` - (Optional) The DNS address to which the IP address of the
hosted service resolves when queried using a reverse DNS query. Changing

View File

@ -0,0 +1,39 @@
---
layout: "azure"
page_title: "Azure: azure_local_network_connection"
sidebar_current: "docs-azure-resource-local-network-connection"
description: |-
Defines a new connection to a remote network throguh a VPN tunnel.
---
# azure\_local\_network\_connection
Defines a new connection to a remote network throguh a VPN tunnel.
## Example Usage
```
resource "azure_local_network_connection" "localnet" {
name = "terraform-local-network-connection"
vpn_gateway_address = "45.12.189.2"
address_space_prefixes = ["10.10.10.0/24", "10.10.11.0/24"]
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name by which this local network connection will
be referenced by. Changing this forces a new resource to be created.
* `vpn_gateway_address` - (Required) The public IPv4 of the VPN endpoint.
* `address_space_prefixes` - (Required) List of address spaces accessible
through the VPN connection. The elements are in the CIDR format.
## Attributes Reference
The following attributes are exported:
* `id` - The local network connection ID.

View File

@ -0,0 +1,42 @@
---
layout: "azure"
page_title: "Azure: azure_security_group"
sidebar_current: "docs-azure-resource-security-group"
description: |-
Creates a new network security group within the context of the specified subscription.
---
# azure\_security\_group
Creates a new network security group within the context of the specified
subscription.
## Example Usage
```
resource "azure_security_group" "web" {
name = "webservers"
location = "West US"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the security group. Changing this forces a
new resource to be created.
* `label` - (Optional) The identifier for the security group. The label can be
up to 1024 characters long. Changing this forces a new resource to be
created (defaults to the security group name)
* `location` - (Required) The location/region where the security group is
created. Changing this forces a new resource to be created.
## Attributes Reference
The following attributes are exported:
* `id` - The security group ID.
* `label` - The identifier for the security group.

View File

@ -1,84 +0,0 @@
---
layout: "azure"
page_title: "Azure: azure_security_group"
sidebar_current: "docs-azure-resource-security-group"
description: |-
Creates a new network security group within the context of the specified subscription.
---
# azure\_security\_group
Creates a new network security group within the context of the specified
subscription.
## Example Usage
```
resource "azure_security_group" "web" {
name = "webservers"
location = "West US"
rule {
name = "HTTPS"
priority = 101
source_cidr = "*"
source_port = "*"
destination_cidr = "*"
destination_port = "443"
protocol = "TCP"
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the security group. Changing this forces a
new resource to be created.
* `label` - (Optional) The identifier for the security group. The label can be
up to 1024 characters long. Changing this forces a new resource to be
created (defaults to the security group name)
* `location` - (Required) The location/region where the security group is
created. Changing this forces a new resource to be created.
* `rule` - (Required) Can be specified multiple times to define multiple
rules. Each `rule` block supports fields documented below.
The `rule` block supports:
* `name` - (Required) The name of the security rule.
* `type ` - (Optional) The type of the security rule. Valid options are:
`Inbound` and `Outbound` (defaults `Inbound`)
* `priority` - (Required) The priority of the network security rule. Rules with
lower priority are evaluated first. This value can be between 100 and 4096.
* `action` - (Optional) The action that is performed when the security rule is
matched. Valid options are: `Allow` and `Deny` (defaults `Allow`)
* `source_cidr` - (Required) The CIDR or source IP range. An asterisk (\*) can
also be used to match all source IPs.
* `source_port` - (Required) The source port or range. This value can be
between 0 and 65535. An asterisk (\*) can also be used to match all ports.
* `destination_cidr` - (Required) The CIDR or destination IP range. An asterisk
(\*) can also be used to match all destination IPs.
* `destination_port` - (Required) The destination port or range. This value can
be between 0 and 65535. An asterisk (\*) can also be used to match all
ports.
* `protocol` - (Optional) The protocol of the security rule. Valid options are:
`TCP`, `UDP` and `*` (defaults `TCP`)
## Attributes Reference
The following attributes are exported:
* `id` - The security group ID.
* `label` - The identifier for the security group.

View File

@ -0,0 +1,71 @@
---
layout: "azure"
page_title: "Azure: azure_security_group_rule"
sidebar_current: "docs-azure-resource-security-group-rule"
description: |-
Creates a new network security rule to be associated with a given security group.
---
# azure\_security\_group\_rule
Creates a new network security rule to be associated with a given security group.
## Example Usage
```
resource "azure_security_group" "web" {
...
}
resource "azure_security_group_rule" "ssh_access" {
name = "ssh-access-rule"
security_group_name = "${azure_security_group.web.name}"
type = "Inbound"
action = "Allow"
priority = 200
source_address_prefix = "100.0.0.0/32"
source_port_range = "*"
destination_address_prefix = "10.0.0.0/32"
destination_port_range = "22"
protocol = "TCP"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the security group the rule should be
applied to.
* `security_group_name` - (Required) The name of the security group m
* `type` - (Required) The type of the security rule. Valid options are:
`Inbound` and `Outbound`.
* `priority` - (Required) The priority of the network security rule. Rules with
lower priority are evaluated first. This value can be between 100 and 4096.
* `action` - (Optional) The action that is performed when the security rule is
matched. Valid options are: `Allow` and `Deny`.
* `source_address_prefix` - (Required) The address prefix of packet sources that
that should be subjected to the rule. An asterisk (\*) can also be used to
match all source IPs.
* `source_port_range` - (Required) The source port or range. This value can be
between 0 and 65535. An asterisk (\*) can also be used to match all ports.
* `destination_address_prefix` - (Required) The address prefix of packet
destinations that should be subjected to the rule. An asterisk
(\*) can also be used to match all destination IPs.
* `destination_port_range` - (Required) The destination port or range. This value
can be between 0 and 65535. An asterisk (\*) can also be used to match all
ports.
* `protocol` - (Optional) The protocol of the security rule. Valid options are:
`TCP`, `UDP` and `*`.
The following attributes are exported:
* `id` - The security group rule ID. Coincides with its given `name`.

View File

@ -0,0 +1,49 @@
---
layout: "azure"
page_title: "Azure: azure_storage_blob"
sidebar_current: "docs-azure-storage-blob"
description: |-
Creates a new storage blob within a given storage container on Azure.
---
# azure\_storage\_blob
Creates a new storage blob within a given storage container on Azure.
## Example Usage
```
resource "azure_storage_blob" "foo" {
name = "tftesting-blob"
storage_service_name = "tfstorserv"
storage_container_name = "terraform-storage-container"
type = "PageBlob"
size = 1024
}
````
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the storage blob. Must be unique within
the storage service the blob is located.
* `storage_service_name` - (Required) The name of the storage service within
which the storage container in which the blob will be created resides.
* `storage_container_name` - (Required) The name of the storage container
in which this blob should be created. Must be located on the storage
service given with `storage_service_name`.
* `type` - (Required) The type of the storage blob to be created. One of either
`BlockBlob` or `PageBlob`.
* `size` - (Optional) Used only for `PageBlob`'s to specify the size in bytes
of the blob to be created. Must be a multiple of 512. Defaults to 0.
## Attributes Reference
The following attributes are exported:
* `id` - The storage blob ID. Coincides with the given `name`.

View File

@ -0,0 +1,43 @@
---
layout: "azure"
page_title: "Azure: azure_storage_container"
sidebar_current: "docs-azure-storage-container"
description: |-
Creates a new storage container within a given storage service on Azure.
---
# azure\_storage\_container
Creates a new storage container within a given storage service on Azure.
## Example Usage
```
resource "azure_storage_container" "stor-cont" {
name = "terraform-storage-container"
container_access_type = "blob"
storage_service_name = "tfstorserv"
}
````
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the storage container. Must be unique within
the storage service the container is located.
* `storage_service_name` - (Required) The name of the storage service within
which the storage container should be created.
* `container_access_type` - (Required) The 'interface' for access the container
provides. Can be either `blob`, `container` or ``.
* `properties` - (Optional) Key-value definition of additional properties
associated to the storage service.
## Attributes Reference
The following attributes are exported:
* `id` - The storage container ID. Coincides with the given `name`.

View File

@ -0,0 +1,36 @@
---
layout: "azure"
page_title: "Azure: azure_storage_queue"
sidebar_current: "docs-azure-storage-queue"
description: |-
Creates a new storage queue within a given storage service on Azure.
---
# azure\_storage\_queue
Creates a new storage queue within a given storage service on Azure.
## Example Usage
```
resource "azure_storage_queue" "stor-queue" {
name = "terraform-storage-queue"
storage_service_name = "tfstorserv"
}
````
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the storage queue. Must be unique within
the storage service the queue is located.
* `storage_service_name` - (Required) The name of the storage service within
which the storage queue should be created.
## Attributes Reference
The following attributes are exported:
* `id` - The storage queue ID. Coincides with the given `name`.

View File

@ -0,0 +1,55 @@
---
layout: "azure"
page_title: "Azure: azure_storage_service"
sidebar_current: "docs-azure-storage-service"
description: |-
Creates a new storage service on Azure in which storage containers may be created.
---
# azure\_storage\_service
Creates a new storage service on Azure in which storage containers may be created.
## Example Usage
```
resource "azure_storage_service" "tfstor" {
name = "tfstor"
location = "West US"
description = "Made by Terraform."
account_type = "Standard_LRS"
}
````
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the storage service. Must be between 4 and 24
lowercase-only characters or digits Must be unique on Azure.
* `location` - (Required) The location where the storage service should be created.
For a list of all Azure locations, please consult [this link](http://azure.microsoft.com/en-us/regions/).
* `account_type` - (Required) The type of storage account to be created.
Available options include `Standard_LRS`, `Standard_ZRS`, `Standard_GRS`,
`Standard_RAGRS` and `Premium_LRS`. To learn more about the differences
of each storage account type, please consult [this link](http://blogs.msdn.com/b/windowsazurestorage/archive/2013/12/11/introducing-read-access-geo-replicated-storage-ra-grs-for-windows-azure-storage.aspx).
* `affinity_group` - (Optional) The affinity group the storage service should
belong to.
* `properties` - (Optional) Key-value definition of additional properties
associated to the storage service. For additional information on what
these properties do, please consult [this link](https://msdn.microsoft.com/en-us/library/azure/hh452235.aspx).
* `label` - (Optional) A label to be used for tracking purposes. Must be
non-void. Defaults to `Made by Terraform.`.
* `description` - (Optional) A description for the storage service.
## Attributes Reference
The following attributes are exported:
* `id` - The storage service ID. Coincides with the given `name`.

View File

@ -40,6 +40,9 @@ The following arguments are supported:
* `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.

View File

@ -17,14 +17,46 @@
<a href="/docs/providers/azure/r/data_disk.html">azure_data_disk</a>
</li>
<li<%= sidebar_current("docs-azure-resource-dns-server") %>>
<a href="/docs/providers/azure/r/dns_server.html">azure_dns_server</a>
</li>
<li<%= sidebar_current("docs-azure-resource-hosted-service") %>>
<a href="/docs/providers/azure/r/hosted_service.html">azure_hosted_service</a>
</li>
<li<%= sidebar_current("docs-azure-resource-instance") %>>
<a href="/docs/providers/azure/r/instance.html">azure_instance</a>
</li>
<li<%= sidebar_current("docs-azure-resource-local-network") %>>
<a href="/docs/providers/azure/r/local_network_connection.html">azure_local_network_connection</a>
</li>
<li<%= sidebar_current("docs-azure-resource-security-group") %>>
<a href="/docs/providers/azure/r/security_group.html">azure_security_group</a>
</li>
<li<%= sidebar_current("docs-azure-resource-security-group-rule") %>>
<a href="/docs/providers/azure/r/security_group_rule.html">azure_security_group_rule</a>
</li>
<li<%= sidebar_current("docs-azure-resource-storage-blob") %>>
<a href="/docs/providers/azure/r/storage_blob.html">azure_storage_blob</a>
</li>
<li<%= sidebar_current("docs-azure-resource-storage-container") %>>
<a href="/docs/providers/azure/r/storage_container.html">azure_storage_container</a>
</li>
<li<%= sidebar_current("docs-azure-resource-storage-queue") %>>
<a href="/docs/providers/azure/r/storage_queue.html">azure_storage_queue</a>
</li>
<li<%= sidebar_current("docs-azure-resource-storage-service") %>>
<a href="/docs/providers/azure/r/storage_service.html">azure_storage_service</a>
</li>
<li<%= sidebar_current("docs-azure-resource-virtual-network") %>>
<a href="/docs/providers/azure/r/virtual_network.html">azure_virtual_network</a>
</li>