Merge resources unto upstream.

This commit is contained in:
aznashwan 2015-06-05 17:12:21 +03:00
parent dd24b58bf3
commit 9670e69613
29 changed files with 2654 additions and 557 deletions

Binary file not shown.

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,17 @@ 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_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,17 @@ 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.
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}"
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}"
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}"
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}"
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

@ -5,11 +5,11 @@ import (
"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 +313,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 +324,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 +346,24 @@ 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"
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 +378,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 +386,54 @@ 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"
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"
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 +455,4 @@ resource "azure_instance" "foo" {
public_port = 5985
private_port = 5985
}
}`, os.Getenv("AZURE_STORAGE"))
}`, testAccStorageServiceName)

View File

@ -0,0 +1,265 @@
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, ok := meta.(*Client)
if !ok {
return fmt.Errorf("Failed to convert to *Client, got: %T", meta)
}
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, ok := meta.(*Client)
if !ok {
return fmt.Errorf("Failed to convert to *Client, got: %T", meta)
}
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, ok := meta.(*Client)
if !ok {
return fmt.Errorf("Failed to convert to *Client, got: %T", meta)
}
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, ok := meta.(*Client)
if !ok {
return false, fmt.Errorf("Failed to convert to *Client, got: %T", meta)
}
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, ok := meta.(*Client)
if !ok {
return fmt.Errorf("Failed to convert to *Client, got: %T", meta)
}
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,320 @@
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"],
},
// TODO(aznashwan): update Sander's docs to remove default.
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: parameterDescriptions["netsecgroup_type"],
},
"priority": &schema.Schema{
Type: schema.TypeInt,
Required: true,
Description: parameterDescriptions["netsecgroup_priority"],
},
// TODO(aznashwan): update Sander's docs to remove default.
"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"],
},
// TODO(aznashwan): update Sander's docs to remove default.
"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{
// TODO(aznashwan): security checks here:
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{
// TODO(azhnashwan): Parameter check here:
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,186 @@
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,
DefaultFunc: func() (interface{}, error) {
return "BlockBlob", nil
},
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,139 @@
package azure
import (
"fmt"
"testing"
"time"
"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,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureStorageBlockBlobConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureStorageBlobExists(name),
resource.TestCheckResourceAttr(name, "name", "tftesting-blob"),
resource.TestCheckResourceAttr(name, "type", "BlockBlob"),
resource.TestCheckResourceAttr(name, "storage_container_name", testAccStorageContainerName),
resource.TestCheckResourceAttr(name, "storage_service_name", testAccStorageServiceName),
),
},
},
})
// because containers take a while to get deleted, sleep for a while:
time.Sleep(5 * time.Minute)
}
func TestAccAzureStoragePageBlob(t *testing.T) {
name := "azure_storage_blob.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureStorageBlobDeleted,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureStoragePageBlobConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureStorageBlobExists(name),
resource.TestCheckResourceAttr(name, "name", "tftesting-blob"),
resource.TestCheckResourceAttr(name, "type", "PageBlob"),
resource.TestCheckResourceAttr(name, "size", "512"),
resource.TestCheckResourceAttr(name, "storage_container_name", testAccStorageContainerName),
resource.TestCheckResourceAttr(name, "storage_service_name", testAccStorageServiceName),
),
},
},
})
// because containers take a while to get deleted, sleep for a while:
time.Sleep(5 * time.Minute)
}
func testAccCheckAzureStorageBlobExists(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.BlobExists(testAccStorageContainerName, 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(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(testAccStorageContainerName, resource.Primary.ID)
if err != nil {
return err
}
if exists {
return fmt.Errorf("Azure Storage Blob still exists.")
}
}
return nil
}
var testAccAzureStorageBlockBlobConfig = testAccAzureStorageContainerConfig + fmt.Sprintf(`
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 = "%s"
storage_container_name = "%s"
}
`, testAccStorageServiceName, testAccStorageContainerName)
var testAccAzureStoragePageBlobConfig = testAccAzureStorageContainerConfig + fmt.Sprintf(`
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 = "%s"
storage_container_name = "%s"
# NOTE: must be a multiple of 512:
size = 512
}
`, testAccStorageServiceName, testAccStorageContainerName)

View File

@ -0,0 +1,166 @@
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,
DefaultFunc: func() (interface{}, error) {
return "", nil
},
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,96 @@
package azure
import (
"fmt"
"testing"
"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,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,31 @@
package azure
import (
"fmt"
"log"
"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"
)
// getStorageServiceBlobClient is a helper function which returns the
// storage.BlobStorageClient associated to the given storage account name.
func getStorageServiceBlobClient(mgmtClient management.Client, serviceName string) (storage.BlobStorageClient, error) {
log.Println("[INFO] Begun generating Azure Storage Service Blob client.")
var blobClient storage.BlobStorageClient
storageServiceClient := storageservice.NewClient(mgmtClient)
keys, err := storageServiceClient.GetStorageServiceKeys(serviceName)
if err != nil {
return blobClient, fmt.Errorf("Error reading Storage Service %s's keys from Azure: %s", serviceName, err)
}
storageClient, err := storage.NewBasicClient(serviceName, keys.PrimaryKey)
if err != nil {
return blobClient, fmt.Errorf("Error creating Storage Service Client for %s: %s", serviceName, err)
}
return storageClient.GetBlobService(), nil
}

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
}
}