Skip to content

Commit

Permalink
feat: Add network module (#401)
Browse files Browse the repository at this point in the history
* Add network module

* address code review comments

* rename the tfvars file
  • Loading branch information
helayoty authored Jul 31, 2020
1 parent 5a6debf commit c16df5a
Show file tree
Hide file tree
Showing 13 changed files with 334 additions and 0 deletions.
36 changes: 36 additions & 0 deletions infra/modules/providers/azure/network/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Network

This Terraform module template provide a vnet and subnet resources in Azure for the given resource group.

Information about Azure virtual network can be found [here](https://docs.microsoft.com/en-us/azure/virtual-network/) as well as for the subnet [here](https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-manage-subnet#add-a-subnet)

## Current Features:

- Provisions a virtual network resource for a given resource group
- Provisions a set of subnets for the created VNet

## Usage Example

Virtual network (vnet) usage example:

```hcl
module "network" {
source = "../../modules/providers/azure/network"
vnet_name = var.vnet_name
resource_group_name = data.azurerm_resource_group.vnet.name
location = data.azurerm_resource_group.vnet.location
address_space = azurerm_virtual_network.address_space
subnet_names = ["subnet1", "subent2"]
subnet_prefixes = ["10.0.1.0/24", "10.0.2.0/24"]
}
```

## Outputs

Once the deployments are completed successfully, the output for the current module will be available in [output.tf](./output.tf)

## Argument Reference

Supported arguments for this module are available in [variables.tf](./variables.tf).
More resources on this module are provided [here](https://www.terraform.io/docs/providers/azurerm/r/virtual_network.html) and [here](https://www.terraform.io/docs/providers/azurerm/r/subnet.html)

34 changes: 34 additions & 0 deletions infra/modules/providers/azure/network/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module "azure-provider" {
source = "../provider"
}

data "azurerm_resource_group" "vnet" {
name = var.resource_group_name
}

resource "azurerm_virtual_network" "vnet" {
name = var.vnet_name
location = data.azurerm_resource_group.vnet.location
resource_group_name = data.azurerm_resource_group.vnet.name
address_space = var.address_space
}


resource "azurerm_subnet" "subnet" {
count = length(var.subnets)
name = var.subnets[count.index].name
resource_group_name = data.azurerm_resource_group.vnet.name
address_prefix = var.subnets[count.index].address_prefix
virtual_network_name = var.vnet_name
service_endpoints = var.subnets[count.index].service_endpoints

delegation {
name = var.subnets[count.index].delegation.name
service_delegation {
name = var.subnets[count.index].delegation.service_delegation.name
actions = var.subnets[count.index].delegation.service_delegation.actions
}
}

depends_on = [azurerm_virtual_network.vnet]
}
9 changes: 9 additions & 0 deletions infra/modules/providers/azure/network/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "virtual_network_id" {
description = "The id of the newly created vnet"
value = azurerm_virtual_network.vnet.id
}

output "subnet_ids" {
description = "If supplied this represents the subnet IDs that should be allowed to access this resource"
value = azurerm_subnet.subnet[*].id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
resource_group_name = ""
vnet_name = ""
subnets = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
RESOURCE_GROUP_NAME=""
VNET_NAME=""
SUBNET_NAME=""
ADDRESS_SPACE="10.2.0.0/16"
ADDRESS_PREFIX=""
38 changes: 38 additions & 0 deletions infra/modules/providers/azure/network/tests/integration/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package integration

import (
"testing"

"github.com/microsoft/cobalt/test-harness/terratest-extensions/modules/azure"
"github.com/microsoft/terratest-abstraction/integration"
"github.com/stretchr/testify/require"
)

// healthCheck - Asserts that the deployment was successful.
func healthCheck(t *testing.T, provisionState *string) {
require.Equal(t, "Succeeded", *provisionState, "The deployment hasn't succeeded.")
}

// VerifyCreatedVnets - validate the created subnets
func VerifyCreatedVnets(subscriptionID, resourceGroupName, vnetOutputName string, vnetID string) func(goTest *testing.T, output integration.TerraformOutput) {
return func(goTest *testing.T, output integration.TerraformOutput) {

expectedVnetList := output[vnetID].(interface{})

actualVnetList := azure.VnetsList(goTest, subscriptionID, resourceGroupName)

require.Subset(goTest, *actualVnetList, expectedVnetList, "Vnet does not exist in the resource group")
}
}

// VerifyCreatedSubnets - validate the created subnets
func VerifyCreatedSubnets(subscriptionID, resourceGroupName, subnetOutputName string, vnetName string, subnetID string) func(goTest *testing.T, output integration.TerraformOutput) {
return func(goTest *testing.T, output integration.TerraformOutput) {

expectedSubnetList := output[subnetID].([]interface{})

actualSubnetList := azure.VnetSubnetsList(goTest, subscriptionID, resourceGroupName, vnetName)

require.Subset(goTest, actualSubnetList, expectedSubnetList, "subnet does not exist in the VNet")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package integration

import (
"os"
"testing"

"github.com/microsoft/cobalt/infra/modules/providers/azure/network/tests"
"github.com/microsoft/terratest-abstraction/integration"
)

var subscriptionID = os.Getenv("ARM_SUBSCRIPTION_ID")

func TestNetwork(t *testing.T) {
testFixture := integration.IntegrationTestFixture{
GoTest: t,
TfOptions: tests.NetworkTFOptions,
ExpectedTfOutputCount: 2,
TfOutputAssertions: []integration.TerraformOutputValidation{
VerifyCreatedVnets(subscriptionID,
tests.ResourceGroupName,
tests.VnetName,
"virtual_network_id",
),
VerifyCreatedSubnets(subscriptionID,
tests.ResourceGroupName,
tests.SubnetName,
tests.VnetName,
"subnet_ids",
),
},
}
integration.RunIntegrationTests(&testFixture)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
resource_group_name = ""
vnet_name = ""
subnet_names = [""]
subnet_prefixes = [""]
28 changes: 28 additions & 0 deletions infra/modules/providers/azure/network/tests/tf_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package tests

import (
"os"

"github.com/gruntwork-io/terratest/modules/terraform"
)

// ResourceGroupName - The Resource Group Name
var ResourceGroupName = os.Getenv("RESOURCE_GROUP_NAME")

// VnetName - The name of vnet
var VnetName = os.Getenv("VNET_NAME")

// SubnetName - The name of subnet
var SubnetName = os.Getenv("SUBNET_NAME")

// AddressSpace -
var AddressSpace = os.Getenv("ADDRESS_SPACE")

// AddressPrefix -
var AddressPrefix = os.Getenv("ADDRESS_PREFIX")

// NetworkTFOptions common terraform options used for unit and integration testing
var NetworkTFOptions = &terraform.Options{
TerraformDir: "../../",
VarFiles: []string{"./tests/test.tfvars"},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package unit

import (
"encoding/json"
"strings"
"testing"

"github.com/gruntwork-io/terratest/modules/random"
tests "github.com/microsoft/cobalt/infra/modules/providers/azure/network/tests"
"github.com/microsoft/terratest-abstraction/unit"
)

var resourceCount = 3

// helper function to parse blocks of JSON into a generic Go map
func asMap(t *testing.T, jsonString string) map[string]interface{} {
var theMap map[string]interface{}
if err := json.Unmarshal([]byte(jsonString), &theMap); err != nil {
t.Fatal(err)
}
return theMap
}

func TestNetworkDeployment_Unit(t *testing.T) {

expectedVnetResult := asMap(t, `{
"name": "`+tests.VnetName+`",
"resource_group_name": "`+tests.ResourceGroupName+`",
"address_space": ["`+tests.AddressSpace+`"]
}`)

expectedSubnetResult := asMap(t, `{
"name": "`+tests.SubnetName+`",
"resource_group_name": "`+tests.ResourceGroupName+`",
"address_prefix": ["`+tests.AddressPrefix+`"],
"virtual_network_name": "`+tests.VnetName+`"
}`)

testFixture := unit.UnitTestFixture{
GoTest: t,
TfOptions: tests.NetworkTFOptions,
PlanAssertions: nil,
ExpectedResourceCount: resourceCount,
ExpectedResourceAttributeValues: unit.ResourceDescription{
"azurerm_virtual_network.vnet": expectedVnetResult,
"azurerm_subnet.subnet[0]": expectedSubnetResult,
},
}

unit.RunUnitTests(&testFixture)
}
32 changes: 32 additions & 0 deletions infra/modules/providers/azure/network/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
variable "resource_group_name" {
description = "(Required) The name of the resource group in which to create the Container Registry. Changing this forces a new resource to be created."
type = string
}

variable "vnet_name" {
description = "The vnet integration name."
type = string
}

variable "address_space" {
description = "The address space that is used the virtual network. You can supply more than one address space. Changing this forces a new resource to be created"
type = list(string)
default = ["10.2.0.0/16"]
}

variable "subnets" {
description = ""
type = list(object({
name = string
resource_group_name = string
address_prefix = string
service_endpoints = list(string)
delegation = object({
name = string
service_delegation = object({
name = string
actions = list(string)
})
})
}))
}
8 changes: 8 additions & 0 deletions infra/modules/providers/azure/network/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
terraform {
required_version = ">= 0.12"
}

provider "azurerm" {
version = "~> 2.9.0"
features {}
}
53 changes: 53 additions & 0 deletions test-harness/terratest-extensions/modules/azure/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,56 @@ func VnetSubnetsList(t *testing.T, subscriptionID string, resourceGroupName stri
}
return subnets
}

// VnetsListE - Return the vnets that exist within a given resource group
func VnetsListE(subscriptionID string, resourceGroupName string) (*[]string, error) {

lsList, err := VnetsIDListE(subscriptionID, resourceGroupName)
if err != nil {
return nil, err
}

results := make([]string, 0)
for _, linkedService := range *lsList {
results = append(results, *linkedService.ID)
if err != nil {
return nil, err
}
}

return &results, nil
}

// VnetsIDListE - Returns the ID of the vnets in the resource group
func VnetsIDListE(subscriptionID string, resourceGroupName string) (*[]network.VirtualNetwork, error) {
client, err := vnetClient(subscriptionID)
if err != nil {
return nil, err
}

iteratorVnet, err := client.ListComplete(context.Background(), resourceGroupName)
if err != nil {
return nil, err
}

lsList := make([]network.VirtualNetwork, 0)

for iteratorVnet.NotDone() {
lsList = append(lsList, iteratorVnet.Value())
err = iteratorVnet.Next()
if err != nil {
return nil, err
}
}
return &lsList, err

}

// VnetsList - Like VnetsListE but fails in the case an error is returned
func VnetsList(t *testing.T, subscriptionID string, resourceGroupName string) *[]string {
vnets, err := VnetsListE(subscriptionID, resourceGroupName)
if err != nil {
t.Fatal(err)
}
return vnets
}

0 comments on commit c16df5a

Please sign in to comment.