diff --git a/.github/actions/plan_apply/action.yml b/.github/actions/plan_apply/action.yml new file mode 100644 index 00000000..0a7d6c13 --- /dev/null +++ b/.github/actions/plan_apply/action.yml @@ -0,0 +1,63 @@ +name: 'TF plan/apply' +description: 'Runs Terraform plan and/or apply for a specified path.' +inputs: + tf_version: + description: 'TF version used.' + required: true + path: + description: 'Path to Terraform module.' + required: true + do_apply: + description: When set to true runs also apply + type: boolean + default: false + idempotence: + description: When set to true runs plan to on already applied configuration + type: boolean + default: true + +runs: + using: "composite" + steps: + + - name: setup Terraform + uses: hashicorp/setup-terraform@v2 + with: + terraform_version: ${{ inputs.tf_version }} + # below settings is required for Terratest (details are in https://github.com/gruntwork-io/terratest/issues/706) + terraform_wrapper: false + + - name: configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ env.ASSUME_ROLE }} + role-session-name: gh-action-role-session + aws-region: ${{ env.AWS_REGION }} + # TODO: it's temporary solution until in repository settings -> secrets and variables -> variables + # there will be defined new repository variable AWS_REGION + env: + AWS_REGION : "us-east-1" + + - name: test infrastructure + id: test + env: + TPATH: ${{ inputs.path }} + DO_APPLY: ${{ inputs.do_apply }} + shell: bash + run: | + echo "::group::TERRATEST" + cd "$GITHUB_WORKSPACE/$TPATH" + DO_APPLY=$DO_APPLY make test + echo "::endgroup::" + + - name: destroy + id: destroy + if: always() && inputs.do_apply == 'true' + env: + TPATH: ${{ inputs.path }} + shell: bash + run: | + cd "$GITHUB_WORKSPACE/$TPATH" + echo "::group::TERRAFORM DESTROY" + make destroy + echo "::endgroup::" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index d1c85d40..00000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,108 +0,0 @@ - ---- -name: CI/CD -on: - push: - branches: - - main - - develop - pull_request: - schedule: - - cron: '0 0 1 * *' - -jobs: - pre-commit: - name: Verify the pre-commit framework was used properly by developers - runs-on: ubuntu-latest - - steps: - - name: Check out source - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - # Semantic version range syntax (like 3.x) or the exact Python version - python-version: '3.11.0' - - - name: Run pre-commit framework as the developer should run it - run: sudo ./scripts/install.sh && sudo ./scripts/run.sh - - - name: The `git diff` showing whether the pre-commit mandated extra changes to the repository files - if: failure() - run: git diff - - validate: - name: Validate - runs-on: ubuntu-latest - - steps: - - name: Check out source - uses: actions/checkout@v2 - - - name: Set up Terraform - uses: hashicorp/setup-terraform@v1 - with: - terraform_version: 1.0.0 - - - name: terraform validate - env: - AWS_DEFAULT_REGION: us-west-2 - run: | - cd "$GITHUB_WORKSPACE" - for dir in $(find modules examples -type d -not \( -name ".?*" \) -maxdepth 1 -mindepth 1); - do - if [[ "$dir" == "modules/transit_gateway_peering" ]]; - then - echo "Skipping directory: $dir" - echo "Terraform does not support validating a module which uses an aliased provider (module-specific; validating an entire configuration works fine)." - continue - fi - - echo "Processing directory: $dir" - cd "$GITHUB_WORKSPACE/$dir" - terraform init -backend=false - terraform validate - done - - checkov: - name: Scan Terraform code with Checkov - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v1 - with: - python-version: 3.8 - - name: Test with Checkov - id: checkov - uses: bridgecrewio/checkov-action@master - with: - output_format: cli - directory: . - framework: terraform - compact: true - soft_fail: true - quiet: true # display only failed checks - - # release: - # name: release - # if: github.event_name == 'push' && github.ref == 'refs/heads/main' - # needs: [validate] - # runs-on: ubuntu-latest - - # steps: - # - name: Checkout - # uses: actions/checkout@v2 - - # - name: Create release and publish - # id: release - # uses: cycjimmy/semantic-release-action@v2 - # with: - # semantic_version: 17.4.2 - # extra_plugins: | - # conventional-changelog-conventionalcommits@^4.5.0 - # @semantic-release/git@^9.0.0 - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/oidc.yml b/.github/workflows/oidc.yml deleted file mode 100644 index 0d410f6a..00000000 --- a/.github/workflows/oidc.yml +++ /dev/null @@ -1,31 +0,0 @@ -# Sample workflow to perform terratest against a test AWS account -name: AWS Terratest workflow -on: workflow_dispatch - -env: - AWS_REGION : "eu-west-2" - -permissions: - id-token: write # This is required for requesting the JWT - contents: read # This is required for actions/checkout -jobs: - TerraTest: - runs-on: ubuntu-latest - steps: - - name: Git clone the repository - uses: actions/checkout@v3 - - - name: configure aws credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - role-to-assume: ${{ secrets.ASSUME_ROLE }} - role-session-name: gh-action-role-session - aws-region: ${{ env.AWS_REGION }} - - - name: Setup Dependencies - working-directory: tests/vpc/deployment - run: go get -v -t -d && go mod tidy - - - name: Test - working-directory: tests/vpc/deployment - run: go test -v \ No newline at end of file diff --git a/.github/workflows/pr_ci.yml b/.github/workflows/pr_ci.yml new file mode 100644 index 00000000..bbe81057 --- /dev/null +++ b/.github/workflows/pr_ci.yml @@ -0,0 +1,29 @@ +name: PR CI +run-name: "CI pipeline for PR - (#${{ github.event.number }}) ${{ github.event.pull_request.title }}" + +permissions: + contents: read + actions: read + id-token: write + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + branches: ['main'] + +jobs: + pr_ci_wrkflw: + name: Run CI + uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/pr_ci.yml@v1.2.0 + if: github.actor != 'dependabot[bot]' + secrets: inherit + with: + cloud: aws + tf_version: 1.2 1.3 1.4 1.5 + do_apply: false + fail_fast: false + apply_timeout: 120 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 627a399a..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Release -on: workflow_dispatch - -jobs: - release: - name: release - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Create release and publish - id: release - uses: cycjimmy/semantic-release-action@v2 - with: - semantic_version: 17.4.2 - extra_plugins: | - conventional-changelog-conventionalcommits@^4.5.0 - @semantic-release/git@^9.0.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release_ci.yml b/.github/workflows/release_ci.yml new file mode 100644 index 00000000..4a1ebae5 --- /dev/null +++ b/.github/workflows/release_ci.yml @@ -0,0 +1,25 @@ +name: Release CI +run-name: "Continous Release" + +permissions: + contents: write + issues: read + id-token: write + +on: + workflow_dispatch: + schedule: + - cron: '0 1 * * 4' # this means every Thursday @1am UTC + +jobs: + release_wrkflw: + name: Do release + uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/release_ci.yml@v1.2.0 + if: github.actor != 'dependabot[bot]' + secrets: inherit + with: + cloud: aws + max_parallel: 10 + do_apply: true + fail_fast: false + apply_timeout: 120 diff --git a/examples/centralized_design/Makefile b/examples/centralized_design/Makefile new file mode 100644 index 00000000..7258f45c --- /dev/null +++ b/examples/centralized_design/Makefile @@ -0,0 +1,11 @@ +init: + @../../makefile.sh init + +validate: + @../../makefile.sh validate + +test: + @../../makefile.sh test + +destroy: + @../../makefile.sh destroy diff --git a/examples/centralized_design/main_test.go b/examples/centralized_design/main_test.go new file mode 100644 index 00000000..6bc58612 --- /dev/null +++ b/examples/centralized_design/main_test.go @@ -0,0 +1,47 @@ +package centralized_design + +import ( + "fmt" + "math/rand" + "os" + "testing" + "time" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestExampleCentralizedDesign(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") + } +} diff --git a/examples/combined_design/Makefile b/examples/combined_design/Makefile new file mode 100644 index 00000000..7258f45c --- /dev/null +++ b/examples/combined_design/Makefile @@ -0,0 +1,11 @@ +init: + @../../makefile.sh init + +validate: + @../../makefile.sh validate + +test: + @../../makefile.sh test + +destroy: + @../../makefile.sh destroy diff --git a/examples/combined_design/main_test.go b/examples/combined_design/main_test.go new file mode 100644 index 00000000..e0c7a3e6 --- /dev/null +++ b/examples/combined_design/main_test.go @@ -0,0 +1,47 @@ +package centralized_design + +import ( + "fmt" + "math/rand" + "os" + "testing" + "time" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestExampleCombinedDesign(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") + } +} diff --git a/examples/combined_design_autoscale/Makefile b/examples/combined_design_autoscale/Makefile new file mode 100644 index 00000000..7258f45c --- /dev/null +++ b/examples/combined_design_autoscale/Makefile @@ -0,0 +1,11 @@ +init: + @../../makefile.sh init + +validate: + @../../makefile.sh validate + +test: + @../../makefile.sh test + +destroy: + @../../makefile.sh destroy diff --git a/examples/combined_design_autoscale/example.tfvars b/examples/combined_design_autoscale/example.tfvars new file mode 100644 index 00000000..62bfe4d5 --- /dev/null +++ b/examples/combined_design_autoscale/example.tfvars @@ -0,0 +1,592 @@ +### 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-frankfurt" # 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 = { + trusted_path_monitoring = { + name = "trusted-path-monitoring" + rules = { + block_outbound_icmp_1 = { + rule_number = 110 + egress = true + protocol = "icmp" + rule_action = "deny" + cidr_block = "10.100.1.0/24" + from_port = null + to_port = null + } + block_outbound_icmp_2 = { + rule_number = 120 + egress = true + protocol = "icmp" + rule_action = "deny" + cidr_block = "10.100.65.0/24" + from_port = null + to_port = null + } + allow_other_outbound = { + rule_number = 200 + egress = true + protocol = "-1" + rule_action = "allow" + cidr_block = "0.0.0.0/0" + from_port = null + to_port = null + } + allow_inbound = { + rule_number = 300 + egress = false + protocol = "-1" + rule_action = "allow" + cidr_block = "0.0.0.0/0" + from_port = null + to_port = null + } + } + } + } + security_groups = { + vmseries_private = { + name = "vmseries_private" + 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"] + } + geneve = { + description = "Permit GENEVE to GWLB subnets" + type = "ingress", from_port = "6081", to_port = "6081", protocol = "udp" + cidr_blocks = [ + "10.100.5.0/24", "10.100.69.0/24" + ] + } + health_probe = { + description = "Permit Port 80 Health Probe to GWLB subnets" + type = "ingress", from_port = "80", to_port = "80", protocol = "tcp" + cidr_blocks = [ + "10.100.5.0/24", "10.100.69.0/24" + ] + } + } + } + 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) + } + panorama_ssh = { + description = "Permit Panorama SSH (Optional)" + type = "ingress", from_port = "22", to_port = "22", protocol = "tcp" + cidr_blocks = ["10.0.0.0/8"] + } + panorama_mgmt = { + description = "Permit Panorama Management" + type = "ingress", from_port = "3978", to_port = "3978", protocol = "tcp" + cidr_blocks = ["10.0.0.0/8"] + } + panorama_log = { + description = "Permit Panorama Logging" + type = "ingress", from_port = "28443", to_port = "28443", protocol = "tcp" + cidr_blocks = ["10.0.0.0/8"] + } + } + } + vmseries_public = { + name = "vmseries_public" + 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"] + } + ssh = { + description = "Permit SSH" + type = "ingress", from_port = "22", to_port = "22", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # TODO: update here (replace 0.0.0.0/0 by your IP range) + } + https = { + description = "Permit HTTPS" + type = "ingress", from_port = "443", to_port = "443", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # TODO: update here (replace 0.0.0.0/0 by your IP range) + } + http = { + description = "Permit HTTP" + type = "ingress", from_port = "80", to_port = "80", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # 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 } + "10.100.64.0/24" = { az = "eu-central-1b", set = "mgmt", nacl = null } + "10.100.1.0/24" = { az = "eu-central-1a", set = "private", nacl = "trusted_path_monitoring" } + "10.100.65.0/24" = { az = "eu-central-1b", set = "private", nacl = "trusted_path_monitoring" } + "10.100.2.0/24" = { az = "eu-central-1a", set = "public", nacl = null } + "10.100.66.0/24" = { az = "eu-central-1b", set = "public", nacl = null } + "10.100.3.0/24" = { az = "eu-central-1a", set = "tgw_attach", nacl = null } + "10.100.67.0/24" = { az = "eu-central-1b", set = "tgw_attach", nacl = null } + "10.100.4.0/24" = { az = "eu-central-1a", set = "gwlbe_outbound", nacl = null } + "10.100.68.0/24" = { az = "eu-central-1b", set = "gwlbe_outbound", nacl = null } + "10.100.5.0/24" = { az = "eu-central-1a", set = "gwlb", nacl = null } + "10.100.69.0/24" = { az = "eu-central-1b", set = "gwlb", nacl = null } + "10.100.10.0/24" = { az = "eu-central-1a", set = "gwlbe_eastwest", nacl = null } + "10.100.74.0/24" = { az = "eu-central-1b", set = "gwlbe_eastwest", 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" + } + mgmt_panorama = { + vpc_subnet = "security_vpc-mgmt" + to_cidr = "10.255.0.0/16" + next_hop_key = "security" + next_hop_type = "transit_gateway_attachment" + } + mgmt_rfc1918 = { + vpc_subnet = "security_vpc-mgmt" + to_cidr = "10.0.0.0/8" + next_hop_key = "security" + next_hop_type = "transit_gateway_attachment" + } + tgw_rfc1918 = { + vpc_subnet = "security_vpc-tgw_attach" + to_cidr = "10.0.0.0/8" + next_hop_key = "security_gwlb_eastwest" + next_hop_type = "gwlbe_endpoint" + } + tgw_default = { + vpc_subnet = "security_vpc-tgw_attach" + to_cidr = "0.0.0.0/0" + next_hop_key = "security_gwlb_outbound" + next_hop_type = "gwlbe_endpoint" + } + public_default = { + vpc_subnet = "security_vpc-public" + to_cidr = "0.0.0.0/0" + next_hop_key = "security_vpc" + next_hop_type = "internet_gateway" + } + gwlbe_outbound_rfc1918 = { + vpc_subnet = "security_vpc-gwlbe_outbound" + to_cidr = "10.0.0.0/8" + next_hop_key = "security" + next_hop_type = "transit_gateway_attachment" + } + gwlbe_eastwest_rfc1918 = { + vpc_subnet = "security_vpc-gwlbe_eastwest" + to_cidr = "10.0.0.0/8" + next_hop_key = "security" + next_hop_type = "transit_gateway_attachment" + } + } + } + app1_vpc = { + name = "app1-spoke-vpc" + cidr = "10.104.0.0/16" + nacls = {} + security_groups = { + app1_vm = { + name = "app1_vm" + 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"] + } + ssh = { + description = "Permit SSH" + type = "ingress", from_port = "22", to_port = "22", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # TODO: update here (replace 0.0.0.0/0 by your IP range) + } + https = { + description = "Permit HTTPS" + type = "ingress", from_port = "443", to_port = "443", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # TODO: update here (replace 0.0.0.0/0 by your IP range) + } + http = { + description = "Permit HTTP" + type = "ingress", from_port = "80", to_port = "80", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # 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. + "10.104.0.0/24" = { az = "eu-central-1a", set = "app1_vm", nacl = null } + "10.104.128.0/24" = { az = "eu-central-1b", set = "app1_vm", nacl = null } + "10.104.2.0/24" = { az = "eu-central-1a", set = "app1_lb", nacl = null } + "10.104.130.0/24" = { az = "eu-central-1b", set = "app1_lb", nacl = null } + "10.104.3.0/24" = { az = "eu-central-1a", set = "app1_gwlbe", nacl = null } + "10.104.131.0/24" = { az = "eu-central-1b", set = "app1_gwlbe", 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 + vm_default = { + vpc_subnet = "app1_vpc-app1_vm" + to_cidr = "0.0.0.0/0" + next_hop_key = "app1" + next_hop_type = "transit_gateway_attachment" + } + gwlbe_default = { + vpc_subnet = "app1_vpc-app1_gwlbe" + to_cidr = "0.0.0.0/0" + next_hop_key = "app1_vpc" + next_hop_type = "internet_gateway" + } + lb_default = { + vpc_subnet = "app1_vpc-app1_lb" + to_cidr = "0.0.0.0/0" + next_hop_key = "app1_inbound" + next_hop_type = "gwlbe_endpoint" + } + } + } + app2_vpc = { + name = "app2-spoke-vpc" + cidr = "10.105.0.0/16" + nacls = {} + security_groups = { + app2_vm = { + name = "app2_vm" + 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"] + } + ssh = { + description = "Permit SSH" + type = "ingress", from_port = "22", to_port = "22", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # TODO: update here (replace 0.0.0.0/0 by your IP range) + } + https = { + description = "Permit HTTPS" + type = "ingress", from_port = "443", to_port = "443", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # TODO: update here (replace 0.0.0.0/0 by your IP range) + } + http = { + description = "Permit HTTP" + type = "ingress", from_port = "80", to_port = "80", protocol = "tcp" + cidr_blocks = ["0.0.0.0/0", "10.104.0.0/16", "10.105.0.0/16"] # 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. + "10.105.0.0/24" = { az = "eu-central-1a", set = "app2_vm", nacl = null } + "10.105.128.0/24" = { az = "eu-central-1b", set = "app2_vm", nacl = null } + "10.105.2.0/24" = { az = "eu-central-1a", set = "app2_lb", nacl = null } + "10.105.130.0/24" = { az = "eu-central-1b", set = "app2_lb", nacl = null } + "10.105.3.0/24" = { az = "eu-central-1a", set = "app2_gwlbe", nacl = null } + "10.105.131.0/24" = { az = "eu-central-1b", set = "app2_gwlbe", 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 + vm_default = { + vpc_subnet = "app2_vpc-app2_vm" + to_cidr = "0.0.0.0/0" + next_hop_key = "app2" + next_hop_type = "transit_gateway_attachment" + } + gwlbe_default = { + vpc_subnet = "app2_vpc-app2_gwlbe" + to_cidr = "0.0.0.0/0" + next_hop_key = "app2_vpc" + next_hop_type = "internet_gateway" + } + lb_default = { + vpc_subnet = "app2_vpc-app2_lb" + to_cidr = "0.0.0.0/0" + next_hop_key = "app2_inbound" + next_hop_type = "gwlbe_endpoint" + } + } + } +} + +### TRANSIT GATEWAY +tgw = { + create = true + id = null + name = "tgw" + asn = "64512" + route_tables = { + # Do not change keys `from_security_vpc` and `from_spoke_vpc` as they are used in `main.tf` and attachments + "from_security_vpc" = { + create = true + name = "from_security" + } + "from_spoke_vpc" = { + create = true + name = "from_spokes" + } + } + attachments = { + # Value of `vpc_subnet` is built from key of VPCs concatenate with `-` and key of subnet in format: `VPCKEY-SUBNETKEY` + # Value of `route_table` and `propagate_routes_to` must match `route_tables` stores under `tgw` + security = { + name = "vmseries" + vpc_subnet = "security_vpc-tgw_attach" + route_table = "from_security_vpc" + propagate_routes_to = "from_spoke_vpc" + } + app1 = { + name = "app1-spoke-vpc" + vpc_subnet = "app1_vpc-app1_vm" + route_table = "from_spoke_vpc" + propagate_routes_to = "from_security_vpc" + } + app2 = { + name = "app2-spoke-vpc" + vpc_subnet = "app2_vpc-app2_vm" + route_table = "from_spoke_vpc" + propagate_routes_to = "from_security_vpc" + } + } +} + +### NAT GATEWAY +natgws = {} + +### GATEWAY LOADBALANCER +gwlbs = { + # Value of `vpc_subnet` is built from key of VPCs concatenate with `-` and key of subnet in format: `VPCKEY-SUBNETKEY` + security_gwlb = { + name = "security-gwlb" + vpc_subnet = "security_vpc-gwlb" + } +} +gwlb_endpoints = { + # Value of `gwlb` must match key of objects stored in `gwlbs` + # Value of `vpc` must match key of objects stored in `vpcs` + # Value of `vpc_subnet` is built from key of VPCs concatenate with `-` and key of subnet in format: `VPCKEY-SUBNETKEY` + security_gwlb_eastwest = { + name = "eastwest-gwlb-endpoint" + gwlb = "security_gwlb" + vpc = "security_vpc" + vpc_subnet = "security_vpc-gwlbe_eastwest" + act_as_next_hop = false + to_vpc_subnets = null + } + security_gwlb_outbound = { + name = "outbound-gwlb-endpoint" + gwlb = "security_gwlb" + vpc = "security_vpc" + vpc_subnet = "security_vpc-gwlbe_outbound" + act_as_next_hop = false + to_vpc_subnets = null + } + app1_inbound = { + name = "app1-gwlb-endpoint" + gwlb = "security_gwlb" + vpc = "app1_vpc" + vpc_subnet = "app1_vpc-app1_gwlbe" + act_as_next_hop = true + to_vpc_subnets = "app1_vpc-app1_lb" + } + app2_inbound = { + name = "app2-gwlb-endpoint" + gwlb = "security_gwlb" + vpc = "app2_vpc" + vpc_subnet = "app2_vpc-app2_gwlbe" + act_as_next_hop = true + to_vpc_subnets = "app2_vpc-app2_lb" + } +} + +### VM-SERIES +vmseries_asgs = { + main_asg = { + # Value of `panorama-server`, `auth-key`, `dgname`, `tplname` can be taken from plugin `sw_fw_license` + bootstrap_options = { + mgmt-interface-swap = "enable" + plugin-op-commands = "panorama-licensing-mode-on,aws-gwlb-inspect:enable,aws-gwlb-overlay-routing:enable" # TODO: update here + panorama-server = "" # TODO: update here + auth-key = "" # TODO: update here + dgname = "" # TODO: update here + tplname = "" # TODO: update here + dhcp-send-hostname = "yes" # TODO: update here + dhcp-send-client-id = "yes" # TODO: update here + dhcp-accept-server-hostname = "yes" # TODO: update here + dhcp-accept-server-domain = "yes" # 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" + + # Value of `gwlb` must match key of objects stored in `gwlbs` + gwlb = "security_gwlb" + + interfaces = { + private = { + device_index = 0 + security_group = "vmseries_private" + subnet = { + "privatea" = "eu-central-1a", + "privateb" = "eu-central-1b" + } + create_public_ip = false + source_dest_check = false + } + mgmt = { + device_index = 1 + security_group = "vmseries_mgmt" + subnet = { + "mgmta" = "eu-central-1a", + "mgmtb" = "eu-central-1b" + } + create_public_ip = true + source_dest_check = true + } + public = { + device_index = 2 + security_group = "vmseries_public" + subnet = { + "publica" = "eu-central-1a", + "publicb" = "eu-central-1b" + } + create_public_ip = true + source_dest_check = false + } + } + + # Value of `gwlb_endpoint` must match key of objects stored in `gwlb_endpoints` + subinterfaces = { + inbound = { + app1 = { + gwlb_endpoint = "app1_inbound" + subinterface = "ethernet1/1.11" + } + app2 = { + gwlb_endpoint = "app2_inbound" + subinterface = "ethernet1/1.12" + } + } + outbound = { + only_1_outbound = { + gwlb_endpoint = "security_gwlb_outbound" + subinterface = "ethernet1/1.20" + } + } + eastwest = { + only_1_eastwest = { + gwlb_endpoint = "security_gwlb_eastwest" + subinterface = "ethernet1/1.30" + } + } + } + + asg = { + desired_cap = 0 + min_size = 0 + max_size = 4 + } + + scaling_plan = { + enabled = true # TODO: update here + metric_name = "panSessionActive" # TODO: update here + target_value = 75 # TODO: update here + statistic = "Average" # TODO: update here + cloudwatch_namespace = "example-vmseries" # TODO: update here + tags = { + ManagedBy = "terraform" + } + } + } +} + +### PANORAMA +panorama_attachment = { + transit_gateway_attachment_id = null # TODO: update here + vpc_cidr = "10.255.0.0/24" # TODO: update here +} + +### SPOKE VMS +spoke_vms = { + "app1_vm01" = { + az = "eu-central-1a" + vpc = "app1_vpc" + vpc_subnet = "app1_vpc-app1_vm" + security_group = "app1_vm" + type = "t2.micro" + } + "app1_vm02" = { + az = "eu-central-1b" + vpc = "app1_vpc" + vpc_subnet = "app1_vpc-app1_vm" + security_group = "app1_vm" + type = "t2.micro" + } + "app2_vm01" = { + az = "eu-central-1a" + vpc = "app2_vpc" + vpc_subnet = "app2_vpc-app2_vm" + security_group = "app2_vm" + type = "t2.micro" + } + "app2_vm02" = { + az = "eu-central-1b" + vpc = "app2_vpc" + vpc_subnet = "app2_vpc-app2_vm" + security_group = "app2_vm" + type = "t2.micro" + } +} + +### SPOKE LOADBALANCERS +spoke_lbs = { + "app1-nlb" = { + vpc_subnet = "app1_vpc-app1_lb" + vms = ["app1_vm01", "app1_vm02"] + } + "app2-nlb" = { + vpc_subnet = "app2_vpc-app2_lb" + vms = ["app2_vm01", "app2_vm02"] + } +} \ No newline at end of file diff --git a/examples/combined_design_autoscale/main.tf b/examples/combined_design_autoscale/main.tf index 2d15e69d..b47547a9 100644 --- a/examples/combined_design_autoscale/main.tf +++ b/examples/combined_design_autoscale/main.tf @@ -336,5 +336,5 @@ module "vm_series_asg" { scaling_target_value = each.value.scaling_plan.target_value scaling_statistic = each.value.scaling_plan.statistic scaling_cloudwatch_namespace = each.value.scaling_plan.cloudwatch_namespace - scaling_tags = each.value.scaling_plan.tags + scaling_tags = merge(each.value.scaling_plan.tags, { prefix : var.name_prefix }) } diff --git a/examples/combined_design_autoscale/main_test.go b/examples/combined_design_autoscale/main_test.go new file mode 100644 index 00000000..bcea9e06 --- /dev/null +++ b/examples/combined_design_autoscale/main_test.go @@ -0,0 +1,47 @@ +package centralized_design + +import ( + "fmt" + "math/rand" + "os" + "testing" + "time" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestExampleCombinedAutoscaleDesign(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") + } +} diff --git a/examples/isolated_design/Makefile b/examples/isolated_design/Makefile new file mode 100644 index 00000000..7258f45c --- /dev/null +++ b/examples/isolated_design/Makefile @@ -0,0 +1,11 @@ +init: + @../../makefile.sh init + +validate: + @../../makefile.sh validate + +test: + @../../makefile.sh test + +destroy: + @../../makefile.sh destroy diff --git a/examples/isolated_design/example.tfvars b/examples/isolated_design/example.tfvars index ca8d85e5..7924166b 100644 --- a/examples/isolated_design/example.tfvars +++ b/examples/isolated_design/example.tfvars @@ -167,12 +167,12 @@ vpcs = { next_hop_key = "security_vpc" next_hop_type = "internet_gateway" } - mgmt_panorama = { - vpc_subnet = "security_vpc-mgmt" - to_cidr = "10.255.0.0/24" - next_hop_key = "security_vpc_panorama" - next_hop_type = "vpc_peer" - } + # mgmt_panorama = { + # vpc_subnet = "security_vpc-mgmt" + # to_cidr = "10.255.0.0/24" + # next_hop_key = "security_vpc_panorama" + # next_hop_type = "vpc_peer" + # } public_default = { vpc_subnet = "security_vpc-public" to_cidr = "0.0.0.0/0" @@ -473,7 +473,7 @@ vmseries = { ### PANORAMA panorama_connection = { security_vpc = "security_vpc" - peering_vpc_id = "vpc-123456789" # TODO: update here + peering_vpc_id = null # TODO: update here vpc_cidr = "10.255.0.0/24" # TODO: update here } diff --git a/examples/isolated_design/main.tf b/examples/isolated_design/main.tf index b944445c..9369fde6 100644 --- a/examples/isolated_design/main.tf +++ b/examples/isolated_design/main.tf @@ -49,6 +49,7 @@ module "subnet_sets" { ### VPC PEERINGS ### resource "aws_vpc_peering_connection" "this" { + count = var.panorama_connection.peering_vpc_id != null ? 1 : 0 peer_vpc_id = var.panorama_connection.peering_vpc_id vpc_id = module.vpc[var.panorama_connection.security_vpc].id auto_accept = true @@ -116,9 +117,9 @@ locals { next_hop_set = ( rv.next_hop_type == "internet_gateway" ? module.vpc[rv.next_hop_key].igw_as_next_hop_set : ( rv.next_hop_type == "gwlbe_endpoint" ? module.gwlbe_endpoint[rv.next_hop_key].next_hop_set : ( - rv.next_hop_type == "vpc_peer" ? { + rv.next_hop_type == "vpc_peer" && var.panorama_connection.peering_vpc_id != null ? { type = "vpc_peer" - id = aws_vpc_peering_connection.this.id + id = aws_vpc_peering_connection.this[0].id ids = {} } : null ) diff --git a/examples/isolated_design/main_test.go b/examples/isolated_design/main_test.go new file mode 100644 index 00000000..234e95cd --- /dev/null +++ b/examples/isolated_design/main_test.go @@ -0,0 +1,47 @@ +package centralized_design + +import ( + "fmt" + "math/rand" + "os" + "testing" + "time" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestExampleIsolatedDesign(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") + } +} diff --git a/examples/panorama_standalone/Makefile b/examples/panorama_standalone/Makefile new file mode 100644 index 00000000..7258f45c --- /dev/null +++ b/examples/panorama_standalone/Makefile @@ -0,0 +1,11 @@ +init: + @../../makefile.sh init + +validate: + @../../makefile.sh validate + +test: + @../../makefile.sh test + +destroy: + @../../makefile.sh destroy diff --git a/examples/panorama_standalone/main_test.go b/examples/panorama_standalone/main_test.go new file mode 100644 index 00000000..335355d9 --- /dev/null +++ b/examples/panorama_standalone/main_test.go @@ -0,0 +1,47 @@ +package centralized_design + +import ( + "fmt" + "math/rand" + "os" + "testing" + "time" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestExampleStandalonePanorama(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") + } +} diff --git a/examples/standalone_vmseries_with_userdata_bootstrap/Makefile b/examples/standalone_vmseries_with_userdata_bootstrap/Makefile new file mode 100644 index 00000000..7258f45c --- /dev/null +++ b/examples/standalone_vmseries_with_userdata_bootstrap/Makefile @@ -0,0 +1,11 @@ +init: + @../../makefile.sh init + +validate: + @../../makefile.sh validate + +test: + @../../makefile.sh test + +destroy: + @../../makefile.sh destroy diff --git a/examples/standalone_vmseries_with_userdata_bootstrap/README.md b/examples/standalone_vmseries_with_userdata_bootstrap/README.md index f7397a1a..6b288e5f 100644 --- a/examples/standalone_vmseries_with_userdata_bootstrap/README.md +++ b/examples/standalone_vmseries_with_userdata_bootstrap/README.md @@ -71,7 +71,7 @@ No resources. | [bootstrap\_options](#input\_bootstrap\_options) | n/a | `any` | n/a | yes | | [ebs\_kms\_key\_alias](#input\_ebs\_kms\_key\_alias) | n/a | `any` | n/a | yes | | [global\_tags](#input\_global\_tags) | n/a | `any` | n/a | yes | -| [name](#input\_name) | n/a | `any` | n/a | yes | +| [name\_prefix](#input\_name\_prefix) | n/a | `any` | n/a | yes | | [region](#input\_region) | n/a | `any` | n/a | yes | | [security\_vpc\_cidr](#input\_security\_vpc\_cidr) | n/a | `any` | n/a | yes | | [security\_vpc\_name](#input\_security\_vpc\_name) | n/a | `any` | n/a | yes | diff --git a/examples/standalone_vmseries_with_userdata_bootstrap/example.tfvars b/examples/standalone_vmseries_with_userdata_bootstrap/example.tfvars index 576930cb..9022f494 100644 --- a/examples/standalone_vmseries_with_userdata_bootstrap/example.tfvars +++ b/examples/standalone_vmseries_with_userdata_bootstrap/example.tfvars @@ -1,6 +1,6 @@ # General -region = "us-east-1" -name = "vmseries-example" +region = "eu-central-1" +name_prefix = "vmseries-example" global_tags = { ManagedBy = "Terraform" Application = "Palo Alto Networks VM-Series NGFW" @@ -13,7 +13,7 @@ security_vpc_cidr = "10.100.0.0/16" # Subnets security_vpc_subnets = { # Do not modify value of `set=`, it is an internal identifier referenced by main.tf. - "10.100.0.0/24" = { az = "us-east-1a", set = "mgmt" } + "10.100.0.0/24" = { az = "eu-central-1a", set = "mgmt", nacl = null } } # Security Groups @@ -45,7 +45,7 @@ ssh_key_name = "example-ssh-key" vmseries_version = "10.2.2" vmseries = { vmseries01 = { - az = "us-east-1a" + az = "eu-central-1a" interfaces = { mgmt = { device_index = 0 @@ -60,7 +60,7 @@ vmseries = { bootstrap_options = "plugin-op-commands=aws-gwlb-inspect:enable,aws-gwlb-overlay-routing:enable;type=dhcp-client;hostname=vms01" -ebs_kms_key_alias = "alias/example-key-alias" +ebs_kms_key_alias = "alias/aws/ebs" # Routes security_vpc_routes_outbound_destin_cidrs = ["0.0.0.0/0"] diff --git a/examples/standalone_vmseries_with_userdata_bootstrap/main.tf b/examples/standalone_vmseries_with_userdata_bootstrap/main.tf index 1335c89b..af185ddf 100644 --- a/examples/standalone_vmseries_with_userdata_bootstrap/main.tf +++ b/examples/standalone_vmseries_with_userdata_bootstrap/main.tf @@ -1,7 +1,7 @@ module "security_vpc" { source = "../../modules/vpc" - name = var.security_vpc_name + name = "${var.name_prefix}${var.security_vpc_name}" cidr_block = var.security_vpc_cidr security_groups = var.security_vpc_security_groups create_internet_gateway = true @@ -18,14 +18,25 @@ module "security_subnet_sets" { name = 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 } + nacl_associations = { + for i in flatten([ + for k, v in var.security_vpc_subnets : + { + az : v.az, + nacl_id : lookup(module.security_vpc.nacl_ids, v.nacl, null) + } if v.nacl != null && v.set == each.key + ]) : i.az => i.nacl_id + } + cidrs = { + for k, v in var.security_vpc_subnets : k => v if v.set == each.key + } } module "vmseries" { for_each = var.vmseries source = "../../modules/vmseries" - name = var.name + name = "${var.name_prefix}vmseries" ssh_key_name = var.ssh_key_name bootstrap_options = var.bootstrap_options ebs_kms_key_alias = var.ebs_kms_key_alias diff --git a/examples/standalone_vmseries_with_userdata_bootstrap/main_test.go b/examples/standalone_vmseries_with_userdata_bootstrap/main_test.go new file mode 100644 index 00000000..321b2656 --- /dev/null +++ b/examples/standalone_vmseries_with_userdata_bootstrap/main_test.go @@ -0,0 +1,47 @@ +package centralized_design + +import ( + "fmt" + "math/rand" + "os" + "testing" + "time" + + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/terraform" +) + +func TestExampleStandaloneVmseries(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") + } +} diff --git a/examples/standalone_vmseries_with_userdata_bootstrap/variables.tf b/examples/standalone_vmseries_with_userdata_bootstrap/variables.tf index 8e5b5479..f05af290 100644 --- a/examples/standalone_vmseries_with_userdata_bootstrap/variables.tf +++ b/examples/standalone_vmseries_with_userdata_bootstrap/variables.tf @@ -1,5 +1,5 @@ variable "region" {} -variable "name" {} +variable "name_prefix" {} variable "global_tags" {} variable "security_vpc_name" {} variable "security_vpc_cidr" {} diff --git a/tests/internal/helpers/helpers.go b/go/helpers/helpers.go similarity index 100% rename from tests/internal/helpers/helpers.go rename to go/helpers/helpers.go diff --git a/tests/internal/testskeleton/testskeleton.go b/go/testskeleton/testskeleton.go similarity index 98% rename from tests/internal/testskeleton/testskeleton.go rename to go/testskeleton/testskeleton.go index ffe90ec7..05f5b625 100644 --- a/tests/internal/testskeleton/testskeleton.go +++ b/go/testskeleton/testskeleton.go @@ -275,9 +275,13 @@ func PlanInfraCheckErrors(t *testing.T, terraformOptions *terraform.Options, // Terraform initalization and plan if _, err := terraform.InitAndPlanE(t, terraformOptions); err != nil { - // Verify errors and compare to expected results - assert.Error(t, err) - AssertErrors(t, err, assertList) + if len(assertList) > 0 { + // Verify errors and compare to expected results + assert.Error(t, err) + AssertErrors(t, err, assertList) + } else { + t.Error(noErrorsMessage) + } } else { // Fail test, if errors were expected if len(assertList) > 0 { diff --git a/makefile.sh b/makefile.sh new file mode 100755 index 00000000..a1cff972 --- /dev/null +++ b/makefile.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -o pipefail +set -e + +COMMAND=$1 + +case $1 in + init) + echo ":: INITIALIZING TERRAFORM ::" + terraform init + echo + ;; + + validate) + echo ":: INITIALIZING TERRAFORM ::" + terraform init -backend=false + echo + echo ":: VALIDATING CODE ::" + terraform validate + echo + ;; + + test) + echo ":: DOWNLOADING GO DEPENDENCIES ::" + go get -v -t -d && go mod tidy + echo + echo ":: EXECUTING TERRATEST ::" + go test -v -timeout 120m -count=1 + echo + ;; + + destroy) + echo ":: DESTROYING INFRASTRUCTURE ::" + for i in {1..3}; do terraform destroy -auto-approve -var-file=example.tfvars && break || sleep 3; done + ;; + + *) + echo "ERROR: wrong param passed:: [$1]" + exit 1 + +esac diff --git a/modules/alb/Makefile b/modules/alb/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/alb/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/asg/Makefile b/modules/asg/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/asg/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/bootstrap/Makefile b/modules/bootstrap/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/bootstrap/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/crosszone_failover/Makefile b/modules/crosszone_failover/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/crosszone_failover/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/gwlb/Makefile b/modules/gwlb/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/gwlb/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/gwlb_endpoint_set/Makefile b/modules/gwlb_endpoint_set/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/gwlb_endpoint_set/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/nat_gateway_set/Makefile b/modules/nat_gateway_set/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/nat_gateway_set/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/nlb/Makefile b/modules/nlb/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/nlb/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/panorama/Makefile b/modules/panorama/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/panorama/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/subnet_set/Makefile b/modules/subnet_set/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/subnet_set/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/transit_gateway/Makefile b/modules/transit_gateway/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/transit_gateway/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/transit_gateway_attachment/Makefile b/modules/transit_gateway_attachment/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/transit_gateway_attachment/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/transit_gateway_peering/Makefile b/modules/transit_gateway_peering/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/transit_gateway_peering/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/transit_gateway_peering/versions.tf b/modules/transit_gateway_peering/versions.tf index 238718cf..9ff8478e 100644 --- a/modules/transit_gateway_peering/versions.tf +++ b/modules/transit_gateway_peering/versions.tf @@ -8,3 +8,7 @@ terraform { } } } + +provider "aws" { + alias = "remote" +} diff --git a/modules/vmseries/Makefile b/modules/vmseries/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/vmseries/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/vpc/Makefile b/modules/vpc/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/vpc/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/vpc_endpoint/Makefile b/modules/vpc_endpoint/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/vpc_endpoint/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/modules/vpc_route/Makefile b/modules/vpc_route/Makefile new file mode 100644 index 00000000..f9cee6eb --- /dev/null +++ b/modules/vpc_route/Makefile @@ -0,0 +1,2 @@ +validate: + @../../makefile.sh validate \ No newline at end of file diff --git a/tests/alb/main_test.go b/tests/alb/main_test.go index b457f583..1a12bb86 100644 --- a/tests/alb/main_test.go +++ b/tests/alb/main_test.go @@ -4,8 +4,8 @@ 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/PaloAltoNetworks/terraform-aws-vmseries-modules/go/helpers" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/bootstrap/main_test.go b/tests/bootstrap/main_test.go index 93338a59..87ae09eb 100644 --- a/tests/bootstrap/main_test.go +++ b/tests/bootstrap/main_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/panorama/main_test.go b/tests/panorama/main_test.go index 211ed453..9db64d85 100644 --- a/tests/panorama/main_test.go +++ b/tests/panorama/main_test.go @@ -3,8 +3,8 @@ 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/PaloAltoNetworks/terraform-aws-vmseries-modules/go/helpers" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/subnet_set/main_test.go b/tests/subnet_set/main_test.go index 7c8e6fb4..48e8573f 100644 --- a/tests/subnet_set/main_test.go +++ b/tests/subnet_set/main_test.go @@ -3,7 +3,7 @@ package subnet_set import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/transit_gateway/main_test.go b/tests/transit_gateway/main_test.go index a7e60cad..ffc7ec81 100644 --- a/tests/transit_gateway/main_test.go +++ b/tests/transit_gateway/main_test.go @@ -3,7 +3,7 @@ package transit_gateway import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/transit_gateway_attachment/main_test.go b/tests/transit_gateway_attachment/main_test.go index c0c21aac..0b320c64 100644 --- a/tests/transit_gateway_attachment/main_test.go +++ b/tests/transit_gateway_attachment/main_test.go @@ -3,7 +3,7 @@ package transit_gateway_attachment import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/transit_gateway_peering/main_test.go b/tests/transit_gateway_peering/main_test.go index 3b25ce00..82e6b646 100644 --- a/tests/transit_gateway_peering/main_test.go +++ b/tests/transit_gateway_peering/main_test.go @@ -3,7 +3,7 @@ package transit_gateway_peering import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/vmseries/main_test.go b/tests/vmseries/main_test.go index aa7943dd..b4e227c1 100644 --- a/tests/vmseries/main_test.go +++ b/tests/vmseries/main_test.go @@ -3,8 +3,8 @@ package vmseries import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/helpers" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/helpers" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" tfjson "github.com/hashicorp/terraform-json" diff --git a/tests/vpc/deployment/main_test.go b/tests/vpc/deployment/main_test.go index 9794dc70..02d5d736 100644 --- a/tests/vpc/deployment/main_test.go +++ b/tests/vpc/deployment/main_test.go @@ -3,7 +3,7 @@ package vpc_deployment import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/vpc/plan/main_test.go b/tests/vpc/plan/main_test.go index b806c279..10d36343 100644 --- a/tests/vpc/plan/main_test.go +++ b/tests/vpc/plan/main_test.go @@ -3,7 +3,7 @@ package vpc_plan import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" ) diff --git a/tests/vpc_route/main_test.go b/tests/vpc_route/main_test.go index a1ea812d..19c89365 100644 --- a/tests/vpc_route/main_test.go +++ b/tests/vpc_route/main_test.go @@ -3,7 +3,7 @@ package vpc_route import ( "testing" - "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/tests/internal/testskeleton" + "github.com/PaloAltoNetworks/terraform-aws-vmseries-modules/go/testskeleton" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/terraform" )