Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding Azure Network module #631

Merged
merged 25 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions examples/azure/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Terratest Configuration and Setup

Terratest uses Go to make calls to Azure through the azure-sdk-for-go library and independently confirm the actual Azure resource property matches the expected state provided by Terraform output variables.
Terratest uses Go to make calls to Azure through the azure-sdk-for-go library and independently confirm the actual Azure resource property matches the expected state provided by Terraform output variables.

* Instructions for running each Azure Terratest module are included in each Terraform example sub-folder:
* examples/azure/terraform-azure-*-example/README.md
* Tests wich assert against expected Terraform output values are located in the the respective go files of the folder:
* [test/azure/terraform-azure-*-example_test.go](../../test/azure)
* Test APIs which provide the actual Azure resource property values via the azure-sdk-for-go are located in the folder:
* [modules/azure](../../modules/azure)
- Instructions for running each Azure Terratest module are included in each Terraform example sub-folder:
- examples/azure/terraform-azure-\*-example/README.md
- Tests which assert against expected Terraform output values are located in the the respective go files of the folder:
- [test/azure/terraform-azure-\*-example_test.go](../../test/azure)
- Test APIs which provide the actual Azure resource property values via the azure-sdk-for-go are located in the folder:
- [modules/azure](../../modules/azure)

## Go Dependencies

Expand Down Expand Up @@ -44,7 +44,7 @@ export ARM_SUBSCRIPTION_ID=your_subscription_id
export ARM_TENANT_ID=your_tenant_id
```

Note, in a Windows environment, these should be set as **system environment variables**. We can use a PowerShell console with administrative rights to update these environment variables:
Note, in a Windows environment, these should be set as **system environment variables**. We can use a PowerShell console with administrative rights to update these environment variables:

```powershell
[System.Environment]::SetEnvironmentVariable("ARM_CLIENT_ID",$your_app_id,[System.EnvironmentVariableTarget]::Machine)
Expand Down
43 changes: 43 additions & 0 deletions examples/azure/terraform-azure-network-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Terraform Azure Network Example

This folder contains a simple Terraform module that deploys 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 to a Virtual Network two Network Interface Cards, one with an internal only IP and another with an internal and external Public IP.

- A [Virtual Network](https://azure.microsoft.com/en-us/services/virtual-network/) module that includes the following resources:
- [Virtual Network](https://docs.microsoft.com/en-us/azure/virtual-network/) with the name specified in the `virtual_network_name` variable.
- [Subnet](https://docs.microsoft.com/en-us/rest/api/virtualnetwork/subnets) with the name specified in the `subnet_name` variable.
- [Public Address](https://docs.microsoft.com/en-us/azure/virtual-network/public-ip-addresses) with the name specified in the `public_ip_name` variable.
- [Internal Network Interface](https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-network-interface) with the name specified in the `network_interface_internal` variable.
- [ExternalNetwork Interface](https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-network-interface) with the name specified in the `network_interface_external` variable.

Check out [test/azure/terraform_azure_network_test.go](/test/azure/terraform_azure_network_example_test.go) to see how you can write
automated tests for this module.

Note that the Azure Virtual Network, Subnet, Network Interface and Public IP resources in this module don't actually do anything; it just runs the resources for
demonstration purposes.

**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 [Azure](https://azure.microsoft.com/)
1. Configure your Azure credentials using one of the [supported methods for Azure CLI
tools](https://docs.microsoft.com/en-us/cli/azure/azure-cli-configuration?view=azure-cli-latest)
1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`
1. Ensure [environment variables](../README.md#review-environment-variables) are available
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 [Azure](https://azure.microsoft.com/)
1. Configure your Azure credentials using one of the [supported methods for Azure CLI
tools](https://docs.microsoft.com/en-us/cli/azure/azure-cli-configuration?view=azure-cli-latest)
1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`
1. Configure your Terratest [Go test environment](../README.md)
1. `cd test/azure`
1. `go build terraform_azure_network_example_test.go`
1. `go test -v -run TestTerraformAzureNetworkExample`
98 changes: 98 additions & 0 deletions examples/azure/terraform-azure-network-example/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY AN AZURE NETWORK
# This is an example of how to deploy frequent Azure Networking Resources. Note this network doesn't actually do
# anything and is only created for the example to test their commonly needed and integrated properties.
# ---------------------------------------------------------------------------------------------------------------------
# See test/azure/terraform_azure_network_example_test.go for how to write automated tests for this code.
# ---------------------------------------------------------------------------------------------------------------------

provider "azurerm" {
version = "~>2.20"
features {}
}

# ---------------------------------------------------------------------------------------------------------------------
# PIN TERRAFORM VERSION TO >= 0.12
# The examples have been upgraded to 0.12 syntax
# ---------------------------------------------------------------------------------------------------------------------

terraform {
# This module is now only being tested with Terraform 0.13.x. However, to make upgrading easier, we are setting
# 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it
# forwards compatible with 0.13.x code.
required_version = ">= 0.12.26"
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY A RESOURCE GROUP
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_resource_group" "net" {
name = "terratest-network-rg-${var.postfix}"
location = var.location
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY VIRTUAL NETWORK
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_virtual_network" "net" {
name = "vnet-${var.postfix}"
location = azurerm_resource_group.net.location
resource_group_name = azurerm_resource_group.net.name
address_space = ["10.0.0.0/16"]
dns_servers = [var.dns_ip_01, var.dns_ip_02]
}

resource "azurerm_subnet" "net" {
name = "subnet-${var.postfix}"
resource_group_name = azurerm_resource_group.net.name
virtual_network_name = azurerm_virtual_network.net.name
address_prefixes = [var.subnet_prefix]
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY PRIVATE NETWORK INTERFACE
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_network_interface" "net01" {
name = "nic-private-${var.postfix}"
location = azurerm_resource_group.net.location
resource_group_name = azurerm_resource_group.net.name

ip_configuration {
name = "terratestconfiguration1"
subnet_id = azurerm_subnet.net.id
private_ip_address_allocation = "Static"
private_ip_address = var.private_ip
}
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY PUBLIC ADDRESS AND NETWORK INTERFACE
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_public_ip" "net" {
name = "pip-${var.postfix}"
resource_group_name = azurerm_resource_group.net.name
location = azurerm_resource_group.net.location
allocation_method = "Static"
ip_version = "IPv4"
sku = "Basic"
idle_timeout_in_minutes = "4"
domain_name_label = var.domain_name_label
}

resource "azurerm_network_interface" "net02" {
name = "nic-public-${var.postfix}"
location = azurerm_resource_group.net.location
resource_group_name = azurerm_resource_group.net.name

ip_configuration {
name = "terratestconfiguration1"
subnet_id = azurerm_subnet.net.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.net.id
}
}

25 changes: 25 additions & 0 deletions examples/azure/terraform-azure-network-example/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
output "resource_group_name" {
value = azurerm_resource_group.net.name
}

output "virtual_network_name" {
value = azurerm_virtual_network.net.name
}

output "subnet_name" {
value = azurerm_subnet.net.name
}

output "public_address_name" {
value = azurerm_public_ip.net.name
}

output "network_interface_internal" {
value = azurerm_network_interface.net01.name
}

output "network_interface_external" {
value = azurerm_network_interface.net02.name
}


60 changes: 60 additions & 0 deletions examples/azure/terraform-azure-network-example/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# ---------------------------------------------------------------------------------------------------------------------
# ENVIRONMENT VARIABLES
# Define these secrets as environment variables
# ---------------------------------------------------------------------------------------------------------------------

# ARM_CLIENT_ID
# ARM_CLIENT_SECRET
# ARM_SUBSCRIPTION_ID
# ARM_TENANT_ID

# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED PARAMETERS
# You must provide a value for each of these parameters.
# ---------------------------------------------------------------------------------------------------------------------

variable "domain_name_label" {
description = "The Domain Name Label for the Public IP Address"
type = string
}

# ---------------------------------------------------------------------------------------------------------------------
# OPTIONAL PARAMETERS
# These parameters have reasonable defaults.
# ---------------------------------------------------------------------------------------------------------------------

variable "dns_ip_01" {
description = "The first DNS Server IP for the Virtual Network"
type = string
default = "10.0.0.5"
}

variable "dns_ip_02" {
description = "The second DNS Server IP for the Virtual Network"
type = string
default = "10.0.0.6"
}

variable "location" {
description = "The Azure Region to deploy resources too"
type = string
default = "East US"
}

variable "postfix" {
description = "The postfix that will be attached to all resources deployed"
type = string
default = "resource"
}

variable "private_ip" {
description = "The Static Private IP for the Internal NIC"
type = string
default = "10.0.20.5"
}

variable "subnet_prefix" {
description = "The subnet range of IPs for the Virtual Network"
type = string
default = "10.0.20.0/24"
}
3 changes: 3 additions & 0 deletions modules/azure/availabilityset.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ func AvailabilitySetExists(t testing.TestingT, avsName string, resGroupName stri
func AvailabilitySetExistsE(t testing.TestingT, avsName string, resGroupName string, subscriptionID string) (bool, error) {
_, err := GetAvailabilitySetE(t, avsName, resGroupName, subscriptionID)
if err != nil {
if ResourceNotFoundErrorExists(err) {
return false, nil
}
return false, err
}
return true, nil
Expand Down
2 changes: 1 addition & 1 deletion modules/azure/compute.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
)

// GetVirtualMachineClientE is a helper function that will setup an Azure Virtual Machine client on your behalf
// GetVirtualMachineClientE is a helper function that will setup an Azure Virtual Machine client on your behalf.
func GetVirtualMachineClientE(subscriptionID string) (*compute.VirtualMachinesClient, error) {
// Validate Azure subscription ID
subscriptionID, err := getTargetAzureSubscription(subscriptionID)
Expand Down
34 changes: 33 additions & 1 deletion modules/azure/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package azure

import "fmt"
import (
"fmt"

"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
)

// SubscriptionIDNotFound is an error that occurs when the Azure Subscription ID could not be found or was not provided
type SubscriptionIDNotFound struct{}
Expand All @@ -16,6 +21,21 @@ func (err ResourceGroupNameNotFound) Error() string {
return fmt.Sprintf("Could not find an Azure Resource Group name in expected environment variable %s and one was not provided for this test.", AzureResGroupName)
}

// FailedToParseError is returned when an object cannot be parsed
type FailedToParseError struct {
objectType string
objectID string
}

func (err FailedToParseError) Error() string {
return fmt.Sprintf("Failed to parse %s with ID %s", err.objectType, err.objectID)
}

// NewFailedToParseError creates a new not found error when an expected object is not found in the search space
func NewFailedToParseError(objectType string, objectID string) FailedToParseError {
return FailedToParseError{objectType, objectID}
}

// NotFoundError is returned when an expected object is not found in the search spa
type NotFoundError struct {
objectType string
Expand All @@ -31,3 +51,15 @@ func (err NotFoundError) Error() string {
func NewNotFoundError(objectType string, objectID string, region string) NotFoundError {
return NotFoundError{objectType, objectID, region}
}

// ResourceNotFoundErrorExists checks the Service Error Code for the 'Resource Not Found' error
func ResourceNotFoundErrorExists(err error) bool {
if err != nil {
if autorestError, ok := err.(autorest.DetailedError); ok {
if requestError, ok := autorestError.Original.(*azure.RequestError); ok {
return (requestError.ServiceError.Code == "ResourceNotFound")
}
}
}
return false
}
Loading