diff --git a/tests/alb/main_test.go b/tests/alb/main_test.go index 54327053..16f66b8f 100644 --- a/tests/alb/main_test.go +++ b/tests/alb/main_test.go @@ -1,12 +1,13 @@ package main import ( + "log" + "testing" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/helpers" "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" - "log" - "testing" ) func TestALBOutputAndConectivitiyWithFullTFVars(t *testing.T) { @@ -51,7 +52,7 @@ func TestALBOutputAndConectivitiyWithFullTFVars(t *testing.T) { // check communication with app { Operation: "CheckFunctionWithValue", - Check: helpers.CheckHttpGetWebUiLoginPage, + Check: helpers.CheckHttpGetWebApp, TestedValue: "http://" + albName + "/", }, } diff --git a/tests/internal/helpers/helpers.go b/tests/internal/helpers/helpers.go index 7a7bf473..41a7af26 100644 --- a/tests/internal/helpers/helpers.go +++ b/tests/internal/helpers/helpers.go @@ -7,20 +7,21 @@ import ( "time" ) -func CheckHttpGetWebUiLoginPage(t *testing.T, outputValue string) bool { +func CheckHttpGetWebApp(t *testing.T, outputValue string) bool { // Do not verify insecure connection http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - // Define how many retries and how often to do in order to check if Panorama web UI is healthy + // Define how many retries and how often to do in order to check if application is healthy sleepBetweenRetry := 15 * time.Second numberOfRetries := 60 urlHealthy := false - // Check in the loop if Panorama web UI is healthy + // Check in the loop if application is healthy for i := 1; i <= numberOfRetries && !urlHealthy; i++ { // HTTP GET time.Sleep(sleepBetweenRetry) - //TODO: Check if client can replace :15 + + //TODO: Check if client can replace :22 //client := http.Client{ // Timeout: 15 * time.Second, //} @@ -28,9 +29,9 @@ func CheckHttpGetWebUiLoginPage(t *testing.T, outputValue string) bool { // Display errors, if there were any, or HTTPS status code, if no errors if err != nil { - t.Logf("Waiting for App (%d/%d)... error HTTP GET: %v\n", i, numberOfRetries, err) + t.Logf("Waiting for application (%d/%d)... error HTTP GET: %v\n", i, numberOfRetries, err) } else { - t.Logf("APP HTTP GET status code: %v", resp.StatusCode) + t.Logf("Application HTTP GET status code: %v", resp.StatusCode) urlHealthy = resp.StatusCode == 200 } } diff --git a/tests/internal/testskeleton/testskeleton.go b/tests/internal/testskeleton/testskeleton.go index dbb8ef2f..67254e30 100644 --- a/tests/internal/testskeleton/testskeleton.go +++ b/tests/internal/testskeleton/testskeleton.go @@ -82,6 +82,11 @@ func AssertOutputs(t *testing.T, terraformOptions *terraform.Options, assertList case "Equal": outputValue := terraform.Output(t, terraformOptions, assertExpression.OutputName) assert.Equal(t, assertExpression.ExpectedValue, outputValue, assertExpression.Message) + case "NotFound": + _, err := terraform.OutputE(t, terraformOptions, assertExpression.OutputName) + assert.ErrorContains(t, err, + fmt.Sprintf("Output \"%v\" not found", assertExpression.OutputName), + assertExpression.Message) case "ListLengthEqual": outputValue := terraform.OutputList(t, terraformOptions, assertExpression.OutputName) assert.Equal(t, assertExpression.ExpectedValue, len(outputValue), assertExpression.Message) @@ -127,30 +132,15 @@ func PlanInfraCheckErrors(t *testing.T, terraformOptions *terraform.Options, assert.Error(t, err) AssertErrors(t, err, assertList) } else { - // Fail test, because error was expected - t.Error(noErrorsMessage) + // Fail test, if errors were expected + if len(assertList) > 0 { + t.Error(noErrorsMessage) + } } return terraformOptions } -func InitAndApplyOnlyWithoutDelete(t *testing.T, terraformOptions *terraform.Options) *terraform.Options { - // If no Terraform options were provided, use default one - if terraformOptions == nil { - terraformOptions = terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - TerraformDir: ".", - Logger: logger.Default, - Lock: true, - Upgrade: true, - }) - } - - // Terraform initalization and apply with auto-approve - terraform.InitAndApply(t, terraformOptions) - - return terraformOptions -} - // Function is comparing every provided error in expressions lists // and checks value using expression defined in the list func AssertErrors(t *testing.T, err error, assertList []AssertExpression) { @@ -169,3 +159,20 @@ func AssertErrors(t *testing.T, err error, assertList []AssertExpression) { } } } + +func InitAndApplyOnlyWithoutDelete(t *testing.T, terraformOptions *terraform.Options) *terraform.Options { + // If no Terraform options were provided, use default one + if terraformOptions == nil { + terraformOptions = terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: ".", + Logger: logger.Default, + Lock: true, + Upgrade: true, + }) + } + + // Terraform initalization and apply with auto-approve + terraform.InitAndApply(t, terraformOptions) + + return terraformOptions +} diff --git a/tests/panorama/main_test.go b/tests/panorama/main_test.go index affdd146..c602cc6f 100644 --- a/tests/panorama/main_test.go +++ b/tests/panorama/main_test.go @@ -1,11 +1,12 @@ -package bootstrap +package panorama import ( + "testing" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/helpers" "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" - "testing" ) func TestOutputForModulePanoramaWithFullVariables(t *testing.T) { @@ -32,7 +33,7 @@ func TestOutputForModulePanoramaWithFullVariables(t *testing.T) { // check access to login page in web UI for Panorama { Operation: "CheckFunctionWithOutput", - Check: helpers.CheckHttpGetWebUiLoginPage, + Check: helpers.CheckHttpGetWebApp, OutputName: "panorama_url", Message: "After bootstrapping, which takes few minutes, web UI for Panorama should be accessible", }, diff --git a/tests/transit_gateway/main_test.go b/tests/transit_gateway/main_test.go index 9090a8e3..411775bc 100644 --- a/tests/transit_gateway/main_test.go +++ b/tests/transit_gateway/main_test.go @@ -1,4 +1,4 @@ -package bootstrap +package transit_gateway import ( "testing" diff --git a/tests/transit_gateway_attachment/main.tf b/tests/transit_gateway_attachment/main.tf new file mode 100644 index 00000000..8c0a9fbb --- /dev/null +++ b/tests/transit_gateway_attachment/main.tf @@ -0,0 +1,77 @@ +locals { + # List of VPC routes + security_vpc_routes = concat( + [for cidr in var.security_vpc_app_routes : + { + subnet_key = "mgmt" + next_hop_set = module.security_transit_gateway_attachment[0].next_hop_set + to_cidr = cidr + } if length(var.transit_gateway_route_tables) > 0 + ], + ) +} + +# Random ID used in names of the resoruces created for tests +resource "random_string" "random_sufix" { + length = 16 + special = false +} + +# Test security VPC +module "security_vpc" { + source = "../../modules/vpc" + + name = "${var.name_prefix}${random_string.random_sufix.id}" + cidr_block = var.security_vpc_cidr + security_groups = var.security_vpc_security_groups + create_internet_gateway = true + enable_dns_hostnames = true + enable_dns_support = true + instance_tenancy = "default" +} + +# Subnets configured in test security VPC +module "security_subnet_sets" { + for_each = toset(distinct([for _, v in var.security_vpc_subnets : v.set])) + source = "../../modules/subnet_set" + + name = "${var.name_prefix}${random_string.random_sufix.id}_${each.key}" + vpc_id = module.security_vpc.id + has_secondary_cidrs = module.security_vpc.has_secondary_cidrs + cidrs = { for k, v in var.security_vpc_subnets : k => v if v.set == each.key } +} + +# Routes configured in test security VPC +module "security_vpc_routes" { + for_each = { for route in local.security_vpc_routes : "${route.subnet_key}_${route.to_cidr}" => route } + source = "../../modules/vpc_route" + + route_table_ids = module.security_subnet_sets[each.value.subnet_key].unique_route_table_ids + to_cidr = try(each.value.to_cidr, null) + destination_type = try(each.value.destination_type, "ipv4") + managed_prefix_list_id = try(each.value.managed_prefix_list_id, null) + next_hop_set = each.value.next_hop_set +} + +# Transit gateway (without attachments) +module "transit_gateway" { + source = "../../modules/transit_gateway" + + name = var.transit_gateway_name != null ? "${var.name_prefix}${random_string.random_sufix.id}_${var.transit_gateway_name}" : null + asn = var.transit_gateway_asn + route_tables = var.transit_gateway_route_tables +} + +# Transit gateway attachment for security VPC +module "security_transit_gateway_attachment" { + source = "../../modules/transit_gateway_attachment" + count = length(var.transit_gateway_route_tables) > 0 ? 1 : 0 + + name = "${var.name_prefix}${random_string.random_sufix.id}_${var.security_vpc_tgw_attachment_name}" + vpc_id = module.security_subnet_sets["tgw_attach"].vpc_id + subnets = module.security_subnet_sets["tgw_attach"].subnets + transit_gateway_route_table = module.transit_gateway.route_tables["from_security_vpc"] + propagate_routes_to = { + to1 = module.transit_gateway.route_tables["from_spoke_vpc"].id + } +} diff --git a/tests/transit_gateway_attachment/main_test.go b/tests/transit_gateway_attachment/main_test.go new file mode 100644 index 00000000..fae6d5e5 --- /dev/null +++ b/tests/transit_gateway_attachment/main_test.go @@ -0,0 +1,76 @@ +package transit_gateway_attachment + +import ( + "testing" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestOutputForModuleTransitGatewayAttachmentFullVariables(t *testing.T) { + // define options for Terraform + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: ".", + VarFiles: []string{"terraform_full.tfvars"}, + Vars: map[string]interface{}{ + "name_prefix": "terratest_module_tgw_attach_", + }, + Logger: logger.Default, + Lock: true, + Upgrade: true, + SetVarsAfterVarFiles: true, + }) + + // prepare list of items to check + assertList := []testskeleton.AssertExpression{ + {OutputName: "tgw_id", Operation: "NotEmpty"}, + {OutputName: "tgw_id", Operation: "StartsWith", ExpectedValue: "tgw-", Message: "TGW ID should starts from tgw-"}, + + {OutputName: "tgw_arn", Operation: "NotEmpty"}, + {OutputName: "tgw_arn", Operation: "StartsWith", ExpectedValue: "arn:aws:ec2:us-east-1", Message: "TGW ARN should starts from arn:aws:ec2:us-east-1"}, + + {OutputName: "tgw_route_tables", Operation: "NotEmpty"}, + {OutputName: "tgw_route_tables", Operation: "ListLengthEqual", ExpectedValue: 2}, + + {OutputName: "tgw_attachment_next_hop_set", Operation: "NotEmpty", ExpectedValue: nil}, + {OutputName: "tgw_attachment_next_hop_set_tgw_id", Operation: "NotEmpty", ExpectedValue: nil}, + {OutputName: "tgw_attachment_next_hop_set_tgw_id", Operation: "NotEmpty", ExpectedValue: "tgw-", Message: "TGW ID should starts from tgw-"}, + } + + // deploy test infrastructure and verify outputs + testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList) +} + +func TestOutputForModuleTransitGatewayAttachmentMinimumVariables(t *testing.T) { + // define options for Terraform + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: ".", + VarFiles: []string{"terraform_minimum.tfvars"}, + Vars: map[string]interface{}{ + "name_prefix": "terratest_module_tgw_attach_", + }, + Logger: logger.Default, + Lock: true, + Upgrade: true, + SetVarsAfterVarFiles: true, + }) + + // prepare list of items to check + assertList := []testskeleton.AssertExpression{ + {OutputName: "tgw_id", Operation: "NotEmpty"}, + {OutputName: "tgw_id", Operation: "StartsWith", ExpectedValue: "tgw-", Message: "TGW ARN should starts from tgw-"}, + + {OutputName: "tgw_arn", Operation: "NotEmpty"}, + {OutputName: "tgw_arn", Operation: "StartsWith", ExpectedValue: "arn:aws:ec2:us-east-1", Message: "TGW ID should starts from arn:aws:ec2:us-east-1"}, + + {OutputName: "tgw_route_tables", Operation: "NotEmpty"}, + {OutputName: "tgw_route_tables", Operation: "ListLengthEqual", ExpectedValue: 0}, + + {OutputName: "tgw_attachment_next_hop_set", Operation: "NotFound"}, + {OutputName: "tgw_attachment_next_hop_set_tgw_id", Operation: "NotFound"}, + } + + // deploy test infrastructure and verify outputs + testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList) +} diff --git a/tests/transit_gateway_attachment/outputs.tf b/tests/transit_gateway_attachment/outputs.tf new file mode 100644 index 00000000..86091e69 --- /dev/null +++ b/tests/transit_gateway_attachment/outputs.tf @@ -0,0 +1,19 @@ +output "tgw_id" { + value = module.transit_gateway.transit_gateway.id +} + +output "tgw_arn" { + value = module.transit_gateway.transit_gateway.arn +} + +output "tgw_route_tables" { + value = [for k, v in module.transit_gateway.route_tables : v.tags["Name"]] +} + +output "tgw_attachment_next_hop_set" { + value = length(var.transit_gateway_route_tables) > 0 ? module.security_transit_gateway_attachment[0].next_hop_set : null +} + +output "tgw_attachment_next_hop_set_tgw_id" { + value = length(var.transit_gateway_route_tables) > 0 ? module.security_transit_gateway_attachment[0].next_hop_set.id : null +} \ No newline at end of file diff --git a/tests/transit_gateway_attachment/terraform_full.tfvars b/tests/transit_gateway_attachment/terraform_full.tfvars new file mode 100644 index 00000000..a471b49a --- /dev/null +++ b/tests/transit_gateway_attachment/terraform_full.tfvars @@ -0,0 +1,44 @@ +region = "us-east-1" +name_prefix = "test_tgw_" + +security_vpc_cidr = "10.100.0.0/16" +security_vpc_subnets = { + "10.100.0.0/24" = { az = "us-east-1a", set = "mgmt" } + "10.100.1.0/24" = { az = "us-east-1a", set = "tgw_attach" } + "10.100.2.0/24" = { az = "us-east-1a", set = "natgw" } + "10.100.3.0/24" = { az = "us-east-1a", set = "gwlb" } + "10.100.4.0/24" = { az = "us-east-1a", set = "gwlbe_inbound" } +} +security_vpc_security_groups = { + vmseries_mgmt = { + name = "vmseries_mgmt" + rules = { + all_outbound = { + description = "Permit ALL outbound" + type = "egress", from_port = "0", to_port = "0", protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + ssh = { + description = "Permit SSH inbound" + type = "ingress", from_port = "22", to_port = "22", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + } + } +} +security_vpc_app_routes = ["10.231.0.0/16", "10.232.0.0/16"] + +security_vpc_tgw_attachment_name = "tgw" + +transit_gateway_name = "tgw" +transit_gateway_asn = "65200" +transit_gateway_route_tables = { + "from_security_vpc" = { + create = true + name = "from_security" + } + "from_spoke_vpc" = { + create = true + name = "from_spokes" + } +} diff --git a/tests/transit_gateway_attachment/terraform_minimum.tfvars b/tests/transit_gateway_attachment/terraform_minimum.tfvars new file mode 100644 index 00000000..0633cdb6 --- /dev/null +++ b/tests/transit_gateway_attachment/terraform_minimum.tfvars @@ -0,0 +1,29 @@ +region = "us-east-1" +name_prefix = "test_tgw_" + +security_vpc_cidr = "10.100.0.0/16" +security_vpc_subnets = { + "10.100.0.0/24" = { az = "us-east-1a", set = "mgmt" } + "10.100.1.0/24" = { az = "us-east-1a", set = "tgw_attach" } + "10.100.2.0/24" = { az = "us-east-1a", set = "natgw" } + "10.100.3.0/24" = { az = "us-east-1a", set = "gwlb" } + "10.100.4.0/24" = { az = "us-east-1a", set = "gwlbe_inbound" } +} +security_vpc_security_groups = { + vmseries_mgmt = { + name = "vmseries_mgmt" + rules = { + all_outbound = { + description = "Permit ALL outbound" + type = "egress", from_port = "0", to_port = "0", protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + ssh = { + description = "Permit SSH inbound" + type = "ingress", from_port = "22", to_port = "22", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + } + } + } +} +security_vpc_app_routes = ["10.231.0.0/16", "10.232.0.0/16"] diff --git a/tests/transit_gateway_attachment/variables.tf b/tests/transit_gateway_attachment/variables.tf new file mode 100644 index 00000000..5905d108 --- /dev/null +++ b/tests/transit_gateway_attachment/variables.tf @@ -0,0 +1,50 @@ +variable "region" { + description = "AWS region to use for the created resources." + type = string +} + +variable "name_prefix" { + description = "Prefix used in resources created for tests" + type = string +} + +variable "security_vpc_cidr" { + description = "CIDR for VPC" + type = string +} + +variable "security_vpc_subnets" { + description = "Map of subnets in VPC" +} + +variable "security_vpc_security_groups" { + description = "Map of security groups" +} + +variable "security_vpc_app_routes" { + description = "Simple list of CIDR for routes used in security VPC" + default = [] +} + +variable "security_vpc_tgw_attachment_name" { + description = "Transit gateway VPC attachment name" + default = null + type = string +} + +variable "transit_gateway_name" { + description = "Transit gateway name" + default = null + type = string +} + +variable "transit_gateway_asn" { + description = "Transit gateway ASN" + default = null + type = string +} + +variable "transit_gateway_route_tables" { + description = "Transit gateway route tables" + default = {} +} diff --git a/tests/vpc_plan/versions.tf b/tests/transit_gateway_attachment/versions.tf similarity index 69% rename from tests/vpc_plan/versions.tf rename to tests/transit_gateway_attachment/versions.tf index a9554d9e..09d4bdb5 100644 --- a/tests/vpc_plan/versions.tf +++ b/tests/transit_gateway_attachment/versions.tf @@ -5,10 +5,6 @@ terraform { source = "hashicorp/aws" version = "~> 4.25" } - tls = { - source = "hashicorp/tls" - version = "~> 3.3.0" - } random = { source = "hashicorp/random" version = "~> 3.4.3" @@ -19,7 +15,3 @@ terraform { provider "aws" { region = var.region } - -variable "region" { - default = "us-east-1" -} diff --git a/tests/transit_gateway_peering/main.tf b/tests/transit_gateway_peering/main.tf new file mode 100644 index 00000000..12305a3e --- /dev/null +++ b/tests/transit_gateway_peering/main.tf @@ -0,0 +1,87 @@ +### Local Region ### + +module "transit_gateway_local" { + source = "../../modules/transit_gateway" + + name = var.local_transit_gateway_name != null ? "${var.name_prefix}${var.region}-${var.local_transit_gateway_name}" : null + asn = var.local_transit_gateway_asn + route_tables = { + "from_security_vpc" = { + create = true + name = "${var.name_prefix}from-security" + } + "from_spoke_vpc" = { + create = true + name = "${var.name_prefix}from-spokes" + } + } +} + +### Remote Region ### + +# To be able to interact at all with a different AWS region, we need to declare +# an alternative provider to what is normally defined in versions.tf. +provider "aws" { + alias = "remote" + region = var.remote_region +} + +module "transit_gateway_remote" { + source = "../../modules/transit_gateway" + providers = { + aws = aws.remote + } + + name = var.remote_transit_gateway_name != null ? "${var.name_prefix}${var.remote_region}-${var.remote_transit_gateway_name}" : null + asn = var.remote_transit_gateway_asn + route_tables = { + "from_spoke_vpc" = { + create = true + name = "${var.name_prefix}from-spokes" + } + } +} + +### Cross-Region ### + +module "transit_gateway_peering" { + source = "../../modules/transit_gateway_peering" + providers = { + aws = aws + aws.remote = aws.remote + } + + local_tgw_route_table = module.transit_gateway_local.route_tables["from_spoke_vpc"] + remote_tgw_route_table = module.transit_gateway_remote.route_tables["from_spoke_vpc"] + + local_attachment_tags = { Name = "${var.name_prefix}peering-attach" } +} + +# Optional routes across the peering. +# Currently AWS only supports static routes, and not propagations, across a peering. +# +# As an example, assume: +# - the local region has a VPC named "security" +# - the remote region has a VPC named "spoke" (in a typical use case it can host a Panorama management appliance) + +resource "aws_ec2_transit_gateway_route" "from_remote_spoke_to_local_region" { + provider = aws.remote + + destination_cidr_block = "10.0.0.0/8" + transit_gateway_route_table_id = module.transit_gateway_remote.route_tables["from_spoke_vpc"].id + transit_gateway_attachment_id = module.transit_gateway_peering.peering_attachment.id + blackhole = false + + # Workaround: Explicit depends_on ensures that peering_attachment is already accepted when creating this route. + depends_on = [module.transit_gateway_peering] +} + +resource "aws_ec2_transit_gateway_route" "from_local_security_to_remote_region" { + destination_cidr_block = "10.244.0.0/16" + transit_gateway_route_table_id = module.transit_gateway_local.route_tables["from_security_vpc"].id + transit_gateway_attachment_id = module.transit_gateway_peering.peering_attachment.id + blackhole = false + + # Workaround: Explicit depends_on ensures that peering_attachment is already accepted when creating this route. + depends_on = [module.transit_gateway_peering] +} diff --git a/tests/transit_gateway_peering/main_test.go b/tests/transit_gateway_peering/main_test.go new file mode 100644 index 00000000..fa2a3138 --- /dev/null +++ b/tests/transit_gateway_peering/main_test.go @@ -0,0 +1,87 @@ +package transit_gateway_peering + +import ( + "testing" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestOutputForModuleTransitGatewayAttachmentFullVariables(t *testing.T) { + // define options for Terraform + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: ".", + VarFiles: []string{"terraform_full.tfvars"}, + Vars: map[string]interface{}{ + "name_prefix": "terratest_module_tgw_attach_", + }, + Logger: logger.Default, + Lock: true, + Upgrade: true, + SetVarsAfterVarFiles: true, + }) + + // prepare list of items to check + assertList := []testskeleton.AssertExpression{ + {OutputName: "tgw_local_id", Operation: "NotEmpty"}, + {OutputName: "tgw_local_id", Operation: "StartsWith", ExpectedValue: "tgw-", Message: "TGW ID should starts from tgw-"}, + + {OutputName: "tgw_local_arn", Operation: "NotEmpty"}, + {OutputName: "tgw_local_arn", Operation: "StartsWith", ExpectedValue: "arn:aws:ec2:", Message: "TGW ARN should starts from arn:aws:ec2:"}, + + {OutputName: "tgw_remote_id", Operation: "NotEmpty"}, + {OutputName: "tgw_remote_id", Operation: "StartsWith", ExpectedValue: "tgw-", Message: "TGW ID should starts from tgw-"}, + + {OutputName: "tgw_remote_arn", Operation: "NotEmpty"}, + {OutputName: "tgw_remote_arn", Operation: "StartsWith", ExpectedValue: "arn:aws:ec2:", Message: "TGW ARN should starts from arn:aws:ec2:"}, + + {OutputName: "route_destination_from_remote_spoke_to_local_region", Operation: "NotEmpty"}, + {OutputName: "route_destination_from_remote_spoke_to_local_region", Operation: "Equal", ExpectedValue: "10.0.0.0/8"}, + + {OutputName: "route_destination_from_local_security_to_remote_region", Operation: "NotEmpty"}, + {OutputName: "route_destination_from_local_security_to_remote_region", Operation: "Equal", ExpectedValue: "10.244.0.0/16"}, + } + + // deploy test infrastructure and verify outputs + testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList) +} + +func TestOutputForModuleTransitGatewayAttachmentMinimumVariables(t *testing.T) { + // define options for Terraform + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: ".", + VarFiles: []string{"terraform_minimum.tfvars"}, + Vars: map[string]interface{}{ + "name_prefix": "terratest_module_tgw_attach_", + }, + Logger: logger.Default, + Lock: true, + Upgrade: true, + SetVarsAfterVarFiles: true, + }) + + // prepare list of items to check + assertList := []testskeleton.AssertExpression{ + {OutputName: "tgw_local_id", Operation: "NotEmpty"}, + {OutputName: "tgw_local_id", Operation: "StartsWith", ExpectedValue: "tgw-", Message: "TGW ID should starts from tgw-"}, + + {OutputName: "tgw_local_arn", Operation: "NotEmpty"}, + {OutputName: "tgw_local_arn", Operation: "StartsWith", ExpectedValue: "arn:aws:ec2:", Message: "TGW ARN should starts from arn:aws:ec2:"}, + + {OutputName: "tgw_remote_id", Operation: "NotEmpty"}, + {OutputName: "tgw_remote_id", Operation: "StartsWith", ExpectedValue: "tgw-", Message: "TGW ID should starts from tgw-"}, + + {OutputName: "tgw_remote_arn", Operation: "NotEmpty"}, + {OutputName: "tgw_remote_arn", Operation: "StartsWith", ExpectedValue: "arn:aws:ec2:", Message: "TGW ARN should starts from arn:aws:ec2:"}, + + {OutputName: "route_destination_from_remote_spoke_to_local_region", Operation: "NotEmpty"}, + {OutputName: "route_destination_from_remote_spoke_to_local_region", Operation: "Equal", ExpectedValue: "10.0.0.0/8"}, + + {OutputName: "route_destination_from_local_security_to_remote_region", Operation: "NotEmpty"}, + {OutputName: "route_destination_from_local_security_to_remote_region", Operation: "Equal", ExpectedValue: "10.244.0.0/16"}, + } + + // deploy test infrastructure and verify outputs + testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList) +} diff --git a/tests/transit_gateway_peering/outputs.tf b/tests/transit_gateway_peering/outputs.tf new file mode 100644 index 00000000..f4ae9915 --- /dev/null +++ b/tests/transit_gateway_peering/outputs.tf @@ -0,0 +1,23 @@ +output "tgw_local_id" { + value = module.transit_gateway_local.transit_gateway.id +} + +output "tgw_local_arn" { + value = module.transit_gateway_local.transit_gateway.arn +} + +output "tgw_remote_id" { + value = module.transit_gateway_remote.transit_gateway.id +} + +output "tgw_remote_arn" { + value = module.transit_gateway_remote.transit_gateway.arn +} + +output "route_destination_from_remote_spoke_to_local_region" { + value = aws_ec2_transit_gateway_route.from_remote_spoke_to_local_region.destination_cidr_block +} + +output "route_destination_from_local_security_to_remote_region" { + value = aws_ec2_transit_gateway_route.from_local_security_to_remote_region.destination_cidr_block +} diff --git a/tests/transit_gateway_peering/terraform_full.tfvars b/tests/transit_gateway_peering/terraform_full.tfvars new file mode 100644 index 00000000..e199d658 --- /dev/null +++ b/tests/transit_gateway_peering/terraform_full.tfvars @@ -0,0 +1,10 @@ +region = "us-east-1" +remote_region = "us-west-1" + +name_prefix = "test_tgw_" + +local_transit_gateway_name = "tgw-local" +local_transit_gateway_asn = "65201" + +remote_transit_gateway_name = "tgw-remote" +remote_transit_gateway_asn = "65202" diff --git a/tests/transit_gateway_peering/terraform_minimum.tfvars b/tests/transit_gateway_peering/terraform_minimum.tfvars new file mode 100644 index 00000000..9361d6e0 --- /dev/null +++ b/tests/transit_gateway_peering/terraform_minimum.tfvars @@ -0,0 +1,4 @@ +region = "us-east-1" +remote_region = "us-west-1" + +name_prefix = "test_tgw_" diff --git a/tests/transit_gateway_peering/variables.tf b/tests/transit_gateway_peering/variables.tf new file mode 100644 index 00000000..9b4b5073 --- /dev/null +++ b/tests/transit_gateway_peering/variables.tf @@ -0,0 +1,38 @@ +variable "region" { + description = "The AWS region where to create local resources." + type = string +} + +variable "remote_region" { + description = "The AWS region where to create remote resources." + type = string +} + +variable "name_prefix" { + description = "Prefix used in resources created for tests" + type = string +} + +variable "local_transit_gateway_name" { + description = "Transit gateway name" + default = null + type = string +} + +variable "local_transit_gateway_asn" { + description = "Transit gateway ASN" + default = null + type = string +} + +variable "remote_transit_gateway_name" { + description = "Transit gateway name" + default = null + type = string +} + +variable "remote_transit_gateway_asn" { + description = "Transit gateway ASN" + default = null + type = string +} diff --git a/tests/transit_gateway_peering/versions.tf b/tests/transit_gateway_peering/versions.tf new file mode 100644 index 00000000..09d4bdb5 --- /dev/null +++ b/tests/transit_gateway_peering/versions.tf @@ -0,0 +1,17 @@ +terraform { + required_version = ">= 0.15.0, < 2.0.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.25" + } + random = { + source = "hashicorp/random" + version = "~> 3.4.3" + } + } +} + +provider "aws" { + region = var.region +} diff --git a/tests/vpc/deployment/main.tf b/tests/vpc/deployment/main.tf new file mode 100644 index 00000000..bf80d5eb --- /dev/null +++ b/tests/vpc/deployment/main.tf @@ -0,0 +1,61 @@ +# This Terraform code does not deploy a real-world cloud environment. +# It is a temporary deployment intended solely to perform tests. +# For a quick start see the file main_test.go, which executes the terratest library. +# +# Change this code in the same pull request that changes the code in `modules` directory. +# +# Core tests: +# - Do various combinations of known inputs produce expected outputs? +# - Can we discover a pre-existing vpc? +# +# Boilerplate tests: +# - Can we call the module twice? + +# Random name allows parallel runs on the same cloud account. +resource "random_pet" "this" { + prefix = "test-vpc-read" +} + +locals { + vpc_name = random_pet.this.id +} + +module "vpc" { + source = "../../../modules/vpc" + + name = local.vpc_name + create_vpc = true + create_internet_gateway = false + create_vpn_gateway = true + cidr_block = "10.0.0.0/16" + secondary_cidr_blocks = ["10.4.0.0/16", "10.5.0.0/16", "10.6.0.0/16"] +} + +### Reuse Existing Resources ### + +module "vpc_read" { + source = "../../../modules/vpc" + + create_vpc = false + name = module.vpc.name + create_internet_gateway = false + use_internet_gateway = false +} + +module "vpc_read_igw_create" { + source = "../../../modules/vpc" + + create_vpc = false + name = module.vpc.name + create_internet_gateway = true + use_internet_gateway = false +} + +module "vpc_read_igw_read" { + source = "../../../modules/vpc" + + create_vpc = false + name = module.vpc_read_igw_create.name + create_internet_gateway = false + use_internet_gateway = true +} diff --git a/tests/vpc/deployment/main_test.go b/tests/vpc/deployment/main_test.go new file mode 100644 index 00000000..c5d601bf --- /dev/null +++ b/tests/vpc/deployment/main_test.go @@ -0,0 +1,43 @@ +package vpc_deployment + +import ( + "testing" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestOutputForModuleVpc(t *testing.T) { + // define options for Terraform + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: ".", + Logger: logger.Default, + Lock: true, + Upgrade: true, + SetVarsAfterVarFiles: true, + }) + + // prepare list of items to check + assertList := []testskeleton.AssertExpression{ + {OutputName: "vpc_cidr_block_correct", Operation: "NotEmpty"}, + {OutputName: "vpc_cidr_block_correct", Operation: "Equal", ExpectedValue: "10.0.0.0/16"}, + + {OutputName: "vpc_read_cidr_block_correct", Operation: "NotEmpty"}, + {OutputName: "vpc_read_cidr_block_correct", Operation: "Equal", ExpectedValue: "10.0.0.0/16"}, + + {OutputName: "vpc_read_igw_create_cidr_block_correct", Operation: "NotEmpty"}, + {OutputName: "vpc_read_igw_create_cidr_block_correct", Operation: "Equal", ExpectedValue: "10.0.0.0/16"}, + + {OutputName: "vpc_read_igw_read_cidr_block_correct", Operation: "NotEmpty"}, + {OutputName: "vpc_read_igw_read_cidr_block_correct", Operation: "Equal", ExpectedValue: "10.0.0.0/16"}, + + {OutputName: "is_vpc_name_correct", Operation: "Equal", ExpectedValue: "true"}, + {OutputName: "is_vpc_read_igw_create_name_correct", Operation: "Equal", ExpectedValue: "true"}, + {OutputName: "is_vpc_read_igw_read_name_correct", Operation: "Equal", ExpectedValue: "true"}, + {OutputName: "is_vpc_read_name_correct", Operation: "Equal", ExpectedValue: "true"}, + } + + // deploy test infrastructure and verify outputs + testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList) +} diff --git a/tests/vpc/deployment/outputs.tf b/tests/vpc/deployment/outputs.tf new file mode 100644 index 00000000..79a5e812 --- /dev/null +++ b/tests/vpc/deployment/outputs.tf @@ -0,0 +1,35 @@ +output "generated_vpc_name" { + value = local.vpc_name +} + +output "vpc_cidr_block_correct" { + value = module.vpc.vpc.cidr_block +} + +output "is_vpc_name_correct" { + value = (module.vpc.name == local.vpc_name) +} + +output "vpc_read_cidr_block_correct" { + value = module.vpc_read.vpc.cidr_block +} + +output "is_vpc_read_name_correct" { + value = (module.vpc_read.name == local.vpc_name) +} + +output "vpc_read_igw_create_cidr_block_correct" { + value = module.vpc_read_igw_create.vpc.cidr_block +} + +output "is_vpc_read_igw_create_name_correct" { + value = (module.vpc_read_igw_create.name == local.vpc_name) +} + +output "vpc_read_igw_read_cidr_block_correct" { + value = module.vpc_read_igw_read.vpc.cidr_block +} + +output "is_vpc_read_igw_read_name_correct" { + value = (module.vpc_read_igw_read.name == local.vpc_name) +} diff --git a/tests/vpc/deployment/variables.tf b/tests/vpc/deployment/variables.tf new file mode 100644 index 00000000..905a247d --- /dev/null +++ b/tests/vpc/deployment/variables.tf @@ -0,0 +1,3 @@ +variable "region" { + default = "us-east-1" +} diff --git a/tests/vpc/deployment/versions.tf b/tests/vpc/deployment/versions.tf new file mode 100644 index 00000000..09d4bdb5 --- /dev/null +++ b/tests/vpc/deployment/versions.tf @@ -0,0 +1,17 @@ +terraform { + required_version = ">= 0.15.0, < 2.0.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.25" + } + random = { + source = "hashicorp/random" + version = "~> 3.4.3" + } + } +} + +provider "aws" { + region = var.region +} diff --git a/tests/vpc_plan/main.tf b/tests/vpc/plan/main.tf similarity index 97% rename from tests/vpc_plan/main.tf rename to tests/vpc/plan/main.tf index 58edc5c7..6209311b 100644 --- a/tests/vpc_plan/main.tf +++ b/tests/vpc/plan/main.tf @@ -48,7 +48,7 @@ locals { ### The code under test. ### module "vpc" { - source = "../../modules/vpc" + source = "../../../modules/vpc" # Inputs that cannot handle unknown values. create_vpc = true @@ -74,7 +74,7 @@ module "vpc" { # Below replace each "true" with "false", one by one. It should detect unwanted dependencies. module "vpc_novgw" { - source = "../../modules/vpc" + source = "../../../modules/vpc" # Inputs that cannot handle unknown values. create_vpc = true @@ -96,7 +96,7 @@ module "vpc_novgw" { } module "vpc_noigw" { - source = "../../modules/vpc" + source = "../../../modules/vpc" # Inputs that cannot handle unknown values. create_vpc = true @@ -119,7 +119,7 @@ module "vpc_noigw" { } module "vpc_useigw" { - source = "../../modules/vpc" + source = "../../../modules/vpc" # Inputs that cannot handle unknown values. create_vpc = true @@ -142,7 +142,7 @@ module "vpc_useigw" { } module "novpc" { - source = "../../modules/vpc" + source = "../../../modules/vpc" # Inputs that cannot handle unknown values. create_vpc = false # the change @@ -164,7 +164,7 @@ module "novpc" { # For the known values that are lists or maps, check if they can be empty, one by one. module "vpc_nosg" { - source = "../../modules/vpc" + source = "../../../modules/vpc" # Inputs that cannot handle unknown values. create_vpc = true @@ -186,7 +186,7 @@ module "vpc_nosg" { } module "vpc_noseccidr" { - source = "../../modules/vpc" + source = "../../../modules/vpc" # Inputs that cannot handle unknown values. create_vpc = true diff --git a/tests/vpc/plan/main_test.go b/tests/vpc/plan/main_test.go new file mode 100644 index 00000000..b806c279 --- /dev/null +++ b/tests/vpc/plan/main_test.go @@ -0,0 +1,27 @@ +package vpc_plan + +import ( + "testing" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestNoErrorInPlanForModuleVpc(t *testing.T) { + // define options for Terraform + terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: ".", + Vars: map[string]interface{}{}, + Logger: logger.Default, + Lock: true, + Upgrade: true, + SetVarsAfterVarFiles: true, + }) + + // prepare list of items to check + assertList := []testskeleton.AssertExpression{} + + // deploy test infrastructure and verify outputs + testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected") +} diff --git a/tests/vpc/plan/variables.tf b/tests/vpc/plan/variables.tf new file mode 100644 index 00000000..905a247d --- /dev/null +++ b/tests/vpc/plan/variables.tf @@ -0,0 +1,3 @@ +variable "region" { + default = "us-east-1" +} diff --git a/tests/vpc/plan/versions.tf b/tests/vpc/plan/versions.tf new file mode 100644 index 00000000..09d4bdb5 --- /dev/null +++ b/tests/vpc/plan/versions.tf @@ -0,0 +1,17 @@ +terraform { + required_version = ">= 0.15.0, < 2.0.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.25" + } + random = { + source = "hashicorp/random" + version = "~> 3.4.3" + } + } +} + +provider "aws" { + region = var.region +} diff --git a/tests/vpc_plan/main_test.go b/tests/vpc_plan/main_test.go deleted file mode 100644 index e8d84c5f..00000000 --- a/tests/vpc_plan/main_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package vpc_plan - -import ( - "testing" - - "github.com/gruntwork-io/terratest/modules/terraform" -) - -func TestVPCPlan(t *testing.T) { - // Construct the terraform options with default retryable errors to handle the most common retryable errors in - // terraform testing. - terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ - // The path to where our Terraform code is located - TerraformDir: ".", - }) - - // Schedule `terraform destroy` at the end of the test, to clean up the created resources. - defer terraform.Destroy(t, terraformOptions) - - // This will run `terraform init` and `terraform plan` and fail the test if there are any errors. - // This specific test is not intended to execute `terraform apply` at all. - terraform.InitAndPlan(t, terraformOptions) -} diff --git a/tests/vpc_route/main_test.go b/tests/vpc_route/main_test.go index 9a96cec1..dbac3ccf 100644 --- a/tests/vpc_route/main_test.go +++ b/tests/vpc_route/main_test.go @@ -1,4 +1,4 @@ -package bootstrap +package vpc_route import ( "testing"