diff --git a/README.md b/README.md index 73815de06..15e25d44e 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ Next, head over to the [test folder](/test) to see how you can use Terratest to the web server to check that it is working correctly, and run `terraform destroy` to undeploy the web server. 1. [terraform_gcp_example_test.go](/test/terraform_gcp_example_test.go): Use Terratest to run `terraform apply` on the Terraform GCP Example and verify you get the expected outputs. -1. [terraform_remote_exec_example_test.go](/test/terraform_remote_exec_example_test.go): Use Terratest to run +1. [terraform_remote_exec_example_test.go](/test/terraform_remote_exec_example_test.go): Use Terratest to run `terraform apply` and then remotely provision the instance while using a custom SSH agent managed by Terratest 1. [terraform_scp_example_test.go](/test/terraform_scp_example_test.go): Use Terratest to simplify copying resources like config files and logs from deployed EC2 Instances. This is especially useful for getting a snapshot of the @@ -232,7 +232,7 @@ Terratest's [modules folder](/modules) and how they can help you test different | Package | Description | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **aws** | Functions that make it easier to work with the AWS APIs. Examples: find an EC2 Instance by tag, get the IPs of EC2 Instances in an ASG, create an EC2 KeyPair, look up a VPC ID. | -| **azure** | Functions that make it easier to work with the Azure APIs. Examples: get the size of a virtual machine, get the tags of a virtual machine. | +| **azure** | Functions that make it easier to work with the Azure APIs. Examples: get the size of a virtual machine, get the tags of a virtual machine, get the subnets in a virtual network. | | **collections** | Go doesn't have much of a collections library built-in, so this package has a few helper methods for working with lists and maps. Examples: subtract two lists from each other. | | **docker** | Functions that make it easier to work with Docker and Docker Compose. Examples: run `docker-compose` commands. | | **environment** | Functions for interacting with os environment. Examples: check for first non empty environment variable in a list. | diff --git a/examples/terraform-azure-network-example/README.md b/examples/terraform-azure-network-example/README.md new file mode 100644 index 000000000..9e0a87a45 --- /dev/null +++ b/examples/terraform-azure-network-example/README.md @@ -0,0 +1,41 @@ +# Terraform Azure Example + +This folder contains a simple Terraform module that deploys network-related resources in [Azure](https://azure.microsoft.com/) to demonstrate +how you can use Terratest to write automated tests for your Azure Terraform code. + +This module deploys the following resources : + - a Resource Group + - a [Virtual Network](https://azure.microsoft.com/en-us/services/virtual-network/) + - 2 subnets within the Virtual Network + - a [Network Security Group](https://docs.microsoft.com/en-us/azure/virtual-network/security-overview) for each subnet + - a Network Security Rule associated with the first Network Security Group + - a Public IP (with a public DNS name) + +Check out [test/terraform_azure_network_example_test.go](/test/terraform_azure_network_example_test.go) to see how you can write +automated tests for this module. + +Note that the Public IP in this module is just for demonstration purposes. +It is not attached to any resource (Virtual Machine, Load Balancer, etc...), so while this address is reachable from the internet, connecting to it will not result in anything. + +**WARNING**: This module and the automated tests for it deploy real resources into your Azure account which can cost you +money. The resources are all part of the [Azure Free Account](https://azure.microsoft.com/en-us/free/), so if you haven't used that up, +it should be free, but you are completely responsible for all Azure charges. + +## Running this module manually + +1. Sign up for an [Azure](https://azure.microsoft.com/) account. +1. Configure your Azure credentials using [the Azure CLI](https://www.terraform.io/docs/providers/azurerm/auth/azure_cli.html), or environment variables supported by [the Azure Terraform provider](https://www.terraform.io/docs/providers/azurerm/index.html#argument-reference). +1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`. +1. Run `terraform init`. +1. Run `terraform apply`. +1. When you're done, run `terraform destroy`. + +## Running automated tests against this module + +1. Sign up for an [Azure](https://azure.microsoft.com/) account. +1. Configure your Azure credentials using [the Azure CLI](https://www.terraform.io/docs/providers/azurerm/auth/azure_cli.html), or environment variables supported by [the Azure Terraform provider](https://www.terraform.io/docs/providers/azurerm/index.html#argument-reference). +1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`. +1. Install [Golang](https://golang.org/) and make sure this code is checked out into your `GOPATH`. +1. `cd test` +1. `dep ensure` +1. `go test -v -run TestTerraformAzureNetworkExample -timeout 20m` diff --git a/examples/terraform-azure-network-example/main.tf b/examples/terraform-azure-network-example/main.tf new file mode 100644 index 000000000..544f681f0 --- /dev/null +++ b/examples/terraform-azure-network-example/main.tf @@ -0,0 +1,100 @@ +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY A RESOURCE GROUP +# See test/terraform_azure_network_example_test.go for how to write automated tests for this code. +# --------------------------------------------------------------------------------------------------------------------- + +resource "azurerm_resource_group" "example" { + name = "${var.resource_group_name}" + location = "${var.location}" +} + +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY A VIRTUAL NETWORK +# --------------------------------------------------------------------------------------------------------------------- + +resource "azurerm_virtual_network" "example" { + name = "${var.virtual_network_name}" + resource_group_name = "${azurerm_resource_group.example.name}" + location = "${azurerm_resource_group.example.location}" + address_space = ["10.0.0.0/16"] + + tags = { + environment = "${var.environment_tag}" + } +} + +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY 2 SUBNETS IN THE VIRTUAL NETWORK +# --------------------------------------------------------------------------------------------------------------------- + +resource "azurerm_subnet" "subnet1" { + name = "${var.first_subnet_name}" + resource_group_name = "${azurerm_resource_group.example.name}" + virtual_network_name = "${azurerm_virtual_network.example.name}" + address_prefix = "10.0.11.0/24" + network_security_group_id = "${azurerm_network_security_group.nsg1.id}" +} + +resource "azurerm_subnet" "subnet2" { + name = "${var.second_subnet_name}" + resource_group_name = "${azurerm_resource_group.example.name}" + virtual_network_name = "${azurerm_virtual_network.example.name}" + address_prefix = "10.0.12.0/24" + network_security_group_id = "${azurerm_network_security_group.nsg2.id}" +} + +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY A NETWORK SECURITY GROUP FOR EACH SUBNET +# --------------------------------------------------------------------------------------------------------------------- + +resource "azurerm_network_security_group" "nsg1" { + name = "${var.first_subnet_nsg_name}" + resource_group_name = "${azurerm_resource_group.example.name}" + location = "${azurerm_resource_group.example.location}" +} + +resource "azurerm_subnet_network_security_group_association" "nsg1" { + subnet_id = "${azurerm_subnet.subnet1.id}" + network_security_group_id = "${azurerm_network_security_group.nsg1.id}" +} + +resource "azurerm_network_security_group" "nsg2" { + name = "${var.second_subnet_nsg_name}" + resource_group_name = "${azurerm_resource_group.example.name}" + location = "${azurerm_resource_group.example.location}" +} + +resource "azurerm_subnet_network_security_group_association" "nsg2" { + subnet_id = "${azurerm_subnet.subnet2.id}" + network_security_group_id = "${azurerm_network_security_group.nsg2.id}" +} + +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY A NETWORK SECURITY RULE AND LINK IT TO THE FIRST SUBNET'S NETWORK SECURITY GROUP +# --------------------------------------------------------------------------------------------------------------------- + +resource "azurerm_network_security_rule" "example" { + name = "${var.first_subnet_security_rule_name}" + resource_group_name = "${azurerm_resource_group.example.name}" + network_security_group_name = "${azurerm_network_security_group.nsg1.name}" + priority = 111 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "22" + source_address_prefix = "VirtualNetwork" + destination_address_prefix = "*" +} + +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY A PUBLIC IP ADDRESS RESOURCE +# --------------------------------------------------------------------------------------------------------------------- + +resource "azurerm_public_ip" "example" { + name = "${var.public_ip_name}" + resource_group_name = "${azurerm_resource_group.example.name}" + location = "${azurerm_resource_group.example.location}" + allocation_method = "Static" + domain_name_label = "${var.public_ip_domain_name_label}" +} diff --git a/examples/terraform-azure-network-example/outputs.tf b/examples/terraform-azure-network-example/outputs.tf new file mode 100644 index 000000000..452663143 --- /dev/null +++ b/examples/terraform-azure-network-example/outputs.tf @@ -0,0 +1,23 @@ +output "resource_group_name" { + value = "${azurerm_resource_group.example.name}" +} + +output "virtual_network_name" { + value = "${azurerm_virtual_network.example.name}" +} + +output "first_subnet_address" { + value = "${azurerm_subnet.subnet1.address_prefix}" +} + +output "second_subnet_address" { + value = "${azurerm_subnet.subnet2.address_prefix}" +} + +output "public_ip_address" { + value = "${azurerm_public_ip.example.ip_address}" +} + +output "public_ip_fqdn" { + value = "${azurerm_public_ip.example.fqdn}" +} diff --git a/examples/terraform-azure-network-example/provider.tf b/examples/terraform-azure-network-example/provider.tf new file mode 100644 index 000000000..ca77835ec --- /dev/null +++ b/examples/terraform-azure-network-example/provider.tf @@ -0,0 +1,3 @@ +provider "azurerm" { + version = "~>1.33" +} \ No newline at end of file diff --git a/examples/terraform-azure-network-example/variables.tf b/examples/terraform-azure-network-example/variables.tf new file mode 100644 index 000000000..2afbdd42a --- /dev/null +++ b/examples/terraform-azure-network-example/variables.tf @@ -0,0 +1,72 @@ +# --------------------------------------------------------------------------------------------------------------------- +# ENVIRONMENT VARIABLES +# Define these secrets as environment variables +# --------------------------------------------------------------------------------------------------------------------- + +# ARM_TENANT_ID +# ARM_SUBSCRIPTION_ID +# ARM_CLIENT_ID (if you are using a Service Principal to authenticate to Azure) +# ARM_CLIENT_SECRET (if you are using a Service Principal to authenticate to Azure) + +# --------------------------------------------------------------------------------------------------------------------- +# REQUIRED PARAMETERS +# You must provide a value for each of these parameters. +# --------------------------------------------------------------------------------------------------------------------- + +variable "location" { + description = "The Azure region where resources will be located." +} + +variable "public_ip_domain_name_label" { + description = "The domain name label (DNS prefix) to set for the public IP." +} + +# --------------------------------------------------------------------------------------------------------------------- +# OPTIONAL PARAMETERS +# These parameters have reasonable defaults. +# --------------------------------------------------------------------------------------------------------------------- + +variable "resource_group_name" { + description = "The name of the resource group where the resources will be deployed." + default = "terratest-example-rg" +} + +variable "virtual_network_name" { + description = "The name to set for the virtual network." + default = "terratest-vnet" +} + +variable "environment_tag" { + description = "Value to set for the 'environment' tag applied to the virtual network." + default = "test" +} + +variable "first_subnet_name" { + description = "The name to set for the first subnet." + default = "terratest-subnet1" +} + +variable "second_subnet_name" { + description = "The name to set for the second subnet." + default = "terratest-subnet2" +} + +variable "first_subnet_nsg_name" { + description = "The name of network security group to apply to the first subnet." + default = "subnet1-nsg" +} + +variable "second_subnet_nsg_name" { + description = "The name of network security group to apply to the second subnet." + default = "subnet2-nsg" +} + +variable "first_subnet_security_rule_name" { + description = "The name of the network security rule to link to the first subnet's network security group." + default = "Allow_SSH_Inbound" +} + +variable "public_ip_name" { + description = "The name to set for the public IP resource." + default = "terratest-example-ip" +} diff --git a/modules/azure/network.go b/modules/azure/network.go new file mode 100644 index 000000000..8b047c05a --- /dev/null +++ b/modules/azure/network.go @@ -0,0 +1,300 @@ +package azure + +import ( + "context" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-10-01/network" + "github.com/stretchr/testify/require" +) + +// GetVirtualNetworkClient is a helper function to setup an Azure Virtual Network client +func GetVirtualNetworkClient(subscriptionID string) (*network.VirtualNetworksClient, error) { + // Validate Azure subscription ID + subscriptionID, err := getTargetAzureSubscription(subscriptionID) + if err != nil { + return nil, err + } + + // Create a VNet client + vnetClient := network.NewVirtualNetworksClient(subscriptionID) + + // Create an authorizer + authorizer, err := NewAuthorizer() + if err != nil { + return nil, err + } + + // Attach authorizer to the client + vnetClient.Authorizer = *authorizer + + return &vnetClient, nil +} + +// GetVirtualNetwork gets the details of an Azure Virtual Network +func GetVirtualNetwork(t *testing.T, vnetName string, resGroupName string, subscriptionID string) network.VirtualNetwork { + vnet, err := GetVirtualNetworkE(t, vnetName, resGroupName, subscriptionID) + require.NoError(t, err) + + return vnet +} + +// GetVirtualNetworkE gets the details of an Azure Virtual Network +func GetVirtualNetworkE(t *testing.T, vnetName string, resGroupName string, subscriptionID string) (network.VirtualNetwork, error) { + vnet := network.VirtualNetwork{} + + // Validate resource group name and subscription ID + resGroupName, err := getTargetAzureResourceGroupName(resGroupName) + if err != nil { + return vnet, err + } + + // Create a VNet client + vnetClient, err := GetVirtualNetworkClient(subscriptionID) + if err != nil { + return vnet, err + } + + // Get the details of the Virtual Network + vnet, err = vnetClient.Get(context.Background(), resGroupName, vnetName, "") + if err != nil { + return vnet, err + } + return vnet, nil +} + +// AssertVirtualNetworkExists checks if a given Virtual Network exists in the specified Resource Group and returns an error if it does not +func AssertVirtualNetworkExists(t *testing.T, vnetName string, resGroupName string, subscriptionID string) { + _, err := GetVirtualNetworkE(t, vnetName, resGroupName, subscriptionID) + // If the Virtual Network does not exist, the API returns a "ResourceNotFound" error + require.NoError(t, err) +} + +// Subnet is a representation of an Azure Virtual Network subnet +type Subnet struct { + ID string + Name string + AddressPrefix string +} + +// GetSubnetsForVirtualNetwork gets all subnets in a given Azure Virtual Network +func GetSubnetsForVirtualNetwork(t *testing.T, vnetName string, resGroupName string, subscriptionID string) []Subnet { + subnets, err := GetSubnetsForVirtualNetworkE(t, vnetName, resGroupName, subscriptionID) + require.NoError(t, err) + + return subnets +} + +// GetSubnetsForVirtualNetworkE gets all subnets in a given Azure Virtual Network +func GetSubnetsForVirtualNetworkE(t *testing.T, vnetName string, resGroupName string, subscriptionID string) ([]Subnet, error) { + subnets := []Subnet{} + + vnet, err := GetVirtualNetworkE(t, vnetName, resGroupName, subscriptionID) + if err != nil { + return subnets, err + } + + for _, subnet := range *(vnet.Subnets) { + subnets = append(subnets, Subnet{ID: *(subnet.ID), Name: *(subnet.Name), AddressPrefix: *(subnet.AddressPrefix)}) + } + + return subnets, nil +} + +// GetTagsForVirtualNetwork gets the tags of the given Virtual Network as a map +func GetTagsForVirtualNetwork(t *testing.T, vnetName string, resGroupName string, subscriptionID string) map[string]string { + tags, err := GetTagsForVirtualNetworkE(t, vnetName, resGroupName, subscriptionID) + require.NoError(t, err) + + return tags +} + +// GetTagsForVirtualNetworkE gets the tags of the given Virtual Network as a map +func GetTagsForVirtualNetworkE(t *testing.T, vnetName string, resGroupName string, subscriptionID string) (map[string]string, error) { + // Setup a blank map to populate and return + tags := make(map[string]string) + + vnet, err := GetVirtualNetworkE(t, vnetName, resGroupName, subscriptionID) + if err != nil { + return tags, err + } + + // Range through existing tags and populate above map accordingly + for k, v := range vnet.Tags { + tags[k] = *v + } + + return tags, nil +} + +// GetSubnetClient is a helper function to setup an Azure subnet client +func GetSubnetClient(subscriptionID string) (*network.SubnetsClient, error) { + // Validate Azure subscription ID + subscriptionID, err := getTargetAzureSubscription(subscriptionID) + if err != nil { + return nil, err + } + + // Create a subnet client + subnetClient := network.NewSubnetsClient(subscriptionID) + + // Create an authorizer + authorizer, err := NewAuthorizer() + if err != nil { + return nil, err + } + + // Attach authorizer to the client + subnetClient.Authorizer = *authorizer + + return &subnetClient, nil +} + +// NetworkSecurityGroup is a representation of an Azure Network Security Group +type NetworkSecurityGroup struct { + ID string + Name string + SecurityRulesNames []string +} + +// GetNetworkSecurityGroupForSubnet gets the Network Security Group associated with a given Virtual Network subnet +func GetNetworkSecurityGroupForSubnet(t *testing.T, subnetName string, vnetName string, resGroupName string, subscriptionID string) NetworkSecurityGroup { + nsg, err := GetNetworkSecurityGroupForSubnetE(t, subnetName, vnetName, resGroupName, subscriptionID) + require.NoError(t, err) + + return nsg +} + +// GetNetworkSecurityGroupForSubnetE gets the Network Security Group associated with a given Virtual Network subnet +func GetNetworkSecurityGroupForSubnetE(t *testing.T, subnetName string, vnetName string, resGroupName string, subscriptionID string) (NetworkSecurityGroup, error) { + nsg := NetworkSecurityGroup{} + + // Validate resource group name and subscription ID + resGroupName, err := getTargetAzureResourceGroupName(resGroupName) + if err != nil { + return nsg, err + } + + // Create a subnet client + subnetClient, err := GetSubnetClient(subscriptionID) + if err != nil { + return nsg, err + } + + // Get the details of the subnet and its associated Network Security Group + subnet, err := subnetClient.Get(context.Background(), resGroupName, vnetName, subnetName, "networkSecurityGroup") + if err != nil { + return nsg, err + } + if subnet.NetworkSecurityGroup != nil { + rulesNames := []string{} + for _, r := range *(subnet.NetworkSecurityGroup.SecurityRules) { + rulesNames = append(rulesNames, *(r.Name)) + } + + nsg = NetworkSecurityGroup{ + ID: *(subnet.NetworkSecurityGroup.ID), + Name: *(subnet.NetworkSecurityGroup.Name), + SecurityRulesNames: rulesNames, + } + } + + return nsg, nil +} + +// GetPublicIPClient is a helper function to setup an Azure Public IP client +func GetPublicIPClient(subscriptionID string) (*network.PublicIPAddressesClient, error) { + // Validate Azure subscription ID + subscriptionID, err := getTargetAzureSubscription(subscriptionID) + if err != nil { + return nil, err + } + + // Create a subnet client + ipClient := network.NewPublicIPAddressesClient(subscriptionID) + + // Create an authorizer + authorizer, err := NewAuthorizer() + if err != nil { + return nil, err + } + + // Attach authorizer to the client + ipClient.Authorizer = *authorizer + + return &ipClient, nil +} + +// PublicIP is a representation of an Azure Public IP Address resource +type PublicIP struct { + ID string + Name string + IPAddress string + IPAddressVersion string + AllocationMethod string + FullDNSName string +} + +// GetPublicIP gets an Azure Public IP Address resource +func GetPublicIP(t *testing.T, resGroupName string, publicIPName string, subscriptionID string) PublicIP { + ip, err := GetPublicIPE(t, resGroupName, publicIPName, subscriptionID) + require.NoError(t, err) + + return ip +} + +// GetPublicIPE gets an Azure Public IP Address resource +func GetPublicIPE(t *testing.T, resGroupName string, publicIPName string, subscriptionID string) (PublicIP, error) { + ip := PublicIP{} + + // Validate resource group name and subscription ID + resGroupName, err := getTargetAzureResourceGroupName(resGroupName) + if err != nil { + return ip, err + } + + // Create a public IP client + ipClient, err := GetPublicIPClient(subscriptionID) + if err != nil { + return ip, err + } + + azurePublicIP, err := ipClient.Get(context.Background(), resGroupName, publicIPName, "ipConfiguration") + if err != nil { + return ip, err + } + + ip = PublicIP{ + ID: *(azurePublicIP.ID), + Name: *(azurePublicIP.Name), + IPAddress: *(azurePublicIP.IPAddress), + IPAddressVersion: string(azurePublicIP.PublicIPAddressVersion), + AllocationMethod: string(azurePublicIP.PublicIPAllocationMethod), + FullDNSName: *(azurePublicIP.DNSSettings.Fqdn), + } + + return ip, nil +} + +// CheckPublicDNSNameAvailability checks whether a domain name in the cloudapp.azure.com zone is available for use +func CheckPublicDNSNameAvailability(t *testing.T, location string, domainNameLabel string, subscriptionID string) (available bool) { + available, err := CheckPublicDNSNameAvailabilityE(t, location, domainNameLabel, subscriptionID) + require.NoError(t, err) + + return +} + +// CheckPublicDNSNameAvailabilityE checks whether a domain name in the cloudapp.azure.com zone is available for use +func CheckPublicDNSNameAvailabilityE(t *testing.T, location string, domainNameLabel string, subscriptionID string) (available bool, err error) { + ipClient, err := GetPublicIPClient(subscriptionID) + if err != nil { + return + } + + res, err := (*ipClient).CheckDNSNameAvailability(context.Background(), location, domainNameLabel) + if err != nil { + return + } + available = *(res.Available) + return +} diff --git a/modules/azure/network_test.go b/modules/azure/network_test.go new file mode 100644 index 000000000..0ac5e7e05 --- /dev/null +++ b/modules/azure/network_test.go @@ -0,0 +1,109 @@ +// +build azure + +// NOTE: We use build tags to differentiate azure testing because we currently do not have azure access setup for +// CircleCI. + +package azure + +import ( + "fmt" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/random" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + // subscriptionID is overridden by the environment variable "ARM_SUBSCRIPTION_ID" + subscriptionID = "" + rgName = "terratest-rg" +) + +func TestGetVirtualNetworkClient(t *testing.T) { + t.Parallel() + + vnetClient, err := GetVirtualNetworkClient(subscriptionID) + + require.NoError(t, err) + assert.NotEmpty(t, *vnetClient) +} + +func TestGetPublicIPClient(t *testing.T) { + t.Parallel() + + ipClient, err := GetPublicIPClient(subscriptionID) + + require.NoError(t, err) + assert.NotEmpty(t, *ipClient) +} + +func TestCheckPublicDNSNameAvailability(t *testing.T) { + t.Parallel() + + randomsuffix := strings.ToLower(fmt.Sprintf("%s%s", random.UniqueId(), random.UniqueId())) + nonExistentDomainNameLabel := fmt.Sprintf("nonexistent-%s", randomsuffix) + location := GetRandomStableRegion(t, []string{"northeurope", "eastus", "westus", "centralus"}, []string{}, subscriptionID) + + available := CheckPublicDNSNameAvailability(t, location, nonExistentDomainNameLabel, subscriptionID) + + assert.True(t, available) +} + +/* +The below tests are currently stubbed out, with the expectation that they will throw errors. +If/when methods to create and delete network resources are added, these tests can be extended. +(see AWS S3 tests for reference). +*/ + +func TestGetVirtualNetworkE(t *testing.T) { + t.Parallel() + + vnetName := "" + + _, err := GetVirtualNetworkE(t, vnetName, rgName, subscriptionID) + + require.Error(t, err) +} + +func TestGetSubnetsForVirtualNetworkE(t *testing.T) { + t.Parallel() + + vnetName := "" + + _, err := GetSubnetsForVirtualNetworkE(t, vnetName, rgName, subscriptionID) + + require.Error(t, err) +} + +func TestGetTagsForVirtualNetworkE(t *testing.T) { + t.Parallel() + + vnetName := "" + + _, err := GetTagsForVirtualNetworkE(t, vnetName, rgName, subscriptionID) + + require.Error(t, err) +} + +func TestGetNetworkSecurityGroupForSubnetE(t *testing.T) { + t.Parallel() + + vnetName := "" + subnetName := "" + + _, err := GetNetworkSecurityGroupForSubnetE(t, subnetName, vnetName, rgName, subscriptionID) + + require.Error(t, err) +} + +func TestGetPublicIPE(t *testing.T) { + t.Parallel() + + publicIPName := "" + + _, err := GetPublicIPE(t, rgName, publicIPName, subscriptionID) + + require.Error(t, err) +} diff --git a/test/terraform_azure_network_example_test.go b/test/terraform_azure_network_example_test.go new file mode 100644 index 000000000..37016289b --- /dev/null +++ b/test/terraform_azure_network_example_test.go @@ -0,0 +1,98 @@ +// +build azure + +// NOTE: We use build tags to differentiate azure testing because we currently do not have azure access setup for +// CircleCI. + +package test + +import ( + "fmt" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/azure" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" +) + +// An example of how to test the Terraform module in examples/terraform-azure-network-example using Terratest. +func TestTerraformAzureNetworkExample(t *testing.T) { + t.Parallel() + + // subscriptionID is overridden by the environment variable "ARM_SUBSCRIPTION_ID" + subscriptionID := "" + region := azure.GetRandomStableRegion(t, []string{}, []string{"australiacentral2"}, subscriptionID) + firstSubnetName := "terratest-subnet1" + secondSubnetName := "terratest-subnet2" + domainNameLabel := fmt.Sprintf("terratest-example-dnslabel-%s", strings.ToLower(random.UniqueId())) + publicIPName := "terratest-example-ip" + + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: "../examples/terraform-azure-network-example", + + // Variables to pass to our Terraform code using -var options + Vars: map[string]interface{}{ + "location": region, + "public_ip_domain_name_label": domainNameLabel, + "first_subnet_name": firstSubnetName, + "second_subnet_name": secondSubnetName, + "public_ip_name": publicIPName, + }, + } + + // At the end of the test, run `terraform destroy` to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // This will run `terraform init` and `terraform apply` and fail the test if there are any errors + terraform.InitAndApply(t, terraformOptions) + + // Run `terraform output` to get the value of an output variable + rgName := terraform.Output(t, terraformOptions, "resource_group_name") + vnetName := terraform.Output(t, terraformOptions, "virtual_network_name") + firstSubnetAddressPrefix := terraform.Output(t, terraformOptions, "first_subnet_address") + secondSubnetAddressPrefix := terraform.Output(t, terraformOptions, "second_subnet_address") + publicIPAddress := terraform.Output(t, terraformOptions, "public_ip_address") + publicIPFqdn := terraform.Output(t, terraformOptions, "public_ip_fqdn") + + // Verify that the Virtual Network is now present in the Resource Group + azure.AssertVirtualNetworkExists(t, vnetName, rgName, subscriptionID) + + // Lookup the subnets in the given Virtual Network + actualSubnets := azure.GetSubnetsForVirtualNetwork(t, vnetName, rgName, subscriptionID) + + // Verify that each subnet has the expected address prefix + actualFirstSubnet := filterSubnetsByName(actualSubnets, firstSubnetName) + assert.Equal(t, firstSubnetAddressPrefix, actualFirstSubnet.AddressPrefix) + actualSecondSubnet := filterSubnetsByName(actualSubnets, secondSubnetName) + assert.Equal(t, secondSubnetAddressPrefix, actualSecondSubnet.AddressPrefix) + + // Verify that each subnet has the expected Network Security Group + actualFirstSubnetNsg := azure.GetNetworkSecurityGroupForSubnet(t, firstSubnetName, vnetName, rgName, subscriptionID) + assert.Equal(t, "subnet1-nsg", actualFirstSubnetNsg.Name) + actualSecondSubnetNsg := azure.GetNetworkSecurityGroupForSubnet(t, secondSubnetName, vnetName, rgName, subscriptionID) + assert.Equal(t, "subnet2-nsg", actualSecondSubnetNsg.Name) + + // Lookup the actual Public IP resource + actualIP := azure.GetPublicIP(t, rgName, publicIPName, subscriptionID) + + // Verify that the Public IP resource has the expected properties + assert.Equal(t, publicIPAddress, actualIP.IPAddress) + assert.Equal(t, publicIPFqdn, actualIP.FullDNSName) + + // Verify that our Public IP's domain name label is not available anymore + available := azure.CheckPublicDNSNameAvailability(t, region, domainNameLabel, subscriptionID) + assert.False(t, available) +} + +func filterSubnetsByName(subnets []azure.Subnet, name string) azure.Subnet { + out := azure.Subnet{} + for _, s := range subnets { + if s.Name == name { + out = s + break + } + } + return out +}