Merge resources unto upstream.
This commit is contained in:
parent
dd24b58bf3
commit
9670e69613
Binary file not shown.
|
@ -5,7 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"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
|
// Config is the configuration structure used to instantiate a
|
||||||
|
|
|
@ -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.",
|
||||||
|
}
|
|
@ -32,10 +32,17 @@ func Provider() terraform.ResourceProvider {
|
||||||
},
|
},
|
||||||
|
|
||||||
ResourcesMap: map[string]*schema.Resource{
|
ResourcesMap: map[string]*schema.Resource{
|
||||||
"azure_data_disk": resourceAzureDataDisk(),
|
"azure_instance": resourceAzureInstance(),
|
||||||
"azure_instance": resourceAzureInstance(),
|
"azure_data_disk": resourceAzureDataDisk(),
|
||||||
"azure_security_group": resourceAzureSecurityGroup(),
|
"azure_hosted_service": resourceAzureHostedService(),
|
||||||
"azure_virtual_network": resourceAzureVirtualNetwork(),
|
"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,
|
ConfigureFunc: providerConfigure,
|
||||||
|
|
|
@ -11,6 +11,17 @@ import (
|
||||||
var testAccProviders map[string]terraform.ResourceProvider
|
var testAccProviders map[string]terraform.ResourceProvider
|
||||||
var testAccProvider *schema.Provider
|
var testAccProvider *schema.Provider
|
||||||
|
|
||||||
|
const testAccSecurityGroupName = "terraform-security-group"
|
||||||
|
|
||||||
|
// 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() {
|
func init() {
|
||||||
testAccProvider = Provider().(*schema.Provider)
|
testAccProvider = Provider().(*schema.Provider)
|
||||||
testAccProviders = map[string]terraform.ResourceProvider{
|
testAccProviders = map[string]terraform.ResourceProvider{
|
||||||
|
|
|
@ -5,9 +5,9 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"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/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"
|
const dataDiskBlobStorageURL = "http://%s.blob.core.windows.net/disks/%s.vhd"
|
||||||
|
@ -50,7 +50,7 @@ func resourceAzureDataDisk() *schema.Resource {
|
||||||
Default: "None",
|
Default: "None",
|
||||||
},
|
},
|
||||||
|
|
||||||
"storage": &schema.Schema{
|
"storage_service_name": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: 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))
|
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 {
|
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("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'.")
|
return fmt.Errorf("If not supplying 'media_link', you must supply 'storage'.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@ package azure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"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/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"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) {
|
func TestAccAzureDataDisk_basic(t *testing.T) {
|
||||||
|
@ -174,7 +173,7 @@ resource "azure_instance" "foo" {
|
||||||
name = "terraform-test"
|
name = "terraform-test"
|
||||||
image = "Ubuntu Server 14.04 LTS"
|
image = "Ubuntu Server 14.04 LTS"
|
||||||
size = "Basic_A1"
|
size = "Basic_A1"
|
||||||
storage = "%s"
|
storage_service_name = "%s"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
username = "terraform"
|
username = "terraform"
|
||||||
password = "Pass!admin123"
|
password = "Pass!admin123"
|
||||||
|
@ -183,16 +182,16 @@ resource "azure_instance" "foo" {
|
||||||
resource "azure_data_disk" "foo" {
|
resource "azure_data_disk" "foo" {
|
||||||
lun = 0
|
lun = 0
|
||||||
size = 10
|
size = 10
|
||||||
storage = "${azure_instance.foo.storage}"
|
storage_service_name = "${azure_instance.foo.storage}"
|
||||||
virtual_machine = "${azure_instance.foo.id}"
|
virtual_machine = "${azure_instance.foo.id}"
|
||||||
}`, os.Getenv("AZURE_STORAGE"))
|
}`, testAccStorageServiceName)
|
||||||
|
|
||||||
var testAccAzureDataDisk_advanced = fmt.Sprintf(`
|
var testAccAzureDataDisk_advanced = fmt.Sprintf(`
|
||||||
resource "azure_instance" "foo" {
|
resource "azure_instance" "foo" {
|
||||||
name = "terraform-test1"
|
name = "terraform-test1"
|
||||||
image = "Ubuntu Server 14.04 LTS"
|
image = "Ubuntu Server 14.04 LTS"
|
||||||
size = "Basic_A1"
|
size = "Basic_A1"
|
||||||
storage = "%s"
|
storage_service_name = "%s"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
username = "terraform"
|
username = "terraform"
|
||||||
password = "Pass!admin123"
|
password = "Pass!admin123"
|
||||||
|
@ -202,16 +201,16 @@ resource "azure_data_disk" "foo" {
|
||||||
lun = 1
|
lun = 1
|
||||||
size = 10
|
size = 10
|
||||||
caching = "ReadOnly"
|
caching = "ReadOnly"
|
||||||
storage = "${azure_instance.foo.storage}"
|
storage_service_name = "${azure_instance.foo.storage}"
|
||||||
virtual_machine = "${azure_instance.foo.id}"
|
virtual_machine = "${azure_instance.foo.id}"
|
||||||
}`, os.Getenv("AZURE_STORAGE"))
|
}`, testAccStorageServiceName)
|
||||||
|
|
||||||
var testAccAzureDataDisk_update = fmt.Sprintf(`
|
var testAccAzureDataDisk_update = fmt.Sprintf(`
|
||||||
resource "azure_instance" "foo" {
|
resource "azure_instance" "foo" {
|
||||||
name = "terraform-test1"
|
name = "terraform-test1"
|
||||||
image = "Ubuntu Server 14.04 LTS"
|
image = "Ubuntu Server 14.04 LTS"
|
||||||
size = "Basic_A1"
|
size = "Basic_A1"
|
||||||
storage = "%s"
|
storage_service_name = "%s"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
username = "terraform"
|
username = "terraform"
|
||||||
password = "Pass!admin123"
|
password = "Pass!admin123"
|
||||||
|
@ -221,7 +220,7 @@ resource "azure_instance" "bar" {
|
||||||
name = "terraform-test2"
|
name = "terraform-test2"
|
||||||
image = "Ubuntu Server 14.04 LTS"
|
image = "Ubuntu Server 14.04 LTS"
|
||||||
size = "Basic_A1"
|
size = "Basic_A1"
|
||||||
storage = "${azure_instance.foo.storage}"
|
storage_service_name = "${azure_instance.foo.storage}"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
username = "terraform"
|
username = "terraform"
|
||||||
password = "Pass!admin123"
|
password = "Pass!admin123"
|
||||||
|
@ -231,6 +230,6 @@ resource "azure_data_disk" "foo" {
|
||||||
lun = 2
|
lun = 2
|
||||||
size = 20
|
size = 20
|
||||||
caching = "ReadWrite"
|
caching = "ReadWrite"
|
||||||
storage = "${azure_instance.bar.storage}"
|
storage_service_name = "${azure_instance.bar.storage}"
|
||||||
virtual_machine = "${azure_instance.bar.id}"
|
virtual_machine = "${azure_instance.bar.id}"
|
||||||
}`, os.Getenv("AZURE_STORAGE"))
|
}`, testAccStorageServiceName)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -7,14 +7,14 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"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/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"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 (
|
const (
|
||||||
|
@ -68,7 +68,7 @@ func resourceAzureInstance() *schema.Resource {
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"storage": &schema.Schema{
|
"storage_service_name": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
@ -183,7 +183,7 @@ func resourceAzureInstanceCreate(d *schema.ResourceData, meta interface{}) (err
|
||||||
mc,
|
mc,
|
||||||
d.Get("image").(string),
|
d.Get("image").(string),
|
||||||
name,
|
name,
|
||||||
d.Get("storage").(string),
|
d.Get("storage_service_name").(string),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -5,11 +5,11 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"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/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"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) {
|
func TestAccAzureInstance_basic(t *testing.T) {
|
||||||
|
@ -313,7 +313,7 @@ resource "azure_instance" "foo" {
|
||||||
name = "terraform-test"
|
name = "terraform-test"
|
||||||
image = "Ubuntu Server 14.04 LTS"
|
image = "Ubuntu Server 14.04 LTS"
|
||||||
size = "Basic_A1"
|
size = "Basic_A1"
|
||||||
storage = "%s"
|
storage_service_name = "%s"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
username = "terraform"
|
username = "terraform"
|
||||||
password = "Pass!admin123"
|
password = "Pass!admin123"
|
||||||
|
@ -324,7 +324,7 @@ resource "azure_instance" "foo" {
|
||||||
public_port = 22
|
public_port = 22
|
||||||
private_port = 22
|
private_port = 22
|
||||||
}
|
}
|
||||||
}`, os.Getenv("AZURE_STORAGE"))
|
}`, testAccStorageServiceName)
|
||||||
|
|
||||||
var testAccAzureInstance_advanced = fmt.Sprintf(`
|
var testAccAzureInstance_advanced = fmt.Sprintf(`
|
||||||
resource "azure_virtual_network" "foo" {
|
resource "azure_virtual_network" "foo" {
|
||||||
|
@ -346,23 +346,24 @@ resource "azure_virtual_network" "foo" {
|
||||||
resource "azure_security_group" "foo" {
|
resource "azure_security_group" "foo" {
|
||||||
name = "terraform-security-group1"
|
name = "terraform-security-group1"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
}
|
||||||
|
|
||||||
rule {
|
resource "azure_security_group_rule" "foo" {
|
||||||
name = "rdp"
|
name = "rdp"
|
||||||
priority = 101
|
security_group_name = "${azure_security_group.foo.name}"
|
||||||
source_cidr = "*"
|
priority = 101
|
||||||
source_port = "*"
|
source_address_prefix = "*"
|
||||||
destination_cidr = "*"
|
source_port_range = "*"
|
||||||
destination_port = 3389
|
destination_address_prefix = "*"
|
||||||
protocol = "TCP"
|
destination_port_range = "3389"
|
||||||
}
|
protocol = "TCP"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azure_instance" "foo" {
|
resource "azure_instance" "foo" {
|
||||||
name = "terraform-test1"
|
name = "terraform-test1"
|
||||||
image = "Windows Server 2012 R2 Datacenter, April 2015"
|
image = "Windows Server 2012 R2 Datacenter, April 2015"
|
||||||
size = "Basic_A1"
|
size = "Basic_A1"
|
||||||
storage = "%s"
|
storage_service_name = "%s"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
time_zone = "America/Los_Angeles"
|
time_zone = "America/Los_Angeles"
|
||||||
subnet = "subnet1"
|
subnet = "subnet1"
|
||||||
|
@ -377,7 +378,7 @@ resource "azure_instance" "foo" {
|
||||||
public_port = 3389
|
public_port = 3389
|
||||||
private_port = 3389
|
private_port = 3389
|
||||||
}
|
}
|
||||||
}`, os.Getenv("AZURE_STORAGE"))
|
}`, testAccStorageServiceName)
|
||||||
|
|
||||||
var testAccAzureInstance_update = fmt.Sprintf(`
|
var testAccAzureInstance_update = fmt.Sprintf(`
|
||||||
resource "azure_virtual_network" "foo" {
|
resource "azure_virtual_network" "foo" {
|
||||||
|
@ -385,52 +386,54 @@ resource "azure_virtual_network" "foo" {
|
||||||
address_space = ["10.1.2.0/24"]
|
address_space = ["10.1.2.0/24"]
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
|
||||||
subnet {
|
subnet {
|
||||||
name = "subnet1"
|
name = "subnet1"
|
||||||
address_prefix = "10.1.2.0/25"
|
address_prefix = "10.1.2.0/25"
|
||||||
}
|
}
|
||||||
|
|
||||||
subnet {
|
subnet {
|
||||||
name = "subnet2"
|
name = "subnet2"
|
||||||
address_prefix = "10.1.2.128/25"
|
address_prefix = "10.1.2.128/25"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azure_security_group" "foo" {
|
resource "azure_security_group" "foo" {
|
||||||
name = "terraform-security-group1"
|
name = "terraform-security-group1"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
}
|
||||||
|
|
||||||
rule {
|
resource "azure_security_group_rule" "foo" {
|
||||||
name = "rdp"
|
name = "rdp"
|
||||||
priority = 101
|
security_group_name = "${azure_security_group.foo.name}"
|
||||||
source_cidr = "*"
|
priority = 101
|
||||||
source_port = "*"
|
source_address_prefix = "*"
|
||||||
destination_cidr = "*"
|
source_port_range = "*"
|
||||||
destination_port = 3389
|
destination_address_prefix = "*"
|
||||||
protocol = "TCP"
|
destination_port_range = "3389"
|
||||||
}
|
protocol = "TCP"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azure_security_group" "bar" {
|
resource "azure_security_group" "bar" {
|
||||||
name = "terraform-security-group2"
|
name = "terraform-security-group2"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
}
|
||||||
|
|
||||||
rule {
|
resource "azure_security_group_rule" "bar" {
|
||||||
name = "rdp"
|
name = "rdp"
|
||||||
priority = 101
|
security_group_name = "${azure_security_group.bar.name}"
|
||||||
source_cidr = "192.168.0.0/24"
|
priority = 101
|
||||||
source_port = "*"
|
source_address_prefix = "192.168.0.0/24"
|
||||||
destination_cidr = "*"
|
source_port_range = "*"
|
||||||
destination_port = 3389
|
destination_address_prefix = "*"
|
||||||
protocol = "TCP"
|
destination_port_range = "3389"
|
||||||
}
|
protocol = "TCP"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azure_instance" "foo" {
|
resource "azure_instance" "foo" {
|
||||||
name = "terraform-test1"
|
name = "terraform-test1"
|
||||||
image = "Windows Server 2012 R2 Datacenter, April 2015"
|
image = "Windows Server 2012 R2 Datacenter, April 2015"
|
||||||
size = "Basic_A2"
|
size = "Basic_A2"
|
||||||
storage = "%s"
|
storage_service_name = "%s"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
time_zone = "America/Los_Angeles"
|
time_zone = "America/Los_Angeles"
|
||||||
subnet = "subnet1"
|
subnet = "subnet1"
|
||||||
|
@ -452,4 +455,4 @@ resource "azure_instance" "foo" {
|
||||||
public_port = 5985
|
public_port = 5985
|
||||||
private_port = 5985
|
private_port = 5985
|
||||||
}
|
}
|
||||||
}`, os.Getenv("AZURE_STORAGE"))
|
}`, testAccStorageServiceName)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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"]
|
||||||
|
}
|
||||||
|
`
|
|
@ -1,22 +1,18 @@
|
||||||
package azure
|
package azure
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"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/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 {
|
func resourceAzureSecurityGroup() *schema.Resource {
|
||||||
return &schema.Resource{
|
return &schema.Resource{
|
||||||
Create: resourceAzureSecurityGroupCreate,
|
Create: resourceAzureSecurityGroupCreate,
|
||||||
Read: resourceAzureSecurityGroupRead,
|
Read: resourceAzureSecurityGroupRead,
|
||||||
Update: resourceAzureSecurityGroupUpdate,
|
|
||||||
Delete: resourceAzureSecurityGroupDelete,
|
Delete: resourceAzureSecurityGroupDelete,
|
||||||
|
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
|
@ -38,63 +34,6 @@ func resourceAzureSecurityGroup() *schema.Resource {
|
||||||
Required: true,
|
Required: true,
|
||||||
ForceNew: 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)
|
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)
|
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 {
|
func resourceAzureSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
mc := meta.(*Client).mgmtClient
|
mc := meta.(*Client).mgmtClient
|
||||||
|
|
||||||
|
@ -205,70 +83,9 @@ func resourceAzureSecurityGroupRead(d *schema.ResourceData, meta interface{}) er
|
||||||
d.Set("label", sg.Label)
|
d.Set("label", sg.Label)
|
||||||
d.Set("location", sg.Location)
|
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
|
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 {
|
func resourceAzureSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
mc := meta.(*Client).mgmtClient
|
mc := meta.(*Client).mgmtClient
|
||||||
|
|
||||||
|
@ -288,66 +105,3 @@ func resourceAzureSecurityGroupDelete(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"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/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"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) {
|
func TestAccAzureSecurityGroup_basic(t *testing.T) {
|
||||||
|
@ -19,72 +19,16 @@ func TestAccAzureSecurityGroup_basic(t *testing.T) {
|
||||||
CheckDestroy: testAccCheckAzureSecurityGroupDestroy,
|
CheckDestroy: testAccCheckAzureSecurityGroupDestroy,
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccAzureSecurityGroup_basic,
|
Config: testAccAzureSecurityGroupConfig,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
testAccCheckAzureSecurityGroupExists(
|
testAccCheckAzureSecurityGroupExists(
|
||||||
"azure_security_group.foo", &group),
|
"azure_security_group.foo", &group),
|
||||||
testAccCheckAzureSecurityGroupBasicAttributes(&group),
|
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"azure_security_group.foo", "name", "terraform-security-group"),
|
"azure_security_group.foo", "name", "terraform-security-group"),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"azure_security_group.foo", "location", "West US"),
|
"azure_security_group.foo", "location", "West US"),
|
||||||
resource.TestCheckResourceAttr(
|
resource.TestCheckResourceAttr(
|
||||||
"azure_security_group.foo", "rule.936204579.name", "RDP"),
|
"azure_security_group.foo", "label", "terraform testing security group"),
|
||||||
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"),
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -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 {
|
func testAccCheckAzureSecurityGroupDestroy(s *terraform.State) error {
|
||||||
mc := testAccProvider.Meta().(*Client).mgmtClient
|
mc := testAccProvider.Meta().(*Client).mgmtClient
|
||||||
|
|
||||||
|
@ -228,44 +89,9 @@ func testAccCheckAzureSecurityGroupDestroy(s *terraform.State) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAccAzureSecurityGroup_basic = `
|
var testAccAzureSecurityGroupConfig = fmt.Sprintf(`
|
||||||
resource "azure_security_group" "foo" {
|
resource "azure_security_group" "foo" {
|
||||||
name = "terraform-security-group"
|
name = "%s"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
label = "terraform testing security group"
|
||||||
rule {
|
}`, testAccSecurityGroupName)
|
||||||
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"
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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"
|
||||||
|
}
|
||||||
|
`
|
|
@ -5,12 +5,11 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"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/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"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 (
|
const (
|
||||||
|
@ -37,6 +36,14 @@ func resourceAzureVirtualNetwork() *schema.Resource {
|
||||||
Elem: &schema.Schema{Type: schema.TypeString},
|
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{
|
"subnet": &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
@ -94,11 +101,7 @@ func resourceAzureVirtualNetworkCreate(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
network, err := createVirtualNetwork(d)
|
network := createVirtualNetwork(d)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nc.Configuration.VirtualNetworkSites = append(nc.Configuration.VirtualNetworkSites, network)
|
nc.Configuration.VirtualNetworkSites = append(nc.Configuration.VirtualNetworkSites, network)
|
||||||
|
|
||||||
req, err := virtualnetwork.NewClient(mc).SetVirtualNetworkConfiguration(nc)
|
req, err := virtualnetwork.NewClient(mc).SetVirtualNetworkConfiguration(nc)
|
||||||
|
@ -187,11 +190,7 @@ func resourceAzureVirtualNetworkUpdate(d *schema.ResourceData, meta interface{})
|
||||||
found := false
|
found := false
|
||||||
for i, n := range nc.Configuration.VirtualNetworkSites {
|
for i, n := range nc.Configuration.VirtualNetworkSites {
|
||||||
if n.Name == d.Id() {
|
if n.Name == d.Id() {
|
||||||
network, err := createVirtualNetwork(d)
|
network := createVirtualNetwork(d)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nc.Configuration.VirtualNetworkSites[i] = network
|
nc.Configuration.VirtualNetworkSites[i] = network
|
||||||
|
|
||||||
found = true
|
found = true
|
||||||
|
@ -263,15 +262,19 @@ func resourceAzureSubnetHash(v interface{}) int {
|
||||||
return hashcode.String(subnet)
|
return hashcode.String(subnet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createVirtualNetwork(d *schema.ResourceData) (virtualnetwork.VirtualNetworkSite, error) {
|
func createVirtualNetwork(d *schema.ResourceData) virtualnetwork.VirtualNetworkSite {
|
||||||
var addressPrefix []string
|
// fetch address spaces:
|
||||||
err := mapstructure.WeakDecode(d.Get("address_space"), &addressPrefix)
|
var prefixes []string
|
||||||
if err != nil {
|
for _, prefix := range d.Get("address_space").([]interface{}) {
|
||||||
return virtualnetwork.VirtualNetworkSite{}, fmt.Errorf("Error decoding address_space: %s", err)
|
prefixes = append(prefixes, prefix.(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
addressSpace := virtualnetwork.AddressSpace{
|
// fetch DNS references:
|
||||||
AddressPrefix: addressPrefix,
|
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
|
// Add all subnets that are configured
|
||||||
|
@ -287,11 +290,14 @@ func createVirtualNetwork(d *schema.ResourceData) (virtualnetwork.VirtualNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
return virtualnetwork.VirtualNetworkSite{
|
return virtualnetwork.VirtualNetworkSite{
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
Location: d.Get("location").(string),
|
Location: d.Get("location").(string),
|
||||||
AddressSpace: addressSpace,
|
AddressSpace: virtualnetwork.AddressSpace{
|
||||||
Subnets: subnets,
|
AddressPrefix: prefixes,
|
||||||
}, nil
|
},
|
||||||
|
DNSServersRef: dnsRefs,
|
||||||
|
Subnets: subnets,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func associateSecurityGroups(d *schema.ResourceData, meta interface{}) error {
|
func associateSecurityGroups(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/azure-sdk-for-go/management/virtualnetwork"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/svanharmelen/azure-sdk-for-go/management/virtualnetwork"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccAzureVirtualNetwork_basic(t *testing.T) {
|
func TestAccAzureVirtualNetwork_basic(t *testing.T) {
|
||||||
|
@ -214,16 +214,19 @@ const testAccAzureVirtualNetwork_advanced = `
|
||||||
resource "azure_security_group" "foo" {
|
resource "azure_security_group" "foo" {
|
||||||
name = "terraform-security-group1"
|
name = "terraform-security-group1"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
}
|
||||||
|
|
||||||
rule {
|
resource "azure_security_group_rule" "foo" {
|
||||||
name = "RDP"
|
name = "terraform-secgroup-rule"
|
||||||
priority = 101
|
security_group_name = "${azure_security_group.foo.name}"
|
||||||
source_cidr = "*"
|
type = "Inbound"
|
||||||
source_port = "*"
|
action = "Deny"
|
||||||
destination_cidr = "*"
|
priority = 200
|
||||||
destination_port = "3389"
|
source_address_prefix = "100.0.0.0/32"
|
||||||
protocol = "TCP"
|
source_port_range = "1000"
|
||||||
}
|
destination_address_prefix = "10.0.0.0/32"
|
||||||
|
destination_port_range = "1000"
|
||||||
|
protocol = "TCP"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azure_virtual_network" "foo" {
|
resource "azure_virtual_network" "foo" {
|
||||||
|
@ -242,31 +245,24 @@ const testAccAzureVirtualNetwork_update = `
|
||||||
resource "azure_security_group" "foo" {
|
resource "azure_security_group" "foo" {
|
||||||
name = "terraform-security-group1"
|
name = "terraform-security-group1"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
}
|
||||||
|
|
||||||
rule {
|
resource "azure_security_group_rule" "foo" {
|
||||||
name = "RDP"
|
name = "terraform-secgroup-rule"
|
||||||
priority = 101
|
security_group_name = "${azure_security_group.foo.name}"
|
||||||
source_cidr = "*"
|
type = "Inbound"
|
||||||
source_port = "*"
|
action = "Deny"
|
||||||
destination_cidr = "*"
|
priority = 200
|
||||||
destination_port = "3389"
|
source_address_prefix = "100.0.0.0/32"
|
||||||
protocol = "TCP"
|
source_port_range = "1000"
|
||||||
}
|
destination_address_prefix = "10.0.0.0/32"
|
||||||
|
destination_port_range = "1000"
|
||||||
|
protocol = "TCP"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azure_security_group" "bar" {
|
resource "azure_security_group" "bar" {
|
||||||
name = "terraform-security-group2"
|
name = "terraform-security-group2"
|
||||||
location = "West US"
|
location = "West US"
|
||||||
|
|
||||||
rule {
|
|
||||||
name = "SSH"
|
|
||||||
priority = 101
|
|
||||||
source_cidr = "*"
|
|
||||||
source_port = "*"
|
|
||||||
destination_cidr = "*"
|
|
||||||
destination_port = "22"
|
|
||||||
protocol = "TCP"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "azure_virtual_network" "foo" {
|
resource "azure_virtual_network" "foo" {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue