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(sample): Add samples how to use test skeleton with VM-Series modules #2

Merged
merged 4 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 3 additions & 2 deletions .github/workflows/pr_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ jobs:
- name: Download Go dependencies
run: go get ./... && go mod tidy

- name: Test code
run: go test -v ./...
### Uncomment it, when tests are prepared
# - name: Test code
# run: go test -v ./...
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# This is a minimal list, OK to add things per repo
# mac specific
.DS_Store
.ansible
.azure/
.bash_history
# don't check storage creds into GH
.boto
.cache
*.code-workspace
# may contain gcloud files
.config
.gitconfig
.local
# .netrc contains secrets for service tokens
.netrc
*.plan
.sentinel
# .ssh dir may contain private keys
.ssh
.terrascan
*.pem
.pem
# Terraform dot files
.terraform
.terraform.lock.hcl
.terraformrc
**/.terraform/*
.terraform.d
*.tfstate
*.tfstate.*
.terragrunt-cache
.vscode
.idea
**/test_report.html
# Palo auth codes
authcodes
# Crash log files
crash.log
credentials.json
# Palo specific
init-cfg.txt
temp
terraform.rc
tmp
# Auto variables
terraform.tfvars
terraform.tfvars.json
*.auto.tfvars
*.auto.tfvars.json
modules/asg/lambda_payload.zip
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ Test skeleton with Terratest in Go used to execute integration and e2e tests for
- [terraform-azurerm-vmseries-modules](https://github.com/PaloAltoNetworks/terraform-azurerm-vmseries-modules)
- [terraform-google-vmseries-modules](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules)

## Usage

Terratest can be used to check whole examples and single modules. Below there are few samples of usage:
- [check VM-Series example](samples/vmseries_example_plan_and_deploy/)
- [verify module by checking errors from Terraform plan](samples/vmseries_module_check_terraform_plan_errors/)
- [verify module by checking output and accessing URL after deploying VM-Series](samples/vmseries_module_check_terraform_output_and_vmseries_url/)
- [verify module by deplyoing additional changes after initial deployment](samples/vmseries_module_check_additional_changes_after_deployment/)

In order to execute tests, go to folder with sample and use command:

```
go test -v -timeout 30m -count=1 ./...
```

For tests with example there is possibility to run only plan (using above command) or to deploy whole example using command:

```
DO_APPLY=true go test -v -timeout 30m -count=1 ./...
```

Some of the tests are quick (e.g. checking plan erros), so then there is no need to change default timeout (10 minutes) and below command can be used:

```
go test -v -count=1 ./...
```


## License

This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details
95 changes: 95 additions & 0 deletions samples/vmseries_example_plan_and_deploy/example.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
### GENERAL
region = "eu-central-1" # TODO: update here
name_prefix = "example-" # TODO: update here

global_tags = {
ManagedBy = "terraform"
Application = "Palo Alto Networks VM-Series NGFW"
Owner = "PS Team"
}

ssh_key_name = "example-ssh-key" # TODO: update here

### VPC
vpcs = {
# Do not use `-` in key for VPC as this character is used in concatation of VPC and subnet for module `subnet_set` in `main.tf`
security_vpc = {
name = "security-vpc"
cidr = "10.100.0.0/16"
nacls = {}
security_groups = {
vmseries_mgmt = {
name = "vmseries_mgmt"
rules = {
all_outbound = {
description = "Permit All traffic outbound"
type = "egress", from_port = "0", to_port = "0", protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
https = {
description = "Permit HTTPS"
type = "ingress", from_port = "443", to_port = "443", protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # TODO: update here (replace 0.0.0.0/0 by your IP range)
}
ssh = {
description = "Permit SSH"
type = "ingress", from_port = "22", to_port = "22", protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # TODO: update here (replace 0.0.0.0/0 by your IP range)
}
}
}
}
subnets = {
# Do not modify value of `set=`, it is an internal identifier referenced by main.tf
# Value of `nacl` must match key of objects stored in `nacls`
"10.100.0.0/24" = { az = "eu-central-1a", set = "mgmt", nacl = null }
}
routes = {
# Value of `vpc_subnet` is built from key of VPCs concatenate with `-` and key of subnet in format: `VPCKEY-SUBNETKEY`
# Value of `next_hop_key` must match keys use to create TGW attachment, IGW, GWLB endpoint or other resources
# Value of `next_hop_type` is internet_gateway, nat_gateway, transit_gateway_attachment or gwlbe_endpoint
mgmt_default = {
vpc_subnet = "security_vpc-mgmt"
to_cidr = "0.0.0.0/0"
next_hop_key = "security_vpc"
next_hop_type = "internet_gateway"
}
}
}
}

### VM-SERIES
vmseries = {
vmseries = {
instances = {
"01" = { az = "eu-central-1a" }
}

# Value of `panorama-server`, `auth-key`, `dgname`, `tplname` can be taken from plugin `sw_fw_license`
bootstrap_options = {
mgmt-interface-swap = "disable"
plugin-op-commands = "aws-gwlb-inspect:enable,aws-gwlb-overlay-routing:enable" # TODO: update here
dhcp-send-hostname = "no" # TODO: update here
dhcp-send-client-id = "no" # TODO: update here
dhcp-accept-server-hostname = "no" # TODO: update here
dhcp-accept-server-domain = "no" # TODO: update here
}

panos_version = "10.2.3" # TODO: update here
ebs_kms_id = "alias/aws/ebs" # TODO: update here

# Value of `vpc` must match key of objects stored in `vpcs`
vpc = "security_vpc"

interfaces = {
mgmt = {
device_index = 0
private_ip = "10.100.0.4"
security_group = "vmseries_mgmt"
vpc_subnet = "security_vpc-mgmt"
create_public_ip = true
source_dest_check = true
}
}
}
}
156 changes: 156 additions & 0 deletions samples/vmseries_example_plan_and_deploy/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
### VPCS ###

module "vpc" {
source = "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules//modules/vpc"

for_each = var.vpcs

name = "${var.name_prefix}${each.value.name}"
cidr_block = each.value.cidr
nacls = each.value.nacls
security_groups = each.value.security_groups
create_internet_gateway = true
enable_dns_hostnames = true
enable_dns_support = true
instance_tenancy = "default"
}

### SUBNETS ###

module "subnet_sets" {
for_each = toset(flatten([for _, v in { for vk, vv in var.vpcs : vk => distinct([for sk, sv in vv.subnets : "${vk}-${sv.set}"]) } : v]))
source = "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules//modules/subnet_set"

name = split("-", each.key)[1]
vpc_id = module.vpc[split("-", each.key)[0]].id
has_secondary_cidrs = module.vpc[split("-", each.key)[0]].has_secondary_cidrs
nacl_associations = {
for i in flatten([
for vk, vv in var.vpcs : [
for sk, sv in vv.subnets :
{
az : sv.az,
nacl_id : lookup(module.vpc[split("-", each.key)[0]].nacl_ids, sv.nacl, null)
} if sv.nacl != null && each.key == "${vk}-${sv.set}"
]]) : i.az => i.nacl_id
}
cidrs = {
for i in flatten([
for vk, vv in var.vpcs : [
for sk, sv in vv.subnets :
{
cidr : sk,
subnet : sv
} if each.key == "${vk}-${sv.set}"
]]) : i.cidr => i.subnet
}
}

### ROUTES ###

locals {
vpc_routes = flatten(concat([
for vk, vv in var.vpcs : [
for rk, rv in vv.routes : {
subnet_key = rv.vpc_subnet
to_cidr = rv.to_cidr
next_hop_set = (
rv.next_hop_type == "internet_gateway" ? module.vpc[rv.next_hop_key].igw_as_next_hop_set : null
)
}
]
]))
}

module "vpc_routes" {
for_each = { for route in local.vpc_routes : "${route.subnet_key}_${route.to_cidr}" => route }
source = "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules//modules/vpc_route"

route_table_ids = module.subnet_sets[each.value.subnet_key].unique_route_table_ids
to_cidr = each.value.to_cidr
next_hop_set = each.value.next_hop_set
}

### IAM ROLES AND POLICIES ###

resource "aws_iam_role" "vm_series_ec2_iam_role" {
name = "${var.name_prefix}vmseries"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {"Service": "ec2.amazonaws.com"}
}
]
}
EOF
}

resource "aws_iam_role_policy" "vm_series_ec2_iam_policy" {
role = aws_iam_role.vm_series_ec2_iam_role.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"cloudwatch:PutMetricAlarm",
"cloudwatch:GetMetricData",
"cloudwatch:PutMetricData",
"cloudwatch:ListMetrics",
"cloudwatch:DescribeAlarms",
"logs:CreateLogGroup"
],
"Resource": [
"*"
],
"Effect": "Allow"
}
]
}
EOF
}

resource "aws_iam_instance_profile" "vm_series_iam_instance_profile" {

name = "${var.name_prefix}vmseries_instance_profile"
role = aws_iam_role.vm_series_ec2_iam_role.name
}

### VM-Series INSTANCES

locals {
vmseries_instances = flatten([for kv, vv in var.vmseries : [for ki, vi in vv.instances : { group = kv, instance = ki, az = vi.az, common = vv }]])

bootstrap_options = { for i, j in var.vmseries : i => [
for k, v in j.bootstrap_options : "${k}=${v}"
] }
}

module "vmseries" {
for_each = { for vmseries in local.vmseries_instances : "${vmseries.group}-${vmseries.instance}" => vmseries }
source = "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules//modules/vmseries"

name = "${var.name_prefix}${each.key}"
vmseries_version = each.value.common.panos_version

interfaces = {
for k, v in each.value.common.interfaces : k => {
device_index = v.device_index
private_ips = [v.private_ip]
security_group_ids = try([module.vpc[each.value.common.vpc].security_group_ids[v.security_group]], [])
source_dest_check = try(v.source_dest_check, false)
subnet_id = module.subnet_sets[v.vpc_subnet].subnets[each.value.az].id
create_public_ip = try(v.create_public_ip, false)
}
}

bootstrap_options = join(";", compact(concat(local.bootstrap_options[each.value.group], ["hostname=${var.name_prefix}${each.key}"])))

iam_instance_profile = aws_iam_instance_profile.vm_series_iam_instance_profile.name
ssh_key_name = var.ssh_key_name
tags = var.global_tags
}
47 changes: 47 additions & 0 deletions samples/vmseries_example_plan_and_deploy/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package vmseries_example_plan_and_deploy

import (
"fmt"
"math/rand"
"os"
"testing"
"time"

"github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
"github.com/gruntwork-io/terratest/modules/logger"
"github.com/gruntwork-io/terratest/modules/terraform"
)

func TestVmseriesExamplePlanAndDeploy(t *testing.T) {
// prepare random prefix
source := rand.NewSource(time.Now().UnixNano())
random := rand.New(source)
number := random.Intn(1000)
namePrefix := fmt.Sprintf("terra%d-", number)

// define options for Terraform
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: ".",
VarFiles: []string{"example.tfvars"},
Vars: map[string]interface{}{
"name_prefix": namePrefix,
"ssh_key_name": "test-ssh-key",
},
Logger: logger.Default,
Lock: true,
Upgrade: true,
SetVarsAfterVarFiles: true,
})

// prepare list of items to check
assertList := []testskeleton.AssertExpression{}

// if DO_APPLY is not empty and equal true, then Terraform apply is used, in other case only Terraform plan
if os.Getenv("DO_APPLY") == "true" {
// deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
} else {
// plan test infrastructure and verify outputs
testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
}
}
Loading