├── requirements.txt
├── .github
├── CODEOWNERS
├── workflows
│ ├── lint_pr_title.yml
│ ├── release_ci.yml
│ ├── pr_ci.yml
│ ├── hub_sync.yml
│ ├── pre-commit-update.yml
│ ├── plan-command.yml
│ ├── apply-command.yml
│ ├── idempotence-command.yml
│ ├── validate-command.yml
│ ├── help-command.yml
│ ├── sca-command.yml
│ └── chatops.yml
├── ISSUE_TEMPLATE
│ ├── feature_request.yml
│ └── bug_report.yml
└── actions
│ └── terratest
│ └── action.yml
├── modules
├── vpc-peering
│ ├── versions.tf
│ ├── main_test.go
│ ├── main.tf
│ ├── variables.tf
│ └── README.md
├── iam_service_account
│ ├── outputs.tf
│ ├── versions.tf
│ ├── main_test.go
│ ├── main.tf
│ ├── variables.tf
│ └── README.md
├── bootstrap
│ ├── versions.tf
│ ├── outputs.tf
│ ├── main_test.go
│ ├── variables.tf
│ ├── main.tf
│ └── README.md
├── lb_external
│ ├── versions.tf
│ ├── main_test.go
│ ├── outputs.tf
│ ├── variables.tf
│ └── main.tf
├── lb_internal
│ ├── versions.tf
│ ├── outputs.tf
│ ├── main_test.go
│ ├── main.tf
│ └── variables.tf
├── lb_http_ext_global
│ ├── versions.tf
│ ├── main_test.go
│ ├── outputs.tf
│ ├── howto_lb_region.md
│ ├── main.tf
│ └── variables.tf
├── vpc
│ ├── versions.tf
│ ├── main_test.go
│ ├── outputs.tf
│ ├── main.tf
│ ├── variables.tf
│ └── README.md
├── vpn
│ ├── versions.tf
│ ├── main_test.go
│ ├── outputs.tf
│ ├── variables.tf
│ └── main.tf
├── autoscale
│ ├── versions.tf
│ ├── src
│ │ └── requirements.txt
│ ├── main_test.go
│ └── outputs.tf
├── panorama
│ ├── versions.tf
│ ├── main_test.go
│ ├── outputs.tf
│ ├── main.tf
│ └── variables.tf
└── vmseries
│ ├── versions.tf
│ ├── main_test.go
│ ├── outputs.tf
│ └── main.tf
├── examples
├── panorama_standalone
│ ├── versions.tf
│ ├── outputs.tf
│ ├── example.tfvars
│ ├── main.tf
│ ├── main_test.go
│ ├── variables.tf
│ └── README.md
├── standalone_vmseries_with_metadata_bootstrap
│ ├── versions.tf
│ ├── outputs.tf
│ ├── main.tf
│ ├── main_test.go
│ ├── example.tfvars
│ ├── variables.tf
│ └── README.md
├── vpc_peering_common_with_network_tags
│ ├── versions.tf
│ ├── templates
│ │ └── init-cfg.tmpl
│ ├── outputs.tf
│ └── main_test.go
├── vmseries_ha
│ ├── versions.tf
│ ├── templates
│ │ └── init-cfg.tmpl
│ ├── outputs.tf
│ └── main_test.go
├── multi_nic_common
│ ├── versions.tf
│ ├── templates
│ │ └── init-cfg.tmpl
│ ├── outputs.tf
│ └── main_test.go
├── vpc_peering_common
│ ├── versions.tf
│ ├── templates
│ │ └── init-cfg.tmpl
│ ├── outputs.tf
│ └── main_test.go
├── vpc_peering_dedicated
│ ├── versions.tf
│ ├── templates
│ │ └── init-cfg.tmpl
│ ├── outputs.tf
│ └── main_test.go
├── vpc_peering_common_with_autoscale
│ ├── versions.tf
│ ├── outputs.tf
│ └── main_test.go
└── vpc_peering_dedicated_with_autoscale
│ ├── versions.tf
│ ├── outputs.tf
│ └── main_test.go
├── scripts
├── run.sh
├── install.sh
├── decisions.md
└── requirements.txt
├── Makefile
├── SUPPORT.md
├── LICENSE
├── .gitignore
├── .pre-commit-config.yaml
├── .releaserc.json
├── go.mod
├── README.md
└── CONTRIBUTING.md
/requirements.txt:
--------------------------------------------------------------------------------
1 | pre-commit==3.4.0
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @PaloAltoNetworks/gcp-vmseries-modules-codeowners
2 |
--------------------------------------------------------------------------------
/modules/vpc-peering/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
--------------------------------------------------------------------------------
/modules/iam_service_account/outputs.tf:
--------------------------------------------------------------------------------
1 | output "email" {
2 | value = google_service_account.this.email
3 | }
4 |
--------------------------------------------------------------------------------
/modules/bootstrap/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = { version = "~> 4.54" }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/modules/iam_service_account/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = { version = "~> 4.54" }
5 | }
6 | }
--------------------------------------------------------------------------------
/modules/lb_external/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = { version = "~> 4.54" }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/modules/lb_internal/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = { version = "~> 4.54" }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/modules/bootstrap/outputs.tf:
--------------------------------------------------------------------------------
1 | output "bucket_name" {
2 | value = google_storage_bucket.this.name
3 | }
4 |
5 | output "bucket" {
6 | value = google_storage_bucket.this
7 | }
--------------------------------------------------------------------------------
/modules/lb_http_ext_global/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = { version = "~> 4.54" }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/modules/vpc/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = {
5 | version = "~> 4.54"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/modules/vpn/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = {
5 | version = ">= 4.58"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/panorama_standalone/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | region = var.region
8 | }
--------------------------------------------------------------------------------
/modules/autoscale/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = {
5 | version = "~> 4.54"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/modules/panorama/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | google = {
5 | version = "~> 4.54"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/standalone_vmseries_with_metadata_bootstrap/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | }
8 |
--------------------------------------------------------------------------------
/modules/autoscale/src/requirements.txt:
--------------------------------------------------------------------------------
1 | # Function dependencies, for example:
2 | # package>=version
3 | google-api-python-client==2.86.0
4 | pan-os-python==1.11.0
5 | google-cloud-secret-manager==2.16.2
--------------------------------------------------------------------------------
/modules/vmseries/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | required_providers {
4 | null = { version = "~> 3.1" }
5 | google = { version = "~> 4.54" }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/lb_internal/outputs.tf:
--------------------------------------------------------------------------------
1 | output "forwarding_rule" {
2 | value = google_compute_forwarding_rule.this.self_link
3 | }
4 |
5 | output "address" {
6 | value = google_compute_forwarding_rule.this.ip_address
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/examples/vpc_peering_common_with_network_tags/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | }
8 |
9 | provider "google-beta" {
10 | project = var.project
11 | }
12 |
--------------------------------------------------------------------------------
/modules/vpc/main_test.go:
--------------------------------------------------------------------------------
1 | package vpc
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/modules/vpn/main_test.go:
--------------------------------------------------------------------------------
1 | package vpn
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/scripts/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | # run.sh - Run the usual pre-commit checks.
4 |
5 | set -euo pipefail
6 |
7 | pre-commit run --all-files terraform_fmt
8 | pre-commit run --all-files terraform_docs
9 | pre-commit run --all-files terraform_tflint
10 |
--------------------------------------------------------------------------------
/modules/panorama/main_test.go:
--------------------------------------------------------------------------------
1 | package panorama
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/modules/vmseries/main_test.go:
--------------------------------------------------------------------------------
1 | package vmseries
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/examples/vmseries_ha/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | region = var.region
8 | }
9 |
10 | provider "google-beta" {
11 | project = var.project
12 | region = var.region
13 | }
--------------------------------------------------------------------------------
/modules/autoscale/main_test.go:
--------------------------------------------------------------------------------
1 | package autoscale
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/modules/bootstrap/main_test.go:
--------------------------------------------------------------------------------
1 | package bootstrap
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/modules/lb_external/main_test.go:
--------------------------------------------------------------------------------
1 | package lb_external
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/modules/lb_internal/main_test.go:
--------------------------------------------------------------------------------
1 | package lb_internal
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/modules/vpc-peering/main_test.go:
--------------------------------------------------------------------------------
1 | package vpc_peering
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/examples/multi_nic_common/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | region = var.region
8 | }
9 |
10 | provider "google-beta" {
11 | project = var.project
12 | region = var.region
13 | }
14 |
--------------------------------------------------------------------------------
/examples/vpc_peering_common/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | region = var.region
8 | }
9 |
10 | provider "google-beta" {
11 | project = var.project
12 | region = var.region
13 | }
14 |
--------------------------------------------------------------------------------
/modules/lb_http_ext_global/main_test.go:
--------------------------------------------------------------------------------
1 | package lb_http_ext_global
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/examples/vpc_peering_dedicated/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | region = var.region
8 | }
9 |
10 | provider "google-beta" {
11 | project = var.project
12 | region = var.region
13 | }
14 |
--------------------------------------------------------------------------------
/modules/iam_service_account/main_test.go:
--------------------------------------------------------------------------------
1 | package iam_service_account
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
7 | )
8 |
9 | func TestValidate(t *testing.T) {
10 | testskeleton.ValidateCode(t, nil)
11 | }
--------------------------------------------------------------------------------
/examples/vpc_peering_common_with_autoscale/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | region = var.region
8 | }
9 |
10 | provider "google-beta" {
11 | project = var.project
12 | region = var.region
13 | }
14 |
--------------------------------------------------------------------------------
/examples/vpc_peering_dedicated_with_autoscale/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3, < 2.0"
3 | }
4 |
5 | provider "google" {
6 | project = var.project
7 | region = var.region
8 | }
9 |
10 | provider "google-beta" {
11 | project = var.project
12 | region = var.region
13 | }
14 |
--------------------------------------------------------------------------------
/examples/vmseries_ha/templates/init-cfg.tmpl:
--------------------------------------------------------------------------------
1 | %{ if panorama-server != "" ~}
2 | panorama-server=${panorama-server}
3 | %{ endif ~}
4 | %{ if type != "" ~}
5 | type=${type}
6 | %{ endif ~}
7 | %{ if dns-primary != "" ~}
8 | dns-primary=${dns-primary}
9 | %{ endif ~}
10 | %{ if dns-secondary != "" ~}
11 | dns-secondary=${dns-secondary}
12 | %{ endif ~}
--------------------------------------------------------------------------------
/examples/multi_nic_common/templates/init-cfg.tmpl:
--------------------------------------------------------------------------------
1 | %{ if panorama-server != "" ~}
2 | panorama-server=${panorama-server}
3 | %{ endif ~}
4 | %{ if type != "" ~}
5 | type=${type}
6 | %{ endif ~}
7 | %{ if dns-primary != "" ~}
8 | dns-primary=${dns-primary}
9 | %{ endif ~}
10 | %{ if dns-secondary != "" ~}
11 | dns-secondary=${dns-secondary}
12 | %{ endif ~}
--------------------------------------------------------------------------------
/examples/vpc_peering_common/templates/init-cfg.tmpl:
--------------------------------------------------------------------------------
1 | %{ if panorama-server != "" ~}
2 | panorama-server=${panorama-server}
3 | %{ endif ~}
4 | %{ if type != "" ~}
5 | type=${type}
6 | %{ endif ~}
7 | %{ if dns-primary != "" ~}
8 | dns-primary=${dns-primary}
9 | %{ endif ~}
10 | %{ if dns-secondary != "" ~}
11 | dns-secondary=${dns-secondary}
12 | %{ endif ~}
--------------------------------------------------------------------------------
/examples/vpc_peering_dedicated/templates/init-cfg.tmpl:
--------------------------------------------------------------------------------
1 | %{ if panorama-server != "" ~}
2 | panorama-server=${panorama-server}
3 | %{ endif ~}
4 | %{ if type != "" ~}
5 | type=${type}
6 | %{ endif ~}
7 | %{ if dns-primary != "" ~}
8 | dns-primary=${dns-primary}
9 | %{ endif ~}
10 | %{ if dns-secondary != "" ~}
11 | dns-secondary=${dns-secondary}
12 | %{ endif ~}
--------------------------------------------------------------------------------
/examples/vpc_peering_common_with_network_tags/templates/init-cfg.tmpl:
--------------------------------------------------------------------------------
1 | %{ if panorama-server != "" ~}
2 | panorama-server=${panorama-server}
3 | %{ endif ~}
4 | %{ if type != "" ~}
5 | type=${type}
6 | %{ endif ~}
7 | %{ if dns-primary != "" ~}
8 | dns-primary=${dns-primary}
9 | %{ endif ~}
10 | %{ if dns-secondary != "" ~}
11 | dns-secondary=${dns-secondary}
12 | %{ endif ~}
--------------------------------------------------------------------------------
/examples/panorama_standalone/outputs.tf:
--------------------------------------------------------------------------------
1 | output "panorama_private_ips" {
2 | description = "Private IP address of the Panorama instance."
3 | value = { for k, v in module.panorama : k => v.panorama_private_ip }
4 | }
5 |
6 | output "panorama_public_ips" {
7 | description = "Public IP address of the Panorama instance."
8 | value = { for k, v in module.panorama : k => v.panorama_public_ip }
9 | }
--------------------------------------------------------------------------------
/examples/standalone_vmseries_with_metadata_bootstrap/outputs.tf:
--------------------------------------------------------------------------------
1 | output "vmseries_private_ips" {
2 | description = "Private IP addresses of the vmseries instances."
3 | value = { for k, v in module.vmseries : k => v.private_ips }
4 | }
5 |
6 | output "vmseries_public_ips" {
7 | description = "Public IP addresses of the vmseries instances."
8 | value = { for k, v in module.vmseries : k => v.public_ips }
9 | }
--------------------------------------------------------------------------------
/modules/iam_service_account/main.tf:
--------------------------------------------------------------------------------
1 | resource "google_service_account" "this" {
2 | account_id = var.service_account_id
3 | display_name = var.display_name
4 | project = var.project_id
5 | }
6 |
7 | resource "google_project_iam_member" "this" {
8 | for_each = var.roles
9 |
10 | project = var.project_id
11 | role = each.value
12 | member = "serviceAccount:${google_service_account.this.email}"
13 | }
--------------------------------------------------------------------------------
/modules/panorama/outputs.tf:
--------------------------------------------------------------------------------
1 | output "panorama_public_ip" {
2 | description = "Private IP address of the Panorama instance."
3 | value = var.attach_public_ip ? google_compute_instance.this.network_interface[0].access_config[0].nat_ip : null
4 | }
5 |
6 | output "panorama_private_ip" {
7 | description = "Public IP address of the Panorama instance."
8 | value = google_compute_instance.this.network_interface[0].network_ip
9 | }
10 |
--------------------------------------------------------------------------------
/modules/vpc/outputs.tf:
--------------------------------------------------------------------------------
1 | output "network" {
2 | description = "Created or read network attributes."
3 | value = try(data.google_compute_network.this[0], google_compute_network.this[0])
4 | }
5 |
6 | output "subnetworks" {
7 | description = "Map containing key, value pairs of created or read subnetwork attributes."
8 | value = { for k, v in var.subnetworks :
9 | k => try(data.google_compute_subnetwork.this[k], google_compute_subnetwork.this[k], null)
10 | }
11 | }
--------------------------------------------------------------------------------
/.github/workflows/lint_pr_title.yml:
--------------------------------------------------------------------------------
1 | # DESCRIPTION:
2 | # A workflow used to verify if PR titles matches conventional commits strategy.
3 | # END
4 |
5 | name: Lint PR Title
6 | run-name: "Lint PR - (#${{ github.event.number }}) ${{ github.event.pull_request.title }}"
7 |
8 | permissions:
9 | pull-requests: read
10 |
11 | on:
12 | pull_request_target:
13 | types:
14 | - opened
15 | - edited
16 | - ready_for_review
17 |
18 | jobs:
19 | lint_pr_title:
20 | name: Lint PR
21 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/lint_pr_title.yml@v1.3.0
--------------------------------------------------------------------------------
/modules/lb_http_ext_global/outputs.tf:
--------------------------------------------------------------------------------
1 | output "address" {
2 | value = google_compute_global_address.default.address
3 | }
4 |
5 | output "all" {
6 | description = "Intended mainly for `depends_on` but currently succeeds prematurely (while forwarding rules and healtchecks are not yet usable)."
7 | value = {
8 | google_compute_global_forwarding_rule_http = google_compute_global_forwarding_rule.http
9 | google_compute_global_forwarding_rule_https = google_compute_global_forwarding_rule.https
10 | google_compute_health_check = google_compute_health_check.default
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/modules/vmseries/outputs.tf:
--------------------------------------------------------------------------------
1 | output "instance" {
2 | value = google_compute_instance.this
3 | }
4 |
5 | output "self_link" {
6 | value = google_compute_instance.this.self_link
7 | }
8 |
9 | output "instance_group" {
10 | value = try(google_compute_instance_group.this[0], null)
11 | }
12 |
13 | output "instance_group_self_link" {
14 | value = try(google_compute_instance_group.this[0].self_link, null)
15 | }
16 |
17 | output "private_ips" {
18 | value = { for k, v in google_compute_instance.this.network_interface : k => v.network_ip }
19 | }
20 |
21 | output "public_ips" {
22 | value = { for k, v in google_compute_instance.this.network_interface : k => v.access_config[0].nat_ip if length(v.access_config) != 0 }
23 | }
24 |
--------------------------------------------------------------------------------
/.github/workflows/release_ci.yml:
--------------------------------------------------------------------------------
1 | name: Release CI
2 | run-name: "Continous Release"
3 |
4 |
5 | permissions:
6 | contents: write
7 | issues: read
8 | id-token: write
9 |
10 | on:
11 | workflow_dispatch:
12 | schedule:
13 | - cron: '0 1 * * 4' # this means every Thursday @1am UTC
14 |
15 | concurrency: release
16 |
17 | jobs:
18 | release_wrkflw:
19 | name: Do release
20 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/release_ci.yml@v2.2
21 | secrets: inherit
22 | with:
23 | cloud: gcp
24 | validate_max_parallel: 20
25 | test_max_parallel: 5
26 | fail_fast: false
27 | terratest_action: Idempotence # keep in mind that this has to start with capital letter
--------------------------------------------------------------------------------
/modules/lb_http_ext_global/howto_lb_region.md:
--------------------------------------------------------------------------------
1 | # How to develop lb_http_ext_region
2 |
3 | Use replacement symbols for about everything:
4 |
5 | ```
6 | google_compute_forwarding_rule
7 | google_compute_region_backend_service
8 | google_compute_region_target_http_proxy
9 | google_compute_region_target_https_proxy
10 | google_compute_region_ssl_certificate
11 | google_compute_region_url_map
12 | google_compute_region_health_check
13 | ```
14 |
15 | Some attributes differ, e.g. for the `google_compute_region_backend_service` add:
16 | ```
17 | backend {
18 | failover = lookup(backend.value, "failover", false)
19 | }
20 | ```
21 |
22 | and remove:
23 |
24 | ```
25 | security_policy = var.security_policy
26 | enable_cdn = var.cdn
27 | ```
28 |
--------------------------------------------------------------------------------
/modules/vpn/outputs.tf:
--------------------------------------------------------------------------------
1 | output "vpn_gw_name" {
2 | value = google_compute_ha_vpn_gateway.ha_gateway.name
3 | description = "HA VPN gateway name"
4 | }
5 |
6 | output "vpn_gw_self_link" {
7 | value = google_compute_ha_vpn_gateway.ha_gateway.self_link
8 | description = "HA VPN gateway self_link"
9 | }
10 |
11 | output "vpn_gw_local_address_1" {
12 | value = google_compute_ha_vpn_gateway.ha_gateway.vpn_interfaces[0].ip_address
13 | description = "HA VPN gateway IP address 1"
14 | }
15 |
16 | output "vpn_gw_local_address_2" {
17 | value = google_compute_ha_vpn_gateway.ha_gateway.vpn_interfaces[1].ip_address
18 | description = "HA VPN gateway IP address 2"
19 | }
20 |
21 | output "random_secret" {
22 | value = local.secret
23 | sensitive = true
24 | description = "HA VPN IPsec tunnels secret that has been randomly generated"
25 | }
--------------------------------------------------------------------------------
/.github/workflows/pr_ci.yml:
--------------------------------------------------------------------------------
1 | name: PR CI
2 | run-name: "CI pipeline for PR - (#${{ github.event.number }}) ${{ github.event.pull_request.title }}"
3 |
4 | permissions:
5 | contents: read
6 | actions: read
7 | id-token: write
8 |
9 | on:
10 | pull_request:
11 | types:
12 | - opened
13 | - reopened
14 | - synchronize
15 | - ready_for_review
16 | branches: ['main']
17 |
18 | jobs:
19 | pr_ci_wrkflw:
20 | name: Run CI
21 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/pr_ci.yml@v2.2
22 | if: github.actor != 'dependabot[bot]'
23 | secrets: inherit
24 | with:
25 | cloud: gcp
26 | tf_version: 1.3 1.4 1.5
27 | validate_max_parallel: 20
28 | test_max_parallel: 10
29 | fail_fast: false
30 | terratest_action: Plan # keep in mind that this has to start with capital letter
--------------------------------------------------------------------------------
/.github/workflows/hub_sync.yml:
--------------------------------------------------------------------------------
1 | name: Orchestrator Hub Sync System Workflow
2 |
3 | permissions:
4 | contents: read
5 |
6 | on:
7 | workflow_dispatch:
8 | release:
9 | types: [released]
10 |
11 | jobs:
12 | hub_sync:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Generate GitHub token
16 | id: generate-token
17 | uses: tibdex/github-app-token@v1
18 | with:
19 | app_id: ${{ secrets.APP_ID }}
20 | private_key: ${{ secrets.APP_PRIVATE_KEY }}
21 | installation_id: ${{ secrets.APP_INSTALLATION_ID }}
22 |
23 | - name: Trigger Hub Sync Workflow
24 | uses: benc-uk/workflow-dispatch@v1
25 | with:
26 | workflow: run.yml
27 | repo: PaloAltoNetworks/automation-metadata-collector
28 | ref: main
29 | token: ${{ steps.generate-token.outputs.token }}
30 | inputs: '{"cloud-id": "gcp"}'
31 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .phony: all invalidate help
2 |
3 | all:
4 | @echo "Run [make help] for usage details."
5 |
6 | invalidate:
7 |
8 | help:
9 | @echo "This Makefile is run by specifying a module path as a target name." ; \
10 | echo "It takes one argument: ACTION. Value of this argument is specific to a particular module." ; \
11 | echo "It represents the name of a Terratest test function." ; \
12 | echo "Typically this will be: Validate, Plan, Apply, Idempotence, but it should be verified with" ; \
13 | echo " module's main_test.go file." ; \
14 | echo ; \
15 | echo "Example:" ; \
16 | echo " make examples/common_vmseries ACTION=Plan" ; \
17 | echo
18 |
19 | %: invalidate %/main.tf
20 | @cd $@ && \
21 | echo "::group::DOWNLOADING GO DEPENDENCIES" && \
22 | go get -v -t -d && \
23 | go mod tidy && \
24 | echo "::endgroup::" && \
25 | echo "::group::ACTION >>$(ACTION)<<" && \
26 | go test -run $(ACTION) -timeout 60m -count=1 && \
27 | echo "::endgroup::"
--------------------------------------------------------------------------------
/examples/vmseries_ha/outputs.tf:
--------------------------------------------------------------------------------
1 | output "vmseries_private_ips" {
2 | description = "Private IP addresses of the vmseries instances."
3 | value = { for k, v in module.vmseries : k => v.private_ips }
4 | }
5 |
6 | output "vmseries_public_ips" {
7 | description = "Public IP addresses of the vmseries instances."
8 | value = { for k, v in module.vmseries : k => v.public_ips }
9 | }
10 |
11 | output "lbs_internal_ips" {
12 | description = "Private IP addresses of internal network loadbalancers."
13 | value = { for k, v in module.lb_internal : k => v.address }
14 | }
15 |
16 | output "lbs_external_ips" {
17 | description = "Public IP addresses of external network loadbalancers."
18 | value = { for k, v in module.lb_external : k => v.ip_addresses }
19 | }
20 |
21 | output "linux_vm_ips" {
22 | description = "Private IP addresses of Linux VMs."
23 | value = { for k, v in resource.google_compute_instance.linux_vm : k => v.network_interface[0].network_ip }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/multi_nic_common/outputs.tf:
--------------------------------------------------------------------------------
1 | output "vmseries_private_ips" {
2 | description = "Private IP addresses of the vmseries instances."
3 | value = { for k, v in module.vmseries : k => v.private_ips }
4 | }
5 |
6 | output "vmseries_public_ips" {
7 | description = "Public IP addresses of the vmseries instances."
8 | value = { for k, v in module.vmseries : k => v.public_ips }
9 | }
10 |
11 | output "lbs_internal_ips" {
12 | description = "Private IP addresses of internal network loadbalancers."
13 | value = { for k, v in module.lb_internal : k => v.address }
14 | }
15 |
16 | output "lbs_external_ips" {
17 | description = "Public IP addresses of external network loadbalancers."
18 | value = { for k, v in module.lb_external : k => v.ip_addresses }
19 | }
20 |
21 | output "linux_vm_ips" {
22 | description = "Private IP addresses of Linux VMs."
23 | value = { for k, v in resource.google_compute_instance.linux_vm : k => v.network_interface[0].network_ip }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/vpc_peering_dedicated/outputs.tf:
--------------------------------------------------------------------------------
1 | output "vmseries_private_ips" {
2 | description = "Private IP addresses of the vmseries instances."
3 | value = { for k, v in module.vmseries : k => v.private_ips }
4 | }
5 |
6 | output "vmseries_public_ips" {
7 | description = "Public IP addresses of the vmseries instances."
8 | value = { for k, v in module.vmseries : k => v.public_ips }
9 | }
10 |
11 | output "lbs_internal_ips" {
12 | description = "Private IP addresses of internal network loadbalancers."
13 | value = { for k, v in module.lb_internal : k => v.address }
14 | }
15 |
16 | output "lbs_global_http" {
17 | description = "Public IP addresses of external Global HTTP(S) loadbalancers."
18 | value = { for k, v in module.glb : k => v.address }
19 | }
20 |
21 | output "linux_vm_ips" {
22 | description = "Private IP addresses of Linux VMs."
23 | value = { for k, v in resource.google_compute_instance.linux_vm : k => v.network_interface[0].network_ip }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/vpc_peering_common/outputs.tf:
--------------------------------------------------------------------------------
1 | output "vmseries_private_ips" {
2 | description = "Private IP addresses of the vmseries instances."
3 | value = { for k, v in module.vmseries : k => v.private_ips }
4 | }
5 |
6 | output "vmseries_public_ips" {
7 | description = "Public IP addresses of the vmseries instances."
8 | value = { for k, v in module.vmseries : k => v.public_ips }
9 | }
10 |
11 | output "lbs_internal_ips" {
12 | description = "Private IP addresses of internal network loadbalancers."
13 | value = { for k, v in module.lb_internal : k => v.address }
14 | }
15 |
16 | output "lbs_external_ips" {
17 | description = "Public IP addresses of external network loadbalancers."
18 | value = { for k, v in module.lb_external : k => v.ip_addresses }
19 | }
20 |
21 | output "linux_vm_ips" {
22 | description = "Private IP addresses of Linux VMs."
23 | value = { for k, v in resource.google_compute_instance.linux_vm : k => v.network_interface[0].network_ip }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/vpc_peering_common_with_network_tags/outputs.tf:
--------------------------------------------------------------------------------
1 | output "vmseries_private_ips" {
2 | description = "Private IP addresses of the vmseries instances."
3 | value = { for k, v in module.vmseries : k => v.private_ips }
4 | }
5 |
6 | output "vmseries_public_ips" {
7 | description = "Public IP addresses of the vmseries instances."
8 | value = { for k, v in module.vmseries : k => v.public_ips }
9 | }
10 |
11 | output "lbs_internal_ips" {
12 | description = "Private IP addresses of internal network loadbalancers."
13 | value = { for k, v in module.lb_internal : k => v.address }
14 | }
15 |
16 | output "lbs_external_ips" {
17 | description = "Public IP addresses of external network loadbalancers."
18 | value = { for k, v in module.lb_external : k => v.ip_addresses }
19 | }
20 |
21 | output "linux_vm_ips" {
22 | description = "Private IP addresses of Linux VMs."
23 | value = { for k, v in resource.google_compute_instance.linux_vm : k => v.network_interface[0].network_ip }
24 | }
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Community Supported
2 |
3 | The software and templates in the repo are released under an as-is, best effort,
4 | support policy. This software should be seen as community supported and Palo
5 | Alto Networks will contribute our expertise as and when possible. We do not
6 | provide technical support or help in using or troubleshooting the components of
7 | the project through our normal support options such as Palo Alto Networks
8 | support teams, or ASC (Authorized Support Centers) partners and backline support
9 | options. The underlying product used (the VM-Series firewall) by the scripts or
10 | templates are still supported, but the support is only for the product
11 | functionality and not for help in deploying or using the template or script
12 | itself. Unless explicitly tagged, all projects or work posted in our GitHub
13 | repository (at https://github.com/PaloAltoNetworks) or sites other than our
14 | official Downloads page on https://support.paloaltonetworks.com are provided
15 | under the best effort policy.
16 |
17 |
--------------------------------------------------------------------------------
/modules/lb_external/outputs.tf:
--------------------------------------------------------------------------------
1 | output "forwarding_rules" {
2 | description = "The map of created forwarding rules."
3 | value = google_compute_forwarding_rule.rule
4 | }
5 |
6 | output "ip_addresses" {
7 | description = "The map of IP addresses of the forwarding rules."
8 | value = { for k, v in google_compute_forwarding_rule.rule : k => v.ip_address }
9 | }
10 |
11 | output "target_pool" {
12 | description = "The self-link of the target pool."
13 | value = try(google_compute_target_pool.this[0].self_link, null)
14 | }
15 |
16 | output "created_google_compute_http_health_check" {
17 | description = "The created health check resource. Null if `create_health_check` option was false."
18 | value = try(google_compute_http_health_check.this[0], null)
19 | }
20 |
21 | output "created_google_compute_region_health_check" {
22 | description = "The created health check resource. Null if `create_health_check` option was false."
23 | value = try(google_compute_region_health_check.this[0], null)
24 | }
25 |
--------------------------------------------------------------------------------
/examples/vpc_peering_common_with_autoscale/outputs.tf:
--------------------------------------------------------------------------------
1 | output "pubsub_topic_id" {
2 | description = "The resource ID of the Pub/Sub Topic."
3 | value = try({ for k, v in module.autoscale : k => v.pubsub_topic_id }, null)
4 | }
5 |
6 | output "pubsub_subscription_id" {
7 | description = "The resource ID of the Pub/Sub Subscription."
8 | value = try({ for k, v in module.autoscale : k => v.pubsub_subscription_id }, null)
9 | }
10 |
11 | output "lbs_internal_ips" {
12 | description = "Private IP addresses of internal network loadbalancers."
13 | value = { for k, v in module.lb_internal : k => v.address }
14 | }
15 |
16 | output "lbs_external_ips" {
17 | description = "Public IP addresses of external network loadbalancers."
18 | value = { for k, v in module.lb_external : k => v.ip_addresses }
19 | }
20 |
21 | output "linux_vm_ips" {
22 | description = "Private IP addresses of Linux VMs."
23 | value = { for k, v in resource.google_compute_instance.linux_vm : k => v.network_interface[0].network_ip }
24 | }
--------------------------------------------------------------------------------
/examples/vpc_peering_dedicated_with_autoscale/outputs.tf:
--------------------------------------------------------------------------------
1 | output "pubsub_topic_id" {
2 | description = "The resource ID of the Pub/Sub Topic."
3 | value = try({ for k, v in module.autoscale : k => v.pubsub_topic_id }, null)
4 | }
5 |
6 | output "pubsub_subscription_id" {
7 | description = "The resource ID of the Pub/Sub Subscription."
8 | value = try({ for k, v in module.autoscale : k => v.pubsub_subscription_id }, null)
9 | }
10 |
11 | output "lbs_internal_ips" {
12 | description = "Private IP addresses of internal network loadbalancers."
13 | value = { for k, v in module.lb_internal : k => v.address }
14 | }
15 |
16 | output "lbs_external_ips" {
17 | description = "Public IP addresses of external network loadbalancers."
18 | value = { for k, v in module.lb_external : k => v.ip_addresses }
19 | }
20 |
21 | output "linux_vm_ips" {
22 | description = "Private IP addresses of Linux VMs."
23 | value = { for k, v in resource.google_compute_instance.linux_vm : k => v.network_interface[0].network_ip }
24 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Palo Alto Networks, inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/modules/autoscale/outputs.tf:
--------------------------------------------------------------------------------
1 | output "zonal_instance_group_ids" {
2 | description = "The resource IDs of the zonal VM-Series managed instance groups. This output should only be used when `regional_mig` is set to `false`."
3 | value = var.regional_mig ? null : { for k, v in google_compute_instance_group_manager.zonal : k => v.instance_group }
4 | }
5 |
6 | output "regional_instance_group_id" {
7 | description = "The resource ID of the regional VM-Series managed instance group. This output should only be used when `regional_mig` is set to `true`."
8 | value = var.regional_mig ? google_compute_region_instance_group_manager.regional[0].instance_group : null
9 | }
10 |
11 | output "pubsub_topic_id" {
12 | description = "The resource ID of the Pub/Sub Topic."
13 | value = var.create_pubsub_topic ? google_pubsub_topic.main[0].id : null
14 | }
15 |
16 | output "pubsub_subscription_id" {
17 | description = "The resource ID of the Pub/Sub Subscription."
18 | value = var.create_pubsub_topic ? google_pubsub_subscription.main[0].id : null
19 | }
20 |
21 | output "pubsub_subscription_iam_member_etag" {
22 | description = "The etag of the Pub/Sub IAM Member."
23 | value = var.create_pubsub_topic ? google_pubsub_subscription_iam_member.main[0].etag : null
24 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This is a minimal list, OK to add things per repo
2 | # mac specific
3 | .DS_Store
4 | .ansible
5 | .azure/
6 | .bash_history
7 | # don't check storage creds into GH
8 | .boto
9 | .cache
10 | *.code-workspace
11 | # may contain gcloud files
12 | .config
13 | .gitconfig
14 | .local
15 | # .netrc contains secrets for service tokens
16 | .netrc
17 | *.plan
18 | .sentinel
19 | # .ssh dir may contain private keys
20 | .ssh
21 | .terrascan
22 | # Terraform dot files
23 | .terraform
24 | .terraform.lock.hcl
25 | .terraformrc
26 | **/.terraform/*
27 | .terraform.d
28 | *.tfstate
29 | *.tfstate.*
30 | .vscode
31 | .idea
32 | *.tfsec
33 | # Terraform crash log files
34 | crash.*.log
35 |
36 | # Terraform overrides
37 | override.tf
38 | override.tf.json
39 | *_override.tf
40 | *_override.tf.json
41 |
42 | # Palo auth codes
43 | authcodes
44 | # Crash log files
45 | crash.log
46 | credentials.json
47 | # Palo specific
48 | init-cfg.txt
49 | temp
50 | terraform.rc
51 | tmp
52 | # Auto variables
53 | terraform.tfvars
54 | terraform.tfvars.json
55 | *.auto.tfvars
56 | *.auto.tfvars.json
57 |
58 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as
59 | # password, private keys, and other secrets. But allow example files.
60 | *.tfvars
61 | *.tfvars.json
62 | !**/example.tfvars
63 |
--------------------------------------------------------------------------------
/modules/vpc-peering/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | local_network_name = reverse(split("/", var.local_network))[0]
3 | peer_network_name = reverse(split("/", var.peer_network))[0]
4 | }
5 |
6 | resource "google_compute_network_peering" "local" {
7 | name = coalesce(var.local_peering_name, "${var.name_prefix}${local.local_network_name}-${local.peer_network_name}")
8 | network = var.local_network
9 | peer_network = var.peer_network
10 |
11 | export_custom_routes = var.local_export_custom_routes
12 | import_custom_routes = var.local_import_custom_routes
13 |
14 | export_subnet_routes_with_public_ip = var.local_export_subnet_routes_with_public_ip
15 | import_subnet_routes_with_public_ip = var.local_import_subnet_routes_with_public_ip
16 | }
17 |
18 | resource "google_compute_network_peering" "peer" {
19 | name = coalesce(var.peer_peering_name, "${var.name_prefix}${local.peer_network_name}-${local.local_network_name}")
20 | network = var.peer_network
21 | peer_network = var.local_network
22 |
23 | export_custom_routes = var.peer_export_custom_routes
24 | import_custom_routes = var.peer_import_custom_routes
25 |
26 | export_subnet_routes_with_public_ip = var.peer_export_subnet_routes_with_public_ip
27 | import_subnet_routes_with_public_ip = var.peer_import_subnet_routes_with_public_ip
28 | }
--------------------------------------------------------------------------------
/modules/iam_service_account/variables.tf:
--------------------------------------------------------------------------------
1 | variable "service_account_id" {
2 | default = "The google_service_account.account_id of the created IAM account, unique string per project."
3 | type = string
4 | }
5 |
6 | variable "display_name" {
7 | default = "Palo Alto Networks Firewall Service Account"
8 | }
9 |
10 | variable "roles" {
11 | description = "List of IAM role names, such as [\"roles/compute.viewer\"] or [\"project/A/roles/B\"]. The default list is suitable for Palo Alto Networks Firewall to run and publish custom metrics to GCP Stackdriver."
12 | default = [
13 | "roles/compute.networkViewer",
14 | "roles/logging.logWriter",
15 | "roles/monitoring.metricWriter",
16 | "roles/monitoring.viewer",
17 | "roles/viewer", # to reach a bootstrap bucket (project's storage.buckets.list with bucket's roles/storage.objectViewer insufficient)
18 | # per https://docs.paloaltonetworks.com/vm-series/9-1/vm-series-deployment/set-up-the-vm-series-firewall-on-google-cloud-platform/deploy-vm-series-on-gcp/enable-google-stackdriver-monitoring-on-the-vm-series-firewall.html
19 | "roles/stackdriver.accounts.viewer",
20 | "roles/stackdriver.resourceMetadata.writer",
21 | ]
22 | type = set(string)
23 | }
24 |
25 | variable "project_id" {
26 | description = "ID of a project in which the service account will be created."
27 | type = string
28 | }
--------------------------------------------------------------------------------
/.github/workflows/pre-commit-update.yml:
--------------------------------------------------------------------------------
1 | name: Pre-Commit update
2 | run-name: "Update Pre-Commit dependencies"
3 |
4 | permissions:
5 | contents: write
6 | pull-requests: write
7 |
8 | on:
9 | workflow_dispatch:
10 | schedule:
11 | - cron: 0 1 1 * * # 1am of every 1st day of every month
12 |
13 | jobs:
14 | update:
15 | name: "Update Pre-Commit dependencies"
16 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/_pre-commit-update.yml@v2.3
17 |
18 | pre-commit:
19 | name: Run Pre-Commit with the udpated config
20 | needs: [update]
21 | if: needs.update.outputs.pr_operation == 'created' || needs.update.outputs.pr_operation == 'updated'
22 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/_pre_commit.yml@v2.3
23 | with:
24 | pre-commit-hooks: terraform_fmt terraform_docs terraform_tflint checkov
25 | branch: pre-commit-dependencies-update
26 |
27 | comment-pr:
28 | name: Give comment on the PR if pre-commit failed
29 | needs: [pre-commit, update]
30 | if: always() && (needs.pre-commit.result == 'failure' || needs.pre-commit.result == 'success')
31 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/_comment_pr.yml@v2.3
32 | with:
33 | pr_number: ${{ needs.update.outputs.pr_number }}
34 | job_result: ${{ needs.pre-commit.result }}
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature request
2 | description: Suggest an idea for this project
3 | # title: '[Enhancement]
'
4 | labels: enhancement
5 | assignees: aws-vmseries-modules-codeowners
6 | body:
7 | - type: textarea
8 | attributes:
9 | label: Is your feature request related to a problem?
10 | description: A clear and concise description of what the problem is.
11 | placeholder: eg. I'm always frustrated when [...]
12 | validations:
13 | required: true
14 | - type: textarea
15 | attributes:
16 | label: Describe the solution you'd like
17 | description: A clear and concise description of what you want to happen.
18 | placeholder: How should it work?
19 | validations:
20 | required: true
21 | - type: textarea
22 | attributes:
23 | label: Describe alternatives you've considered.
24 | description: A clear and concise description of any alternative solutions, features, or workarounds you've considered.
25 | validations:
26 | required: false
27 | - type: textarea
28 | attributes:
29 | label: Additional context
30 | description: How has this issue affected you? What are you trying to accomplish?
31 | placeholder: Providing context helps us come up with a solution that is useful in the real world. Drag any screenshot here to help illustrate
32 | validations:
33 | required: false
34 |
--------------------------------------------------------------------------------
/examples/panorama_standalone/example.tfvars:
--------------------------------------------------------------------------------
1 | # General
2 | project = ""
3 | region = "us-central1"
4 | name_prefix = ""
5 |
6 | # VPC
7 |
8 | networks = {
9 | "panorama-vpc" = {
10 | vpc_name = "firewall-vpc"
11 | create_network = true
12 | delete_default_routes_on_create = "false"
13 | mtu = "1460"
14 | routing_mode = "REGIONAL"
15 | subnetworks = {
16 | "panorama-sub" = {
17 | name = "panorama-subnet"
18 | create_subnetwork = true
19 | ip_cidr_range = "172.21.21.0/24"
20 | region = "us-central1"
21 | }
22 | }
23 | firewall_rules = {
24 | "allow-panorama-ingress" = {
25 | name = "panorama-mgmt"
26 | source_ranges = ["1.1.1.1/32", "2.2.2.2/32"]
27 | priority = "1000"
28 | allowed_protocol = "all"
29 | allowed_ports = []
30 | }
31 | }
32 | }
33 | }
34 |
35 | # Panorama
36 |
37 | panoramas = {
38 | "panorama-01" = {
39 | zone = "us-central1-a"
40 | panorama_name = "panorama-01"
41 | vpc_network_key = "panorama-vpc"
42 | subnetwork_key = "panorama-sub"
43 | panorama_version = "panorama-byol-1000"
44 | ssh_keys = "admin:"
45 | attach_public_ip = true
46 | private_static_ip = "172.21.21.2"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/examples/panorama_standalone/main.tf:
--------------------------------------------------------------------------------
1 | module "vpc" {
2 | source = "../../modules/vpc"
3 |
4 | for_each = var.networks
5 |
6 | project_id = var.project
7 | name = "${var.name_prefix}${each.value.vpc_name}"
8 | create_network = each.value.create_network
9 | delete_default_routes_on_create = each.value.delete_default_routes_on_create
10 | mtu = each.value.mtu
11 | routing_mode = each.value.routing_mode
12 | subnetworks = { for k, v in each.value.subnetworks : k => merge(v, {
13 | name = "${var.name_prefix}${v.name}"
14 | })
15 | }
16 | firewall_rules = try({ for k, v in each.value.firewall_rules : k => merge(v, {
17 | name = "${var.name_prefix}${v.name}"
18 | })
19 | }, {})
20 | }
21 |
22 | module "panorama" {
23 | source = "../../modules/panorama"
24 |
25 | for_each = var.panoramas
26 |
27 | name = "${var.name_prefix}${each.value.panorama_name}"
28 | project = var.project
29 | region = var.region
30 | zone = each.value.zone
31 | panorama_version = each.value.panorama_version
32 | ssh_keys = each.value.ssh_keys
33 | subnet = module.vpc[each.value.vpc_network_key].subnetworks[each.value.subnetwork_key].self_link
34 | private_static_ip = each.value.private_static_ip
35 | attach_public_ip = each.value.attach_public_ip
36 | log_disks = try(each.value.log_disks, [])
37 | depends_on = [module.vpc]
38 | }
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/antonbabenko/pre-commit-terraform
3 | rev: v1.68.1 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases
4 | hooks:
5 | - id: terraform_fmt
6 | - id: terraform_docs
7 | args: ['--args=--lockfile=false', '--args=--indent=3']
8 | - id: terraform_tflint
9 | args: [
10 | # --args=--module, # TODO enable it after ensuring `terraform init`
11 | # --args=--only=terraform_comment_syntax,
12 | --args=--only=terraform_deprecated_interpolation,
13 | --args=--only=terraform_deprecated_index,
14 | # --args=--only=terraform_documented_variables,
15 | --args=--only=terraform_module_pinned_source,
16 | --args=--only=terraform_naming_convention,
17 | # --args=--only=terraform_required_providers,
18 | # --args=--only=terraform_required_version,
19 | # --args=--only=terraform_unused_declarations,
20 | --args=--only=terraform_workspace_remote,
21 | ]
22 | - repo: https://github.com/bridgecrewio/checkov.git
23 | rev: '2.2.125'
24 | hooks:
25 | - id: checkov
26 | verbose: true
27 | args: [
28 | --compact,
29 | --quiet,
30 | --skip-check, "CKV_GCP_26,CKV_GCP_32,CKV_GCP_35,CKV_GCP_36,CKV_GCP_39,CKV_GCP_40,CKV_GCP_76,CKV_GCP_62,CKV_GCP_37,CKV_GCP_38,CKV_GCP_74,CKV_GCP_83,CKV2_GHA_1,CKV_SECRET_6",
31 | --soft-fail-on, "CKV_GCP_37,CKV_GCP_38,CKV_GCP_74,CKV_GCP_83,CKV2_GHA_1",
32 | ]
33 |
--------------------------------------------------------------------------------
/.github/actions/terratest/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Terratest'
2 | description: 'Runs Terratest for a specified path.'
3 | inputs:
4 | tf_version:
5 | description: 'TF version used.'
6 | required: true
7 | path:
8 | description: 'Path to Terraform module.'
9 | required: true
10 | terratest_action:
11 | description: The action (name of a test in Terratest) that will be passed to the Makefile's ACTION parameter
12 | type: string
13 | required: true
14 | pr-id:
15 | description: A PR number. Optional value, you might want to use it to prefix resources created for a particular PR to identify them easily.
16 | type: string
17 | default: ""
18 | required: false
19 |
20 | runs:
21 | using: "composite"
22 | steps:
23 |
24 | - name: setup Terraform
25 | uses: hashicorp/setup-terraform@v2
26 | with:
27 | terraform_version: ${{ inputs.tf_version }}
28 | terraform_wrapper: false
29 |
30 | - name: setup Go
31 | uses: actions/setup-go@v4
32 | with:
33 | go-version: '1.20'
34 |
35 | - name: login to GCP
36 | uses: google-github-actions/auth@v1
37 | with:
38 | workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }}
39 | service_account: ${{ env.GCP_SERVICE_ACCOUNT}}
40 |
41 | - name: ${{ inputs.terratest_action }} infrastructure
42 | env:
43 | TPATH: ${{ inputs.path }}
44 | ACTION: ${{ inputs.terratest_action }}
45 | PRID: ${{ inputs.pr-id }}
46 | PROJECT_ID: ${{ env.PROJECT_ID }}
47 | shell: bash
48 | run: make $TPATH ACTION=$ACTION
--------------------------------------------------------------------------------
/.github/workflows/plan-command.yml:
--------------------------------------------------------------------------------
1 | name: ChatOPS Plan
2 | run-name: "On demand Plan test for PR - (#${{ github.event.inputs.pr-id }}) ${{ github.event.inputs.pr-title }}"
3 |
4 | permissions:
5 | contents: read
6 |
7 | concurrency: chatops-plan
8 |
9 | on:
10 | workflow_dispatch:
11 | inputs:
12 | paths:
13 | description: Space delimited list of module paths to test
14 | type: string
15 | required: true
16 | tf_version:
17 | description: Terraform versions to use for tests, comma-separated list
18 | type: string
19 | pr-id:
20 | description: ID of the PR that triggered this workflow
21 | type: string
22 | required: true
23 | pr-title:
24 | description: Title of the PR that triggered this workflow
25 | type: string
26 | required: true
27 | comment-id:
28 | description: 'The comment-id of the slash command'
29 | type: string
30 | required: true
31 | branch:
32 | description: Branch on which the tests should run
33 | type: string
34 | default: main
35 |
36 | jobs:
37 | test:
38 | name: Run plan test
39 | permissions:
40 | contents: read
41 | pull-requests: write
42 | id-token: write
43 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/test_command.yml@v2.3
44 | secrets: inherit
45 | with:
46 | cloud: azure
47 | paths: ${{ inputs.paths }}
48 | tf_version: ${{ inputs.tf_version }}
49 | pr-id: ${{ inputs.pr-id }}
50 | comment-id: ${{ inputs.comment-id }}
51 | branch: ${{ inputs.branch }}
52 | terratest_action: Plan
--------------------------------------------------------------------------------
/.releaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "branches": [
3 | "main"
4 | ],
5 | "plugins": [
6 | [
7 | "@semantic-release/commit-analyzer",
8 | {
9 | "releaseRules": [
10 | {
11 | "breaking": true,
12 | "release": "minor"
13 | },
14 | {
15 | "type": "feat",
16 | "release": "patch"
17 | },
18 | {
19 | "type": "feat",
20 | "scope" : "MAJOR",
21 | "release": "major"
22 | }
23 | ]
24 | }
25 | ],
26 | "@semantic-release/release-notes-generator",
27 | [
28 | "@semantic-release/git",
29 | {
30 | "assets": [
31 | "README.md"
32 | ],
33 | "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
34 | }
35 | ],
36 | [
37 | "@semantic-release/github",
38 | {
39 | "successComment": ":tada: This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:\n\nThe release is available on [Terraform Registry](https://registry.terraform.io/modules/PaloAltoNetworks/vmseries-modules/google/latest) and [GitHub release](../releases/tag/v${nextRelease.version})\n\n> Posted by [semantic-release](https://github.com/semantic-release/semantic-release) bot"
40 | }
41 | ]
42 | ],
43 | "preset": "conventionalcommits"
44 | }
45 |
--------------------------------------------------------------------------------
/.github/workflows/apply-command.yml:
--------------------------------------------------------------------------------
1 | name: ChatOPS Apply
2 | run-name: "On demand Apply test for PR - (#${{ github.event.inputs.pr-id }}) ${{ github.event.inputs.pr-title }}"
3 |
4 | permissions:
5 | contents: read
6 |
7 | concurrency: chatops-apply
8 |
9 | on:
10 | workflow_dispatch:
11 | inputs:
12 | paths:
13 | description: Space delimited list of module paths to test
14 | type: string
15 | required: true
16 | tf_version:
17 | description: Terraform versions to use for tests, comma-separated list
18 | type: string
19 | pr-id:
20 | description: ID of the PR that triggered this workflow
21 | type: string
22 | required: true
23 | pr-title:
24 | description: Title of the PR that triggered this workflow
25 | type: string
26 | required: true
27 | comment-id:
28 | description: 'The comment-id of the slash command'
29 | type: string
30 | required: true
31 | branch:
32 | description: Branch on which the tests should run
33 | type: string
34 | default: main
35 |
36 | jobs:
37 | test:
38 | name: Run apply test
39 | permissions:
40 | contents: read
41 | pull-requests: write
42 | id-token: write
43 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/test_command.yml@v2.3
44 | secrets: inherit
45 | with:
46 | cloud: azure
47 | paths: ${{ inputs.paths }}
48 | tf_version: ${{ inputs.tf_version }}
49 | pr-id: ${{ inputs.pr-id }}
50 | comment-id: ${{ inputs.comment-id }}
51 | branch: ${{ inputs.branch }}
52 | terratest_action: Apply
53 | apply_timeout: 60
--------------------------------------------------------------------------------
/.github/workflows/idempotence-command.yml:
--------------------------------------------------------------------------------
1 | name: ChatOPS Idempotence
2 | run-name: "On demand Idempotence test for PR - (#${{ github.event.inputs.pr-id }}) ${{ github.event.inputs.pr-title }}"
3 |
4 | permissions:
5 | contents: read
6 |
7 | concurrency: chatops-apply
8 |
9 | on:
10 | workflow_dispatch:
11 | inputs:
12 | paths:
13 | description: Space delimited list of module paths to test
14 | type: string
15 | required: true
16 | tf_version:
17 | description: Terraform versions to use for tests, comma-separated list
18 | type: string
19 | pr-id:
20 | description: ID of the PR that triggered this workflow
21 | type: string
22 | required: true
23 | pr-title:
24 | description: Title of the PR that triggered this workflow
25 | type: string
26 | required: true
27 | comment-id:
28 | description: 'The comment-id of the slash command'
29 | type: string
30 | required: true
31 | branch:
32 | description: Branch on which the tests should run
33 | type: string
34 | default: main
35 |
36 | jobs:
37 | test:
38 | name: Run idempotence test
39 | permissions:
40 | contents: read
41 | pull-requests: write
42 | id-token: write
43 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/test_command.yml@v2.3
44 | secrets: inherit
45 | with:
46 | cloud: azure
47 | paths: ${{ inputs.paths }}
48 | tf_version: ${{ inputs.tf_version }}
49 | pr-id: ${{ inputs.pr-id }}
50 | comment-id: ${{ inputs.comment-id }}
51 | branch: ${{ inputs.branch }}
52 | terratest_action: Idempotence
53 | apply_timeout: 60
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: Create an issue to help us improve
3 | title: '[Bug Report] '
4 | assignees: aws-vmseries-modules-codeowners
5 | body:
6 | - type: textarea
7 | attributes:
8 | label: Describe the bug
9 | description: A clear and concise description of what is wrong and steps to reproduce.
10 | validations:
11 | required: true
12 | - type: input
13 | attributes:
14 | label: Module Version
15 | description: What is the module version in use (https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/releases)? Please include the commit hash if you're using an unreleased version.
16 | placeholder: eg. v0.4.1
17 | validations:
18 | required: true
19 | - type: input
20 | attributes:
21 | label: Terraform version
22 | description: What is the Terraform version in use?
23 | placeholder: Execute terraform -version
24 | validations:
25 | required: false
26 | - type: textarea
27 | attributes:
28 | label: Expected behavior
29 | description: Tell us what should happen or how it should work.
30 | validations:
31 | required: false
32 | - type: textarea
33 | attributes:
34 | label: Current behavior
35 | description: Tell us what happens instead of the expected behavior.
36 | validations:
37 | required: false
38 | - type: textarea
39 | attributes:
40 | label: Anything else to add?
41 | description: If you would like to add any more information, please add it below.
42 | placeholder: |
43 | Drag any screenshot here to help illustrate, other relevant details about the environment you experienced the bug in, terraform debug logs or relevant outputs.
44 |
--------------------------------------------------------------------------------
/.github/workflows/validate-command.yml:
--------------------------------------------------------------------------------
1 | name: ChatOPS Validate
2 | run-name: "On demand Validate test for PR - (#${{ github.event.inputs.pr-id }}) ${{ github.event.inputs.pr-title }}"
3 |
4 | permissions:
5 | contents: read
6 |
7 | on:
8 | workflow_dispatch:
9 | inputs:
10 | paths:
11 | description: Space delimited list of module paths to test
12 | type: string
13 | required: true
14 | tf_version:
15 | description: Terraform versions to use for tests, comma-separated list
16 | type: string
17 | pr-id:
18 | description: ID of the PR that triggered this workflow
19 | type: string
20 | required: true
21 | pr-title:
22 | description: Title of the PR that triggered this workflow
23 | type: string
24 | required: true
25 | comment-id:
26 | description: 'The comment-id of the slash command'
27 | type: string
28 | required: true
29 | branch:
30 | description: Branch on which the tests should run
31 | type: string
32 | default: main
33 |
34 | jobs:
35 | test:
36 | name: Run validate test
37 | concurrency:
38 | group: chatops-validate-${{ github.event.issue.number }}
39 | cancel-in-progress: true
40 | permissions:
41 | contents: read
42 | pull-requests: write
43 | id-token: write
44 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/test_command.yml@v2.3
45 | secrets: inherit
46 | with:
47 | cloud: azure
48 | paths: ${{ inputs.paths }}
49 | tf_version: ${{ inputs.tf_version }}
50 | pr-id: ${{ inputs.pr-id }}
51 | comment-id: ${{ inputs.comment-id }}
52 | branch: ${{ inputs.branch }}
53 | terratest_action: Validate
--------------------------------------------------------------------------------
/scripts/install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/bash
2 |
3 | # install.sh - prepare the dependencies for the run.sh
4 | #
5 | # It only handles installing from scratch and will probably fail on a subsequent run.
6 | # It overuses the &&, &, and backslash line continuation so it could be easily converted
7 | # into a Dockerfile, just by adding `RUN` directives (and `COPY requirements.txt .`).
8 |
9 | set -euo pipefail
10 |
11 | cd "$(dirname $0)"
12 |
13 | curl -sL https://github.com/terraform-docs/terraform-docs/releases/download/v0.16.0/terraform-docs-v0.16.0-linux-amd64.tar.gz > terraform-docs.tar.gz & \
14 | curl -sL https://github.com/tfsec/tfsec/releases/download/v0.34.0/tfsec-linux-amd64 > tfsec & \
15 | curl -sL https://github.com/terraform-linters/tflint/releases/download/v0.29.0/tflint_linux_amd64.zip > tflint.zip & \
16 | wait
17 | echo Finished successfully all parallel downloads ------------------------------------------------------------------
18 |
19 | tar zxf terraform-docs.tar.gz
20 | rm terraform-docs.tar.gz
21 | mv terraform-docs /usr/local/bin/
22 |
23 | chmod +x tfsec
24 | mv tfsec /usr/local/bin/
25 |
26 | unzip tflint.zip
27 | rm tflint.zip
28 | mv tflint /usr/local/bin/
29 |
30 | git --version
31 | terraform-docs --version
32 | tfsec --version
33 | tflint --version
34 | terraform version
35 |
36 | echo "Also, the newest release: $(curl -s https://api.github.com/repos/terraform-docs/terraform-docs/releases/latest | grep -o -E "https://.+?-linux-amd64")"
37 | echo "Also, the newest release: $(curl -s https://api.github.com/repos/tfsec/tfsec/releases/latest | grep -o -E "https://.+?tfsec-linux-amd64")"
38 | echo "Also, the newest release: $(curl -s https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")"
39 |
40 | python3 -m pip install -r requirements.txt
41 |
--------------------------------------------------------------------------------
/scripts/decisions.md:
--------------------------------------------------------------------------------
1 | # Architecture Decision Record (ADR) For Tests and CI/CD
2 |
3 | ## macOS and Linux
4 |
5 | - Decision: Tests compatible with macOS and Linux, including WSL1 and WSL2.
6 | - Reason: Those are the main systems of our users and our developers.
7 |
8 | ## Microsoft Windows (non-WSL)
9 |
10 | - Decision: Maintain open road for test compatibility with Microsoft Windows (non-WSL).
11 | - Reason: predictable customer requirements.
12 |
13 | ## GitHub Actions for CI/CD
14 |
15 | - Decision: Use GitHub Actions for CI/CD.
16 | - Reason: The code was placed on GitHub. Therefore it's just the easiest CI/CD runner to implement.
17 |
18 | ## CLI Commands
19 |
20 | - Decision: Have a command that is runnable from a laptop that runs all the tests. Run exactly the same command in CI/CD.
21 | - Reason: Short feedback loop when developing. It takes less time to run locally the same actions that CI/CD normally does. Time is saved on git-committing, git-pushing, preparing the fresh runner containers.
22 | - Reason: Less vendor lock-in with GitHub Actions (or any other CI/CD runner).
23 |
24 | ## Python
25 |
26 | - Decision: Use Python 3.6 script as the command for the main test.
27 | - Alternative was: Use bash script as the command for the main test.
28 | - Reason: Less pitfalls, cleaner syntax.
29 | - Reason: Predicted MS Windows compatibility.
30 | - Cost: Need to install Python and set it up.
31 |
32 | ## Test the examples
33 |
34 | - Decision: Test the examples to the maximum extent possible, including `terraform apply` on a real cloud.
35 | - Reason: Examples are by far the main contact point of any developer
36 | who starts work on the modules. Typically a developer tries to copy
37 | a relevant example, do minimal customizations and it would be best if they could simply succeed. A successful first run enables to enter a feedback loop and become productive on the main project.
38 |
--------------------------------------------------------------------------------
/modules/bootstrap/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name_prefix" {
2 | description = "Prefix of the name of Google Cloud Storage bucket, followed by 10 random characters"
3 | default = "paloaltonetworks-firewall-bootstrap-"
4 | type = string
5 | }
6 |
7 | variable "files" {
8 | description = "Map of all files to copy to bucket. The keys are local paths, the values are remote paths. For example `{\"dir/my.txt\" = \"config/init-cfg.txt\"}`"
9 | default = {}
10 | type = map(string)
11 | }
12 |
13 | variable "service_account" {
14 | description = "Optional IAM Service Account (just an email) that will be granted read-only access to this bucket"
15 | default = null
16 | type = string
17 | }
18 |
19 | variable "location" {
20 | description = "Location in which the GCS Bucket will be deployed. Available locations can be found under https://cloud.google.com/storage/docs/locations."
21 | type = string
22 | }
23 |
24 |
25 | variable "bootstrap_files_dir" {
26 | description = <<-EOF
27 | Bootstrap file directory. If the variable has a value of `null` (default) - then it will not upload any other files other than the ones specified in the `files` variable.
28 | More information can be found at https://docs.paloaltonetworks.com/vm-series/9-1/vm-series-deployment/bootstrap-the-vm-series-firewall/bootstrap-package.
29 | EOF
30 | type = string
31 | default = null
32 | }
33 |
34 |
35 | variable "folders" {
36 | description = <<-EOF
37 | List of folder paths that will be used to create dedicated boostrap package folder sets per firewall or firewall group (for example to distinguish configuration per region, per inbound/obew role, etc) within the created storage bucket.
38 |
39 | A default value (empty list) will result in the creation of a single bootstrap package folder set in the bucket top-level directory.
40 | EOF
41 | default = []
42 | type = list(any)
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/scripts/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile
3 | # To update, run:
4 | #
5 | # pip-compile requirements.txt
6 | #
7 | appdirs==1.4.4
8 | # via
9 | # -r requirements.txt
10 | # virtualenv
11 | cfgv==3.2.0
12 | # via
13 | # -r requirements.txt
14 | # pre-commit
15 | click==7.1.2
16 | # via
17 | # -r requirements.txt
18 | # pip-tools
19 | distlib==0.3.1
20 | # via
21 | # -r requirements.txt
22 | # virtualenv
23 | filelock==3.0.12
24 | # via
25 | # -r requirements.txt
26 | # virtualenv
27 | identify==2.2.4
28 | # via
29 | # -r requirements.txt
30 | # pre-commit
31 | importlib-metadata==4.0.1
32 | # via
33 | # -r requirements.txt
34 | # pep517
35 | # pre-commit
36 | # virtualenv
37 | importlib-resources==5.1.2
38 | # via
39 | # -r requirements.txt
40 | # pre-commit
41 | # virtualenv
42 | nodeenv==1.6.0
43 | # via
44 | # -r requirements.txt
45 | # pre-commit
46 | pep517==0.10.0
47 | # via
48 | # -r requirements.txt
49 | # pip-tools
50 | pip-tools==6.1.0
51 | # via -r requirements.txt
52 | pre-commit==2.7.1
53 | # via -r requirements.txt
54 | pyyaml==5.4.1
55 | # via
56 | # -r requirements.txt
57 | # pre-commit
58 | six==1.16.0
59 | # via
60 | # -r requirements.txt
61 | # virtualenv
62 | toml==0.10.2
63 | # via
64 | # -r requirements.txt
65 | # pep517
66 | # pre-commit
67 | typing-extensions==3.10.0.0
68 | # via
69 | # -r requirements.txt
70 | # importlib-metadata
71 | virtualenv==20.4.6
72 | # via
73 | # -r requirements.txt
74 | # pre-commit
75 | zipp==3.4.1
76 | # via
77 | # -r requirements.txt
78 | # importlib-metadata
79 | # importlib-resources
80 | # pep517
81 |
82 | # The following packages are considered to be unsafe in a requirements file:
83 | # pip
84 |
--------------------------------------------------------------------------------
/examples/standalone_vmseries_with_metadata_bootstrap/main.tf:
--------------------------------------------------------------------------------
1 | module "vpc" {
2 | source = "../../modules/vpc"
3 |
4 | for_each = var.networks
5 |
6 | project_id = var.project
7 | name = "${var.name_prefix}${each.value.vpc_name}"
8 | create_network = each.value.create_network
9 | delete_default_routes_on_create = each.value.delete_default_routes_on_create
10 | mtu = each.value.mtu
11 | routing_mode = each.value.routing_mode
12 | subnetworks = { for k, v in each.value.subnetworks : k => merge(v, {
13 | name = "${var.name_prefix}${v.name}"
14 | })
15 | }
16 | firewall_rules = try({ for k, v in each.value.firewall_rules : k => merge(v, {
17 | name = "${var.name_prefix}${v.name}"
18 | })
19 | }, {})
20 | }
21 |
22 | module "vmseries" {
23 | source = "../../modules/vmseries"
24 |
25 | for_each = var.vmseries
26 |
27 | name = "${var.name_prefix}${each.value.name}"
28 | zone = each.value.zone
29 | ssh_keys = try(each.value.ssh_keys, var.vmseries_common.ssh_keys)
30 | vmseries_image = try(each.value.vmseries_image, var.vmseries_common.vmseries_image)
31 | machine_type = try(each.value.machine_type, var.vmseries_common.machine_type)
32 | min_cpu_platform = try(each.value.min_cpu_platform, var.vmseries_common.min_cpu_platform, "Intel Cascade Lake")
33 | tags = try(each.value.tags, var.vmseries_common.tags, [])
34 | scopes = try(each.value.scopes, var.vmseries_common.scopes, [])
35 | create_instance_group = true
36 |
37 | bootstrap_options = try(each.value.bootstrap_options, {})
38 |
39 | named_ports = try(each.value.named_ports, [])
40 |
41 | network_interfaces = [for v in each.value.network_interfaces :
42 | {
43 | subnetwork = module.vpc[v.vpc_network_key].subnetworks[v.subnetwork_key].self_link
44 | private_ip = v.private_ip
45 | create_public_ip = try(v.create_public_ip, false)
46 | public_ip = try(v.public_ip, null)
47 | }]
48 | }
--------------------------------------------------------------------------------
/examples/vmseries_ha/main_test.go:
--------------------------------------------------------------------------------
1 | package vmseries_ha
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
--------------------------------------------------------------------------------
/examples/multi_nic_common/main_test.go:
--------------------------------------------------------------------------------
1 | package multi_nic_common
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
--------------------------------------------------------------------------------
/examples/panorama_standalone/main_test.go:
--------------------------------------------------------------------------------
1 | package panorama_standalone
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
--------------------------------------------------------------------------------
/examples/vpc_peering_common/main_test.go:
--------------------------------------------------------------------------------
1 | package vpc_peering_common
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
--------------------------------------------------------------------------------
/examples/vpc_peering_dedicated/main_test.go:
--------------------------------------------------------------------------------
1 | package vpc_peering_dedicated
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
--------------------------------------------------------------------------------
/examples/vpc_peering_common_with_autoscale/main_test.go:
--------------------------------------------------------------------------------
1 | package vpc_peering_common_with_autoscale
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
66 |
--------------------------------------------------------------------------------
/examples/vpc_peering_common_with_network_tags/main_test.go:
--------------------------------------------------------------------------------
1 | package vpc_peering_common_with_network_tags
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
--------------------------------------------------------------------------------
/examples/vpc_peering_dedicated_with_autoscale/main_test.go:
--------------------------------------------------------------------------------
1 | package vpc_peering_dedicated_with_autoscale
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
66 |
--------------------------------------------------------------------------------
/examples/standalone_vmseries_with_metadata_bootstrap/main_test.go:
--------------------------------------------------------------------------------
1 | package standalone_vmseries_with_metadata_bootstrap
2 |
3 | import (
4 | "testing"
5 | "log"
6 |
7 | "github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton/pkg/testskeleton"
8 | "github.com/gruntwork-io/terratest/modules/logger"
9 | "github.com/gruntwork-io/terratest/modules/terraform"
10 | )
11 |
12 | func CreateTerraformOptions(t *testing.T) *terraform.Options {
13 | varsInfo, err := testskeleton.GenerateTerraformVarsInfo("gcp")
14 | if err != nil {
15 | // Handle the error
16 | log.Fatalf("Error generating terraform vars info: %v", err)
17 | }
18 |
19 | // define options for Terraform
20 | terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
21 | TerraformDir: ".",
22 | VarFiles: []string{"example.tfvars"},
23 | Vars: map[string]interface{}{
24 | "name_prefix": varsInfo.NamePrefix,
25 | "project": varsInfo.GoogleProjectId,
26 | },
27 | Logger: logger.Default,
28 | Lock: true,
29 | Upgrade: true,
30 | SetVarsAfterVarFiles: true,
31 | })
32 |
33 | return terraformOptions
34 | }
35 |
36 | func TestValidate(t *testing.T) {
37 | testskeleton.ValidateCode(t, nil)
38 | }
39 |
40 | func TestPlan(t *testing.T) {
41 | // define options for Terraform
42 | terraformOptions := CreateTerraformOptions(t)
43 | // prepare list of items to check
44 | assertList := []testskeleton.AssertExpression{}
45 | // plan test infrastructure and verify outputs
46 | testskeleton.PlanInfraCheckErrors(t, terraformOptions, assertList, "No errors are expected")
47 | }
48 |
49 | func TestApply(t *testing.T) {
50 | // define options for Terraform
51 | terraformOptions := CreateTerraformOptions(t)
52 | // prepare list of items to check
53 | assertList := []testskeleton.AssertExpression{}
54 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
55 | testskeleton.DeployInfraCheckOutputs(t, terraformOptions, assertList)
56 | }
57 |
58 | func TestIdempotence(t *testing.T) {
59 | // define options for Terraform
60 | terraformOptions := CreateTerraformOptions(t)
61 | // prepare list of items to check
62 | assertList := []testskeleton.AssertExpression{}
63 | // deploy test infrastructure and verify outputs and check if there are no planned changes after deployment
64 | testskeleton.DeployInfraCheckOutputsVerifyChanges(t, terraformOptions, assertList)
65 | }
--------------------------------------------------------------------------------
/examples/standalone_vmseries_with_metadata_bootstrap/example.tfvars:
--------------------------------------------------------------------------------
1 | project = ""
2 | name_prefix = ""
3 |
4 | networks = {
5 | "vmseries-vpc" = {
6 | vpc_name = "firewall-vpc"
7 | create_network = true
8 | delete_default_routes_on_create = false
9 | mtu = "1460"
10 | routing_mode = "REGIONAL"
11 | subnetworks = {
12 | "vmseries-sub" = {
13 | name = "vmseries-subnet"
14 | create_subnetwork = true
15 | ip_cidr_range = "10.10.10.0/24"
16 | region = "us-central1"
17 | }
18 | }
19 | firewall_rules = {
20 | "allow-vmseries-ingress" = {
21 | name = "vmseries-mgmt"
22 | source_ranges = ["1.1.1.1/32"] # Replace 1.1.1.1/32 with your own souurce IP address for management purposes.
23 | priority = "1000"
24 | allowed_protocol = "all"
25 | allowed_ports = []
26 | }
27 | }
28 | }
29 | }
30 |
31 | vmseries = {
32 | "fw-vmseries-01" = {
33 | name = "fw-vmseries-01"
34 | zone = "us-central1-b"
35 | vmseries_image = "vmseries-flex-byol-1022h2"
36 | ssh_keys = "admin:"
37 | machine_type = "n2-standard-4"
38 | min_cpu_platform = "Intel Cascade Lake"
39 | tags = ["vmseries"]
40 | scopes = [
41 | "https://www.googleapis.com/auth/compute.readonly",
42 | "https://www.googleapis.com/auth/cloud.useraccounts.readonly",
43 | "https://www.googleapis.com/auth/devstorage.read_only",
44 | "https://www.googleapis.com/auth/logging.write",
45 | "https://www.googleapis.com/auth/monitoring.write",
46 | ]
47 | bootstrap_options = {
48 | panorama-server = "1.1.1.1" # Modify this value as per deployment requirements
49 | dns-primary = "8.8.8.8" # Modify this value as per deployment requirements
50 | dns-secondary = "8.8.4.4" # Modify this value as per deployment requirements
51 | }
52 | named_ports = [
53 | {
54 | name = "http"
55 | port = 80
56 | },
57 | {
58 | name = "https"
59 | port = 443
60 | }
61 | ]
62 | network_interfaces = [
63 | {
64 | vpc_network_key = "vmseries-vpc"
65 | subnetwork_key = "vmseries-sub"
66 | private_ip = "10.10.10.2"
67 | create_public_ip = true
68 | }
69 | ]
70 | }
71 | }
--------------------------------------------------------------------------------
/modules/vpc-peering/variables.tf:
--------------------------------------------------------------------------------
1 | variable "local_network" {
2 | description = "Self-link or id of the first network (local) in pair."
3 | type = string
4 | }
5 |
6 | variable "peer_network" {
7 | description = "Self-link or id of the second network (peer) in pair."
8 | type = string
9 | }
10 |
11 | variable "local_peering_name" {
12 | description = "Name for 'local->peer' direction peering resource. If not specified defaults to `-`."
13 | default = null
14 | type = string
15 | }
16 |
17 | variable "peer_peering_name" {
18 | description = "Name for 'peer->local' direction peering resource. If not specified defaults to `-`."
19 | default = null
20 | type = string
21 | }
22 |
23 | variable "name_prefix" {
24 | description = "Optional prefix for auto-generated peering resource names."
25 | default = ""
26 | type = string
27 | }
28 |
29 | variable "local_export_custom_routes" {
30 | description = "Export custom routes setting for 'local->peer' direction."
31 | default = false
32 | type = bool
33 | }
34 |
35 | variable "local_import_custom_routes" {
36 | description = "Import custom routes setting for 'local->peer' direction."
37 | default = false
38 | type = bool
39 | }
40 |
41 | variable "local_export_subnet_routes_with_public_ip" {
42 | description = "Export subnet routes with public IP setting for 'local->peer' direction."
43 | default = false
44 | type = bool
45 | }
46 |
47 | variable "local_import_subnet_routes_with_public_ip" {
48 | description = "Import subnet routes with public IP setting for 'local->peer' direction."
49 | default = false
50 | type = bool
51 | }
52 |
53 | variable "peer_export_custom_routes" {
54 | description = "Export custom routes setting for 'peer->local' direction."
55 | default = false
56 | type = bool
57 | }
58 |
59 | variable "peer_import_custom_routes" {
60 | description = "Import custom routes setting for 'peer->local' direction."
61 | default = false
62 | type = bool
63 | }
64 |
65 | variable "peer_export_subnet_routes_with_public_ip" {
66 | description = "Export subnet routes with public IP setting for 'peer->local' direction."
67 | default = false
68 | type = bool
69 | }
70 |
71 | variable "peer_import_subnet_routes_with_public_ip" {
72 | description = "Import subnet routes with public IP setting for 'peer->local' direction."
73 | default = false
74 | type = bool
75 | }
76 |
--------------------------------------------------------------------------------
/modules/vpc/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | subnetworks_existing = {
3 | for k, v in var.subnetworks
4 | : k => v
5 | if try(v.create_subnetwork == false, false)
6 | }
7 |
8 | // Some subnetworks need to be created:
9 | subnetworks_to_create = {
10 | for k, v in var.subnetworks
11 | : k => v
12 | if !(try(v.create_subnetwork == false, false))
13 | }
14 | }
15 |
16 | data "google_compute_network" "this" {
17 | count = var.create_network == true ? 0 : 1
18 |
19 | name = var.name
20 | project = var.project_id
21 | }
22 |
23 | resource "google_compute_network" "this" {
24 | count = var.create_network == true ? 1 : 0
25 |
26 | name = var.name
27 | project = var.project_id
28 | delete_default_routes_on_create = var.delete_default_routes_on_create
29 | mtu = var.mtu
30 | auto_create_subnetworks = false
31 | routing_mode = var.routing_mode
32 | }
33 |
34 | data "google_compute_subnetwork" "this" {
35 | for_each = local.subnetworks_existing
36 |
37 | name = each.value.name
38 | project = var.project_id
39 | region = each.value.region
40 | }
41 |
42 | resource "google_compute_subnetwork" "this" {
43 | for_each = local.subnetworks_to_create
44 |
45 | name = each.value.name
46 | ip_cidr_range = each.value.ip_cidr_range
47 | network = try(data.google_compute_network.this[0].self_link, google_compute_network.this[0].self_link)
48 | region = each.value.region
49 | project = var.project_id
50 | }
51 |
52 | resource "google_compute_firewall" "this" {
53 | for_each = var.firewall_rules
54 |
55 | name = "${each.value.name}-ingress"
56 | network = try(data.google_compute_network.this[0].self_link, google_compute_network.this[0].self_link)
57 | direction = "INGRESS"
58 | source_ranges = each.value.source_ranges
59 | source_tags = each.value.source_tags
60 | source_service_accounts = each.value.source_service_accounts
61 | project = var.project_id
62 | priority = each.value.priority
63 | target_service_accounts = each.value.target_service_accounts
64 | target_tags = each.value.target_tags
65 |
66 |
67 | allow {
68 | protocol = each.value.allowed_protocol
69 | ports = each.value.allowed_ports
70 | }
71 |
72 | dynamic "log_config" {
73 | for_each = compact(try([each.value.log_metadata], []))
74 |
75 | content {
76 | metadata = log_config.value
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/modules/bootstrap/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | bootstrap_filenames = var.bootstrap_files_dir != null ? { for f in fileset(var.bootstrap_files_dir, "**") : f => "${var.bootstrap_files_dir}/${f}" } : {}
3 | # invert var.files map
4 | inverted_files = { for k, v in var.files : v => k }
5 | inverted_filenames = merge(local.bootstrap_filenames, local.inverted_files)
6 | # invert local.filenames map
7 | filenames = { for k, v in local.inverted_filenames : v => k }
8 | }
9 | resource "random_string" "randomstring" {
10 | length = 10
11 | min_lower = 10
12 | special = false
13 | }
14 |
15 | resource "google_storage_bucket" "this" {
16 | name = join("", [var.name_prefix, random_string.randomstring.result])
17 | force_destroy = true
18 | uniform_bucket_level_access = true
19 | location = var.location
20 |
21 | versioning {
22 | enabled = true
23 | }
24 | }
25 |
26 | locals {
27 | folders = length(var.folders) == 0 ? [""] : var.folders
28 | }
29 |
30 |
31 | resource "google_storage_bucket_object" "config_empty" {
32 | for_each = toset(local.folders)
33 |
34 | name = each.value != "" ? "${each.value}/config/" : "config/"
35 | content = "config/"
36 | bucket = google_storage_bucket.this.name
37 | }
38 |
39 | resource "google_storage_bucket_object" "content_empty" {
40 | for_each = toset(local.folders)
41 |
42 | name = each.value != "" ? "${each.value}/content/" : "content/"
43 | content = "content/"
44 | bucket = google_storage_bucket.this.name
45 | }
46 |
47 | resource "google_storage_bucket_object" "license_empty" {
48 | for_each = toset(local.folders)
49 |
50 | name = each.value != "" ? "${each.value}/license/" : "license/"
51 | content = "license/"
52 | bucket = google_storage_bucket.this.name
53 | }
54 |
55 | resource "google_storage_bucket_object" "software_empty" {
56 | for_each = toset(local.folders)
57 |
58 | name = each.value != "" ? "${each.value}/software/" : "software/"
59 | content = "software/"
60 | bucket = google_storage_bucket.this.name
61 | }
62 |
63 | resource "google_storage_bucket_object" "file" {
64 | for_each = local.filenames
65 |
66 | name = each.value
67 | source = each.key
68 | bucket = google_storage_bucket.this.name
69 | }
70 |
71 | data "google_compute_default_service_account" "this" {}
72 |
73 | resource "google_storage_bucket_iam_member" "member" {
74 | bucket = google_storage_bucket.this.name
75 | role = "roles/storage.objectViewer"
76 | member = "serviceAccount:${var.service_account != null ? var.service_account : data.google_compute_default_service_account.this.email}"
77 | }
--------------------------------------------------------------------------------
/modules/panorama/main.tf:
--------------------------------------------------------------------------------
1 | data "google_compute_image" "this" {
2 | count = var.custom_image != null ? 0 : 1
3 |
4 | project = "paloaltonetworksgcp-public"
5 | name = var.panorama_version
6 | }
7 |
8 | # Permanent private address, not ephemeral, because the managed firewalls keep it saved.
9 | resource "google_compute_address" "private" {
10 | name = "${var.name}-private"
11 | project = var.project
12 | region = var.region
13 | subnetwork = var.subnet
14 | address = try(var.private_static_ip, null)
15 | address_type = "INTERNAL"
16 | }
17 |
18 | # Permanent public address, not ephemeral.
19 | resource "google_compute_address" "public" {
20 | count = var.attach_public_ip ? 1 : 0
21 |
22 | name = "${var.name}-public"
23 | project = var.project
24 | region = var.region
25 | address = try(var.public_static_ip, null)
26 | }
27 |
28 | resource "google_compute_disk" "this" {
29 | for_each = { for k, v in var.log_disks : k => v }
30 |
31 | name = each.value.name
32 | project = var.project
33 | zone = var.zone
34 | type = each.value.type
35 | size = each.value.size
36 | }
37 |
38 | resource "google_compute_instance" "this" {
39 | name = var.name
40 | zone = var.zone
41 | machine_type = var.machine_type
42 | min_cpu_platform = var.min_cpu_platform
43 | deletion_protection = var.deletion_protection
44 | labels = var.labels
45 | tags = var.tags
46 | project = var.project
47 | can_ip_forward = false
48 | allow_stopping_for_update = true
49 |
50 | metadata = merge({
51 | serial-port-enable = true
52 | ssh-keys = var.ssh_keys
53 | }, var.metadata)
54 |
55 | service_account {
56 | email = var.service_account
57 | scopes = var.scopes
58 | }
59 |
60 | network_interface {
61 |
62 | dynamic "access_config" {
63 | for_each = var.attach_public_ip ? [""] : []
64 | content {
65 | nat_ip = google_compute_address.public[0].address
66 | }
67 | }
68 |
69 | network_ip = google_compute_address.private.address
70 | subnetwork = var.subnet
71 | }
72 |
73 | boot_disk {
74 | initialize_params {
75 | image = coalesce(var.custom_image, try(data.google_compute_image.this[0].id, null))
76 | size = var.disk_size
77 | type = var.disk_type
78 | }
79 | }
80 |
81 | dynamic "attached_disk" {
82 | for_each = google_compute_disk.this
83 | content {
84 | source = google_compute_disk.this[attached_disk.key].id
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/.github/workflows/help-command.yml:
--------------------------------------------------------------------------------
1 | name: ChatOPS Help
2 | run-name: "Display ChatOPS help (#${{ github.event.inputs.pr-id }}) ${{ github.event.inputs.pr-title }}"
3 |
4 | on:
5 | workflow_dispatch:
6 | inputs:
7 | pr-id:
8 | description: ID of the PR that triggered this workflow
9 | type: string
10 | required: true
11 | pr-title:
12 | description: Title of the PR that triggered this workflow
13 | type: string
14 | required: true
15 | comment-id:
16 | description: 'The comment-id of the slash command'
17 | type: string
18 | required: true
19 | branch:
20 | description: Branch on which the tests should run
21 | type: string
22 | default: main
23 |
24 | jobs:
25 | help:
26 | name: Add help comment to originating PR
27 | permissions:
28 | contents: read
29 | pull-requests: write
30 | runs-on: ubuntu-latest
31 | steps:
32 | - name: add help comment
33 | uses: peter-evans/create-or-update-comment@v3
34 | with:
35 | comment-id: ${{ inputs.comment-id }}
36 | issue-number: ${{ inputs.pr-id }}
37 | body: |
38 |
39 | ## ChatOPS built in help:
40 |
41 | Currently supported commands include:
42 |
43 | * `/sca` - run all SCA tests via `pre-commit`
44 | * `/validate` - run `terraform validate`
45 | * `/plan` - plan the infrastructure (only examples)
46 | * `/apply` - deploy the infrastructure and destroy afterwards (only examples)
47 | * `/idempotence` - test idempotence: deploy, plan and destroy afterwards (only examples).
48 |
49 | The 1st command does not take arguments, the remaining take two:
50 |
51 | * `paths` - a space delimitied list of module paths
52 | * `tf_version` - (optional, defaults to the latest available) a space delimited list of Terraform versions to test the infrastrucure against.
53 |
54 | Examples:
55 |
56 | ```bash
57 | # run idempotence tests on listed modules with Terraform versions: 1.2 (latest patch available), 1.4 (latest patch available), 1.5.4.
58 | /idempotence paths="examples/common_vmseries examples/panorama_standalone" tf_version="1.2 1.4 1.5.4"
59 | ```
60 |
61 | ```bash
62 | # run validation tests with the latest available Terraform version on listed modules.
63 | /validate paths="modules/vmseries modules/vnet examples/dedicated_vmseries"
64 | ```
65 |
66 | reactions: '+1'
67 | reactions-edit-mode: replace
--------------------------------------------------------------------------------
/modules/lb_internal/main.tf:
--------------------------------------------------------------------------------
1 | resource "google_compute_health_check" "this" {
2 | name = "${var.name}-${var.region}-check-tcp${var.health_check_port}"
3 | project = var.project
4 |
5 | tcp_health_check {
6 | port = var.health_check_port
7 | }
8 | }
9 |
10 | resource "google_compute_region_backend_service" "this" {
11 | provider = google-beta
12 |
13 | name = var.name
14 | network = var.network
15 | project = var.project
16 | region = var.region
17 |
18 | health_checks = [var.health_check != null ? var.health_check : google_compute_health_check.this.self_link]
19 | session_affinity = var.session_affinity
20 | timeout_sec = var.timeout_sec
21 | connection_draining_timeout_sec = var.connection_draining_timeout_sec
22 |
23 | dynamic "backend" {
24 | for_each = var.backends
25 | content {
26 | group = backend.value
27 | failover = false
28 | }
29 | }
30 |
31 | dynamic "backend" {
32 | for_each = var.failover_backends
33 | content {
34 | group = backend.value
35 | failover = true
36 | }
37 | }
38 |
39 | # This feature requires beta provider as of 2023-03-16
40 | dynamic "connection_tracking_policy" {
41 | for_each = var.connection_tracking_policy != null ? ["this"] : []
42 | content {
43 | tracking_mode = try(var.connection_tracking_policy.mode, null)
44 | idle_timeout_sec = try(var.connection_tracking_policy.idle_timeout_sec, null)
45 | connection_persistence_on_unhealthy_backends = try(var.connection_tracking_policy.persistence_on_unhealthy_backends, null)
46 | }
47 | }
48 |
49 | dynamic "failover_policy" {
50 | for_each = var.disable_connection_drain_on_failover != null || var.drop_traffic_if_unhealthy != null || var.failover_ratio != null ? ["one"] : []
51 |
52 | content {
53 | disable_connection_drain_on_failover = var.disable_connection_drain_on_failover
54 | drop_traffic_if_unhealthy = var.drop_traffic_if_unhealthy
55 | failover_ratio = var.failover_ratio
56 | }
57 | }
58 | }
59 |
60 | resource "google_compute_forwarding_rule" "this" {
61 | name = var.name
62 | project = var.project
63 | region = var.region
64 |
65 | load_balancing_scheme = "INTERNAL"
66 | ip_address = var.ip_address
67 | ip_protocol = var.ip_protocol
68 | all_ports = var.all_ports
69 | ports = var.ports
70 | subnetwork = var.subnetwork
71 | allow_global_access = var.allow_global_access
72 | backend_service = google_compute_region_backend_service.this.self_link
73 | }
74 |
--------------------------------------------------------------------------------
/.github/workflows/sca-command.yml:
--------------------------------------------------------------------------------
1 | name: ChatOPS SCA
2 | run-name: "On demand SCA test for PR - (#${{ github.event.inputs.pr-id }}) ${{ github.event.inputs.pr-title }}"
3 |
4 | permissions:
5 | contents: read
6 |
7 | on:
8 | workflow_dispatch:
9 | inputs:
10 | pr-id:
11 | description: ID of the PR that triggered this workflow
12 | type: string
13 | required: true
14 | pr-title:
15 | description: Title of the PR that triggered this workflow
16 | type: string
17 | required: true
18 | comment-id:
19 | description: 'The comment-id of the slash command'
20 | type: string
21 | required: true
22 | branch:
23 | description: Branch on which the tests should run
24 | type: string
25 | default: main
26 |
27 | jobs:
28 | init:
29 | name: Add a comment to originating PR with job ID
30 | permissions:
31 | contents: read
32 | pull-requests: write
33 | runs-on: ubuntu-latest
34 | outputs:
35 | paths: ${{ steps.paths_reformat.outputs.paths }}
36 | steps:
37 | - name: add comment
38 | uses: peter-evans/create-or-update-comment@v3
39 | with:
40 | comment-id: ${{ inputs.comment-id }}
41 | issue-number: ${{ inputs.pr-id }}
42 | body: |
43 | > Testing job ID: [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
44 |
45 | - name: reformat paths input property
46 | id: paths_reformat
47 | env:
48 | IN_PATHS: ${{ inputs.paths }}
49 | run: echo "paths=$(echo $IN_PATHS | tr " " "," )" >> $GITHUB_OUTPUT
50 |
51 | test:
52 | name: Run SCA test
53 | needs: init
54 | permissions:
55 | contents: read
56 | uses: PaloAltoNetworks/terraform-modules-vmseries-ci-workflows/.github/workflows/_pre_commit.yml@v2.3
57 | secrets: inherit
58 | with:
59 | pre-commit-hooks: terraform_fmt terraform_docs terraform_tflint checkov
60 | branch: ${{ inputs.branch }}
61 |
62 | finish_comment_pr:
63 | name: Add a comment to originating PR
64 | needs: test
65 | if: always()
66 | permissions:
67 | contents: read
68 | pull-requests: write
69 | runs-on: ubuntu-latest
70 | steps:
71 | - name: add comment
72 | uses: peter-evans/create-or-update-comment@v3
73 | with:
74 | comment-id: ${{ inputs.comment-id }}
75 | issue-number: ${{ inputs.pr-id }}
76 | body: |
77 | > Job result: ${{ needs.test.result == 'success' && 'SUCCESS' || 'FAILURE' }}
78 | reactions: ${{ needs.test.result == 'success' && '+1' || '-1' }}
79 | reactions-edit-mode: replace
--------------------------------------------------------------------------------
/modules/iam_service_account/README.md:
--------------------------------------------------------------------------------
1 | # IAM Service Account
2 |
3 | Create a dedicated IAM Service Account that will be used to run firewall instances.
4 | This module is optional - even if you don't use it, firewalls run fine on the default Google Service Account.
5 |
6 | The account produced by this module is intended to have minimal required permissions.
7 |
8 | [Google Cloud Docs](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#best_practices)
9 |
10 | ## Reference
11 |
12 | ### Requirements
13 |
14 | | Name | Version |
15 | |------|---------|
16 | | [terraform](#requirement\_terraform) | >= 1.3, < 2.0 |
17 | | [google](#requirement\_google) | ~> 4.54 |
18 |
19 | ### Providers
20 |
21 | | Name | Version |
22 | |------|---------|
23 | | [google](#provider\_google) | ~> 4.54 |
24 |
25 | ### Modules
26 |
27 | No modules.
28 |
29 | ### Resources
30 |
31 | | Name | Type |
32 | |------|------|
33 | | [google_project_iam_member.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |
34 | | [google_service_account.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource |
35 |
36 | ### Inputs
37 |
38 | | Name | Description | Type | Default | Required |
39 | |------|-------------|------|---------|:--------:|
40 | | [display\_name](#input\_display\_name) | n/a | `string` | `"Palo Alto Networks Firewall Service Account"` | no |
41 | | [project\_id](#input\_project\_id) | ID of a project in which the service account will be created. | `string` | n/a | yes |
42 | | [roles](#input\_roles) | List of IAM role names, such as ["roles/compute.viewer"] or ["project/A/roles/B"]. The default list is suitable for Palo Alto Networks Firewall to run and publish custom metrics to GCP Stackdriver. | `set(string)` | [
"roles/compute.networkViewer",
"roles/logging.logWriter",
"roles/monitoring.metricWriter",
"roles/monitoring.viewer",
"roles/viewer",
"roles/stackdriver.accounts.viewer",
"roles/stackdriver.resourceMetadata.writer"
]
| no |
43 | | [service\_account\_id](#input\_service\_account\_id) | n/a | `string` | `"The google_service_account.account_id of the created IAM account, unique string per project."` | no |
44 |
45 | ### Outputs
46 |
47 | | Name | Description |
48 | |------|-------------|
49 | | [email](#output\_email) | n/a |
50 |
51 |
--------------------------------------------------------------------------------
/modules/vpn/variables.tf:
--------------------------------------------------------------------------------
1 | variable "project" {
2 | default = null
3 | type = string
4 | }
5 |
6 | variable "region" {
7 | description = "Region to deploy VPN gateway in"
8 | type = string
9 | }
10 |
11 | variable "vpn_gateway_name" {
12 | description = "VPN gateway name. Gateway created by the module"
13 | type = string
14 | }
15 |
16 | variable "router_name" {
17 | description = "Cloud router name. The router is created by the module"
18 | type = string
19 | default = null
20 | }
21 |
22 | variable "network" {
23 | description = "VPC network ID that should be used for deployment"
24 | type = string
25 | }
26 |
27 | variable "labels" {
28 | description = "Labels for VPN components"
29 | type = map(string)
30 | default = {}
31 | }
32 |
33 | variable "vpn_config" {
34 | type = any
35 | description = <<-EOF
36 | VPN configuration from GCP to on-prem or from GCP to GCP.
37 | If you'd like secrets to be randomly generated set `shared_secret` to empty string ("").
38 |
39 | Example:
40 |
41 | ```
42 | vpn_config = {
43 | router_asn = 65000
44 | local_network = "vpc-vpn"
45 |
46 | router_advertise_config = {
47 | ip_ranges = {
48 | "10.10.0.0/16" : "GCP range 1"
49 | }
50 | mode = "CUSTOM"
51 | groups = null
52 | }
53 |
54 | instances = {
55 | vpn-to-onprem = {
56 | name = "vpn-to-onprem",
57 | peer_external_gateway = {
58 | redundancy_type = "TWO_IPS_REDUNDANCY"
59 | interfaces = [{
60 | id = 0
61 | ip_address = "1.1.1.1"
62 | }, {
63 | id = 1
64 | ip_address = "2.2.2.2"
65 | }]
66 | },
67 | tunnels = {
68 | remote0 = {
69 | bgp_peer = {
70 | address = "169.254.1.2"
71 | asn = 65001
72 | }
73 | bgp_peer_options = null
74 | bgp_session_range = "169.254.1.1/30"
75 | ike_version = 2
76 | vpn_gateway_interface = 0
77 | peer_external_gateway_interface = 0
78 | shared_secret = "secret"
79 | }
80 | remote1 = {
81 | bgp_peer = {
82 | address = "169.254.1.6"
83 | asn = 65001
84 | }
85 | bgp_peer_options = null
86 | bgp_session_range = "169.254.1.5/30"
87 | ike_version = 2
88 | vpn_gateway_interface = 1
89 | peer_external_gateway_interface = 1
90 | shared_secret = "secret"
91 | }
92 | }
93 | }
94 | }
95 | }
96 | ```
97 | EOF
98 | }
--------------------------------------------------------------------------------
/examples/panorama_standalone/variables.tf:
--------------------------------------------------------------------------------
1 | # General
2 | variable "project" {
3 | description = "The project name to deploy the infrastructure in to."
4 | type = string
5 | default = null
6 | }
7 | variable "region" {
8 | description = "The region into which to deploy the infrastructure in to"
9 | type = string
10 | default = "us-central1"
11 | }
12 | variable "name_prefix" {
13 | description = "A string to prefix resource namings"
14 | type = string
15 | default = ""
16 | }
17 |
18 | # VPC
19 | variable "networks" {
20 | description = <<-EOF
21 | A map containing each network setting.
22 |
23 | Example of variable deployment :
24 |
25 | ```
26 | networks = {
27 | "panorama-vpc" = {
28 | vpc_name = "firewall-vpc"
29 | create_network = true
30 | delete_default_routes_on_create = "false"
31 | mtu = "1460"
32 | routing_mode = "REGIONAL"
33 | subnetworks = {
34 | "panorama-sub" = {
35 | name = "panorama-subnet"
36 | create_subnetwork = true
37 | ip_cidr_range = "172.21.21.0/24"
38 | region = "us-central1"
39 | }
40 | }
41 | firewall_rules = {
42 | "allow-panorama-ingress" = {
43 | name = "panorama-mgmt"
44 | source_ranges = ["1.1.1.1/32", "2.2.2.2/32"]
45 | priority = "1000"
46 | allowed_protocol = "all"
47 | allowed_ports = []
48 | }
49 | }
50 | }
51 | ```
52 |
53 | For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/vpc#input_networks)
54 |
55 | Multiple keys can be added and will be deployed by the code
56 | EOF
57 | }
58 |
59 | # Panorama
60 | variable "panoramas" {
61 | description = <<-EOF
62 | A map containing each panorama setting.
63 |
64 | Example of variable deployment :
65 |
66 | ```
67 | panoramas = {
68 | "panorama-01" = {
69 | panorama_name = "panorama-01"
70 | panorama_vpc = "panorama-vpc"
71 | panorama_subnet = "panorama-subnet"
72 | panorama_version = "panorama-byol-1000"
73 | ssh_keys = "admin:PUBLIC-KEY"
74 | attach_public_ip = true
75 | private_static_ip = "172.21.21.2"
76 | }
77 | }
78 | ```
79 |
80 | For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/panorama#inputs)
81 |
82 | Multiple keys can be added and will be deployed by the code
83 | EOF
84 | }
85 |
--------------------------------------------------------------------------------
/.github/workflows/chatops.yml:
--------------------------------------------------------------------------------
1 | name: ChatOPS dispatcher
2 | run-name: "ChatOPS bot for PR - (#${{ github.event.issue.number }}) ${{ github.event.issue.title }}"
3 |
4 | permissions:
5 | contents: read
6 |
7 | on:
8 | issue_comment:
9 | types: [created]
10 |
11 | concurrency:
12 | group: chat-${{ github.event.issue.number }}
13 | cancel-in-progress: true
14 |
15 | jobs:
16 | dispatch:
17 | name: Dispatch a test job
18 | if: ${{ github.event.issue.pull_request }}
19 | runs-on: ubuntu-latest
20 | permissions:
21 | contents: read
22 | pull-requests: write
23 | steps:
24 | - name: get PR head branch
25 | uses: actions/github-script@v6
26 | id: pr
27 | with:
28 | result-encoding: string
29 | script: |
30 | let pr = await github.rest.pulls.get({
31 | owner: context.repo.owner,
32 | repo: context.repo.repo,
33 | pull_number: context.issue.number,
34 | })
35 | console.log(pr.data.head.ref)
36 | return pr.data.head.ref
37 |
38 | - name: Generate GitHub token
39 | id: generate-token
40 | uses: tibdex/github-app-token@v2
41 | with:
42 | app_id: ${{ secrets.CHATOPS_APP_ID }}
43 | private_key: ${{ secrets.CHATOPS_APP_PRIVATE_KEY }}
44 | installation_retrieval_mode: id
45 | installation_retrieval_payload: ${{ secrets.CHATOPS_APP_INSTALLATION_ID }}
46 |
47 | - name: "dispatch test command on branch: ${{ steps.pr.outputs.result }}"
48 | id: scd
49 | uses: peter-evans/slash-command-dispatch@v3
50 | with:
51 | token: ${{ steps.generate-token.outputs.token }}
52 | issue-type: pull-request
53 | dispatch-type: workflow
54 | permission: maintain
55 | commands: |
56 | validate
57 | plan
58 | apply
59 | idempotence
60 | sca
61 | help
62 | static-args: |
63 | comment-id=${{ github.event.comment.id }}
64 | pr-id=${{ github.event.issue.number }}
65 | pr-title=${{ github.event.issue.title }}
66 | branch=${{ steps.pr.outputs.result }}
67 |
68 | - name: Edit comment with error message
69 | if: steps.scd.outputs.error-message
70 | uses: peter-evans/create-or-update-comment@v3
71 | with:
72 | comment-id: ${{ github.event.comment.id }}
73 | body: |
74 | > ${{ steps.scd.outputs.error-message }}
75 | reactions: '-1'
76 | reactions-edit-mode: replace
77 |
78 | - name: Concurency ratio fallback
79 | if: cancelled()
80 | uses: peter-evans/create-or-update-comment@v3
81 | with:
82 | comment-id: ${{ github.event.comment.id }}
83 | body: |
84 | > ChatOPS run cancelled.
85 | > See [job run log](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.
86 | reactions: 'confused'
87 | reactions-edit-mode: replace
--------------------------------------------------------------------------------
/modules/lb_http_ext_global/main.tf:
--------------------------------------------------------------------------------
1 | resource "google_compute_global_forwarding_rule" "http" {
2 | count = var.http_forward ? 1 : 0
3 | name = "${var.name}-http"
4 | target = google_compute_target_http_proxy.default[0].self_link
5 | ip_address = google_compute_global_address.default.address
6 | port_range = "80"
7 | }
8 |
9 | resource "google_compute_global_forwarding_rule" "https" {
10 | count = var.ssl ? 1 : 0
11 | name = "${var.name}-https"
12 | target = google_compute_target_https_proxy.default[0].self_link
13 | ip_address = google_compute_global_address.default.address
14 | port_range = "443"
15 | }
16 |
17 | resource "google_compute_global_address" "default" {
18 | name = "${var.name}-address"
19 | ip_version = var.ip_version
20 | }
21 |
22 | # HTTP proxy when ssl is false
23 | resource "google_compute_target_http_proxy" "default" {
24 | count = var.http_forward ? 1 : 0
25 | name = "${var.name}-http-proxy"
26 | url_map = (var.url_map != null ? var.url_map : google_compute_url_map.default.self_link)
27 | }
28 |
29 | # HTTPS proxy when ssl is true
30 | resource "google_compute_target_https_proxy" "default" {
31 | count = var.ssl ? 1 : 0
32 | name = "${var.name}-https-proxy"
33 | url_map = (var.url_map != null ? var.url_map : google_compute_url_map.default.self_link)
34 | ssl_certificates = compact(concat(var.ssl_certificates, google_compute_ssl_certificate.default[*].self_link, ), )
35 | }
36 |
37 | resource "google_compute_ssl_certificate" "default" {
38 | count = var.ssl && !var.use_ssl_certificates ? 1 : 0
39 | name_prefix = "${var.name}-certificate"
40 | private_key = var.private_key
41 | certificate = var.certificate
42 |
43 | lifecycle {
44 | create_before_destroy = true
45 | }
46 | }
47 |
48 | resource "google_compute_url_map" "default" {
49 | name = var.name
50 | default_service = google_compute_backend_service.default.self_link
51 | }
52 |
53 | resource "google_compute_backend_service" "default" {
54 | name = var.name
55 | port_name = var.backend_port_name
56 | protocol = var.backend_protocol
57 | custom_request_headers = var.custom_request_headers
58 | timeout_sec = var.timeout_sec
59 | dynamic "backend" {
60 | for_each = var.backend_groups
61 | content {
62 | group = backend.value
63 | balancing_mode = var.balancing_mode
64 | capacity_scaler = var.capacity_scaler
65 | max_connections_per_instance = var.max_connections_per_instance
66 | max_rate_per_instance = var.max_rate_per_instance
67 | max_utilization = var.max_utilization
68 | }
69 | }
70 | health_checks = [google_compute_health_check.default.self_link]
71 | security_policy = var.security_policy
72 | enable_cdn = var.cdn
73 | }
74 |
75 | resource "google_compute_health_check" "default" {
76 | name = coalesce(var.health_check_name, "${var.name}-healthcheck")
77 | tcp_health_check {
78 | port = var.health_check_port
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/PaloAltoNetworks/terraform-google-vmseries-modules
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/PaloAltoNetworks/terraform-modules-vmseries-tests-skeleton v1.1.0
7 | github.com/gruntwork-io/terratest v0.43.6
8 | )
9 |
10 | require (
11 | cloud.google.com/go v0.110.2 // indirect
12 | cloud.google.com/go/compute v1.19.3 // indirect
13 | cloud.google.com/go/compute/metadata v0.2.3 // indirect
14 | cloud.google.com/go/iam v1.1.0 // indirect
15 | cloud.google.com/go/storage v1.31.0 // indirect
16 | github.com/agext/levenshtein v1.2.3 // indirect
17 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
18 | github.com/aws/aws-sdk-go v1.44.122 // indirect
19 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
20 | github.com/davecgh/go-spew v1.1.1 // indirect
21 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
22 | github.com/golang/protobuf v1.5.3 // indirect
23 | github.com/google/go-cmp v0.5.9 // indirect
24 | github.com/google/s2a-go v0.1.4 // indirect
25 | github.com/google/uuid v1.3.0 // indirect
26 | github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
27 | github.com/googleapis/gax-go/v2 v2.11.0 // indirect
28 | github.com/hashicorp/errwrap v1.0.0 // indirect
29 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
30 | github.com/hashicorp/go-getter v1.7.1 // indirect
31 | github.com/hashicorp/go-multierror v1.1.0 // indirect
32 | github.com/hashicorp/go-safetemp v1.0.0 // indirect
33 | github.com/hashicorp/go-version v1.6.0 // indirect
34 | github.com/hashicorp/hcl/v2 v2.9.1 // indirect
35 | github.com/hashicorp/terraform-json v0.17.1 // indirect
36 | github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect
37 | github.com/jmespath/go-jmespath v0.4.0 // indirect
38 | github.com/klauspost/compress v1.15.11 // indirect
39 | github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect
40 | github.com/mitchellh/go-homedir v1.1.0 // indirect
41 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect
42 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect
43 | github.com/pmezard/go-difflib v1.0.0 // indirect
44 | github.com/stretchr/testify v1.8.4 // indirect
45 | github.com/tmccombs/hcl2json v0.3.3 // indirect
46 | github.com/ulikunitz/xz v0.5.10 // indirect
47 | github.com/zclconf/go-cty v1.13.2 // indirect
48 | go.opencensus.io v0.24.0 // indirect
49 | golang.org/x/crypto v0.9.0 // indirect
50 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
51 | golang.org/x/net v0.10.0 // indirect
52 | golang.org/x/oauth2 v0.8.0 // indirect
53 | golang.org/x/sys v0.8.0 // indirect
54 | golang.org/x/text v0.9.0 // indirect
55 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
56 | google.golang.org/api v0.126.0 // indirect
57 | google.golang.org/appengine v1.6.7 // indirect
58 | google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect
59 | google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
60 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
61 | google.golang.org/grpc v1.55.0 // indirect
62 | google.golang.org/protobuf v1.31.0 // indirect
63 | gopkg.in/yaml.v3 v3.0.1 // indirect
64 | )
65 |
--------------------------------------------------------------------------------
/modules/vpc-peering/README.md:
--------------------------------------------------------------------------------
1 | # VPC peering
2 |
3 | The module allows to create VPC peering between two networks in both directions.
4 |
5 | By default, no routes are exported/imported for each direction, every option has to be explicitely enabled by setting appropriate value to `true`.
6 |
7 | ## Reference
8 |
9 | ### Requirements
10 |
11 | | Name | Version |
12 | |------|---------|
13 | | [terraform](#requirement\_terraform) | >= 1.3, < 2.0 |
14 |
15 | ### Providers
16 |
17 | | Name | Version |
18 | |------|---------|
19 | | [google](#provider\_google) | n/a |
20 |
21 | ### Modules
22 |
23 | No modules.
24 |
25 | ### Resources
26 |
27 | | Name | Type |
28 | |------|------|
29 | | [google_compute_network_peering.local](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network_peering) | resource |
30 | | [google_compute_network_peering.peer](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network_peering) | resource |
31 |
32 | ### Inputs
33 |
34 | | Name | Description | Type | Default | Required |
35 | |------|-------------|------|---------|:--------:|
36 | | [local\_export\_custom\_routes](#input\_local\_export\_custom\_routes) | Export custom routes setting for 'local->peer' direction. | `bool` | `false` | no |
37 | | [local\_export\_subnet\_routes\_with\_public\_ip](#input\_local\_export\_subnet\_routes\_with\_public\_ip) | Export subnet routes with public IP setting for 'local->peer' direction. | `bool` | `false` | no |
38 | | [local\_import\_custom\_routes](#input\_local\_import\_custom\_routes) | Import custom routes setting for 'local->peer' direction. | `bool` | `false` | no |
39 | | [local\_import\_subnet\_routes\_with\_public\_ip](#input\_local\_import\_subnet\_routes\_with\_public\_ip) | Import subnet routes with public IP setting for 'local->peer' direction. | `bool` | `false` | no |
40 | | [local\_network](#input\_local\_network) | Self-link or id of the first network (local) in pair. | `string` | n/a | yes |
41 | | [local\_peering\_name](#input\_local\_peering\_name) | Name for 'local->peer' direction peering resource. If not specified defaults to `-`. | `string` | `null` | no |
42 | | [name\_prefix](#input\_name\_prefix) | Optional prefix for auto-generated peering resource names. | `string` | `""` | no |
43 | | [peer\_export\_custom\_routes](#input\_peer\_export\_custom\_routes) | Export custom routes setting for 'peer->local' direction. | `bool` | `false` | no |
44 | | [peer\_export\_subnet\_routes\_with\_public\_ip](#input\_peer\_export\_subnet\_routes\_with\_public\_ip) | Export subnet routes with public IP setting for 'peer->local' direction. | `bool` | `false` | no |
45 | | [peer\_import\_custom\_routes](#input\_peer\_import\_custom\_routes) | Import custom routes setting for 'peer->local' direction. | `bool` | `false` | no |
46 | | [peer\_import\_subnet\_routes\_with\_public\_ip](#input\_peer\_import\_subnet\_routes\_with\_public\_ip) | Import subnet routes with public IP setting for 'peer->local' direction. | `bool` | `false` | no |
47 | | [peer\_network](#input\_peer\_network) | Self-link or id of the second network (peer) in pair. | `string` | n/a | yes |
48 | | [peer\_peering\_name](#input\_peer\_peering\_name) | Name for 'peer->local' direction peering resource. If not specified defaults to `-`. | `string` | `null` | no |
49 |
50 | ### Outputs
51 |
52 | No outputs.
53 |
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > [!WARNING]
2 | > This repository is now considered archived, and all future development will take place at our new location. For more details see https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/issues/236
3 |
4 | > [!IMPORTANT]
5 | > #### New Modules
6 | > - GitHub - https://github.com/PaloAltoNetworks/terraform-google-swfw-modules
7 | > - Terraform Registry - https://registry.terraform.io/modules/PaloAltoNetworks/swfw-modules/google/latest
8 |
9 | 
10 | 
11 | 
12 | 
13 | 
14 | 
15 | 
16 |
17 | # Terraform Modules for Palo Alto Networks VM-Series on Google Cloud Platform
18 |
19 | ## Overview
20 |
21 | A set of modules for using **Palo Alto Networks VM-Series firewalls** to provide control and protection
22 | to your applications running on Google Cloud Platform (GCP). It deploys VM-Series as virtual machine
23 | instances and it configures aspects such as Shared VPC connectivity, IAM access, Service Accounts, Panorama virtual
24 | machine instances, and more.
25 |
26 | The design is heavily based on the [Reference Architecture Guide for Google Cloud Platform](https://pandocs.tech/fw/160p-prime).
27 |
28 | For copyright and license see the LICENSE file.
29 |
30 | ## Structure
31 |
32 | This repository has the following directory structure:
33 |
34 | * [modules](./modules): This directory contains several standalone, reusable, production-grade Terraform modules. Each module is individually documented.
35 | * [examples](./examples): This directory shows examples of different ways to combine the modules contained in the
36 | `modules` directory.
37 |
38 | ## Compatibility
39 |
40 | The compatibility with Terraform is defined individually per each module. In general, expect the earliest compatible
41 | Terraform version to be 1.0.0 across most of the modules.
42 |
43 |
44 | ## Roadmap
45 |
46 | We are maintaining a [public roadmap](https://github.com/orgs/PaloAltoNetworks/projects/33/views/8) to help users understand when we will release new features, bug fixes and enhancements.
47 |
48 | ## Versioning
49 |
50 | These modules follow the principles of [Semantic Versioning](http://semver.org/). You can find each new release,
51 | along with the changelog, on the GitHub [Releases](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/releases) page.
52 |
53 | ## Getting Help
54 |
55 | [Open an issue](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/issues) on Github.
56 |
57 | ## Contributing
58 |
59 | Contributions are welcome, and they are greatly appreciated! Every little bit helps,
60 | and credit will always be given. Please follow our [contributing guide](https://github.com/PaloAltoNetworks/terraform-best-practices/blob/main/CONTRIBUTING.md).
61 |
62 |
66 |
--------------------------------------------------------------------------------
/modules/lb_http_ext_global/variables.tf:
--------------------------------------------------------------------------------
1 | variable "ip_version" {
2 | description = "IP version for the Global address (IPv4 or v6) - Empty defaults to IPV4"
3 | type = string
4 | default = ""
5 | }
6 |
7 | variable "name" {
8 | description = "Name for the forwarding rule and prefix for supporting resources"
9 | type = string
10 | }
11 |
12 | variable "backend_groups" {
13 | description = "The map containing the names of instance groups (IGs) or network endpoint groups (NEGs) to serve. The IGs can be managed or unmanaged or a mix of both. All IGs must handle named port `backend_port_name`. The NEGs just handle unnamed port."
14 | default = {}
15 | type = map(string)
16 | }
17 |
18 | variable "backend_port_name" {
19 | description = "The port_name of the backend groups that this load balancer will serve (default is 'http')"
20 | default = "http"
21 | type = string
22 | }
23 |
24 | variable "backend_protocol" {
25 | description = "The protocol used to talk to the backend service"
26 | default = "HTTP"
27 | type = string
28 | }
29 |
30 | variable "health_check_name" {
31 | description = "Name for the health check. If not provided, defaults to `-healthcheck`."
32 | default = null
33 | type = string
34 | }
35 |
36 | variable "health_check_port" {
37 | description = "TCP port to use for health check."
38 | default = 80
39 | type = number
40 | }
41 |
42 | variable "timeout_sec" {
43 | description = "Timeout to consider a connection dead, in seconds (default 30)"
44 | default = null
45 | type = number
46 | }
47 |
48 | variable "balancing_mode" {
49 | description = ""
50 | default = "RATE"
51 | type = string
52 | }
53 |
54 | variable "capacity_scaler" {
55 | description = ""
56 | default = null
57 | type = number
58 | }
59 |
60 | variable "max_connections_per_instance" {
61 | description = ""
62 | default = null
63 | type = number
64 | }
65 |
66 | variable "max_rate_per_instance" {
67 | description = ""
68 | default = null
69 | type = number
70 | }
71 |
72 | variable "max_utilization" {
73 | description = ""
74 | default = null
75 | type = number
76 | }
77 |
78 | variable "url_map" {
79 | description = "The url_map resource to use. Default is to send all traffic to first backend."
80 | type = string
81 | default = null
82 | }
83 |
84 | variable "http_forward" {
85 | description = "Set to `false` to disable HTTP port 80 forward"
86 | type = bool
87 | default = true
88 | }
89 |
90 | variable "custom_request_headers" {
91 | type = list(string)
92 | default = []
93 | description = "(Optional) Headers that the HTTP/S load balancer should add to proxied responses."
94 | }
95 | variable "ssl" {
96 | description = "Set to `true` to enable SSL support, requires variable `ssl_certificates` - a list of self_link certs"
97 | type = bool
98 | default = false
99 | }
100 |
101 | variable "private_key" {
102 | description = "Content of the private SSL key. Required if `ssl` is `true` and `ssl_certificates` is empty."
103 | type = string
104 | default = ""
105 | }
106 |
107 | variable "certificate" {
108 | description = "Content of the SSL certificate. Required if `ssl` is `true` and `ssl_certificates` is empty."
109 | type = string
110 | default = ""
111 | }
112 |
113 | variable "use_ssl_certificates" {
114 | description = "If true, use the certificates provided by `ssl_certificates`, otherwise, create cert from `private_key` and `certificate`"
115 | type = bool
116 | default = false
117 | }
118 |
119 | variable "ssl_certificates" {
120 | description = "SSL cert self_link list. Required if `ssl` is `true` and no `private_key` and `certificate` is provided."
121 | type = list(string)
122 | default = []
123 | }
124 |
125 | variable "security_policy" {
126 | description = "The resource URL for the security policy to associate with the backend service"
127 | type = string
128 | default = ""
129 | }
130 |
131 | variable "cdn" {
132 | description = "Set to `true` to enable cdn on backend."
133 | type = bool
134 | default = false
135 | }
136 |
--------------------------------------------------------------------------------
/modules/bootstrap/README.md:
--------------------------------------------------------------------------------
1 | # Google Cloud Storage Bucket For Initial Boot Of Palo Alto Networks VM-Series
2 |
3 | ## Reference
4 |
5 | ### Requirements
6 |
7 | | Name | Version |
8 | |------|---------|
9 | | [terraform](#requirement\_terraform) | >= 1.3, < 2.0 |
10 | | [google](#requirement\_google) | ~> 4.54 |
11 |
12 | ### Providers
13 |
14 | | Name | Version |
15 | |------|---------|
16 | | [google](#provider\_google) | ~> 4.54 |
17 | | [random](#provider\_random) | n/a |
18 |
19 | ### Modules
20 |
21 | No modules.
22 |
23 | ### Resources
24 |
25 | | Name | Type |
26 | |------|------|
27 | | [google_storage_bucket.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket) | resource |
28 | | [google_storage_bucket_iam_member.member](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_member) | resource |
29 | | [google_storage_bucket_object.config_empty](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_object) | resource |
30 | | [google_storage_bucket_object.content_empty](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_object) | resource |
31 | | [google_storage_bucket_object.file](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_object) | resource |
32 | | [google_storage_bucket_object.license_empty](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_object) | resource |
33 | | [google_storage_bucket_object.software_empty](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_object) | resource |
34 | | [random_string.randomstring](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
35 | | [google_compute_default_service_account.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_default_service_account) | data source |
36 |
37 | ### Inputs
38 |
39 | | Name | Description | Type | Default | Required |
40 | |------|-------------|------|---------|:--------:|
41 | | [bootstrap\_files\_dir](#input\_bootstrap\_files\_dir) | Bootstrap file directory. If the variable has a value of `null` (default) - then it will not upload any other files other than the ones specified in the `files` variable.
More information can be found at https://docs.paloaltonetworks.com/vm-series/9-1/vm-series-deployment/bootstrap-the-vm-series-firewall/bootstrap-package. | `string` | `null` | no |
42 | | [files](#input\_files) | Map of all files to copy to bucket. The keys are local paths, the values are remote paths. For example `{"dir/my.txt" = "config/init-cfg.txt"}` | `map(string)` | `{}` | no |
43 | | [folders](#input\_folders) | List of folder paths that will be used to create dedicated boostrap package folder sets per firewall or firewall group (for example to distinguish configuration per region, per inbound/obew role, etc) within the created storage bucket.
A default value (empty list) will result in the creation of a single bootstrap package folder set in the bucket top-level directory. | `list(any)` | `[]` | no |
44 | | [location](#input\_location) | Location in which the GCS Bucket will be deployed. Available locations can be found under https://cloud.google.com/storage/docs/locations. | `string` | n/a | yes |
45 | | [name\_prefix](#input\_name\_prefix) | Prefix of the name of Google Cloud Storage bucket, followed by 10 random characters | `string` | `"paloaltonetworks-firewall-bootstrap-"` | no |
46 | | [service\_account](#input\_service\_account) | Optional IAM Service Account (just an email) that will be granted read-only access to this bucket | `string` | `null` | no |
47 |
48 | ### Outputs
49 |
50 | | Name | Description |
51 | |------|-------------|
52 | | [bucket](#output\_bucket) | n/a |
53 | | [bucket\_name](#output\_bucket\_name) | n/a |
54 |
55 |
--------------------------------------------------------------------------------
/modules/vmseries/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | create_public_ip = {
3 | for k, v in var.network_interfaces : k => try(v.create_public_ip, false)
4 | }
5 | access_configs = {
6 | for k, v in var.network_interfaces : k => {
7 | nat_ip = try(v.public_ip, google_compute_address.public[k].address, null)
8 | public_ptr_domain_name = try(v.public_ptr_domain_name, google_compute_address.public[k].public_ptr_domain_name, null)
9 | }
10 | if try(v.public_ip, null) != null || local.create_public_ip[k]
11 | }
12 | }
13 |
14 | data "google_compute_image" "vmseries" {
15 | count = var.custom_image == null ? 1 : 0
16 |
17 | name = var.vmseries_image
18 | project = "paloaltonetworksgcp-public"
19 | }
20 |
21 | data "google_compute_subnetwork" "this" {
22 | for_each = { for k, v in var.network_interfaces : k => v }
23 | project = var.project
24 | self_link = each.value.subnetwork
25 | }
26 |
27 | resource "null_resource" "dependency_getter" {
28 | provisioner "local-exec" {
29 | command = "echo ${length(var.dependencies)}"
30 | }
31 | }
32 |
33 | resource "google_compute_address" "private" {
34 | for_each = { for k, v in var.network_interfaces : k => v }
35 |
36 | name = try(each.value.private_ip_name, "${var.name}-${each.key}-private")
37 | address_type = "INTERNAL"
38 | address = try(each.value.private_ip, null)
39 | project = var.project
40 | subnetwork = each.value.subnetwork
41 | region = data.google_compute_subnetwork.this[each.key].region
42 | }
43 |
44 | resource "google_compute_address" "public" {
45 | for_each = { for k, v in var.network_interfaces : k => v if local.create_public_ip[k] && try(v.public_ip, null) == null }
46 |
47 | name = try(each.value.public_ip_name, "${var.name}-${each.key}-public")
48 | address_type = "EXTERNAL"
49 | project = var.project
50 | region = data.google_compute_subnetwork.this[each.key].region
51 | }
52 |
53 | resource "google_compute_instance" "this" {
54 |
55 | name = var.name
56 | zone = var.zone
57 | machine_type = var.machine_type
58 | min_cpu_platform = var.min_cpu_platform
59 | deletion_protection = var.deletion_protection
60 | labels = var.labels
61 | tags = var.tags
62 | metadata_startup_script = var.metadata_startup_script
63 | project = var.project
64 | resource_policies = var.resource_policies
65 | can_ip_forward = true
66 | allow_stopping_for_update = true
67 |
68 | metadata = merge({
69 | serial-port-enable = true
70 | ssh-keys = var.ssh_keys
71 | },
72 | var.bootstrap_options,
73 | var.metadata
74 | )
75 |
76 | service_account {
77 | email = var.service_account
78 | scopes = var.scopes
79 | }
80 |
81 | dynamic "network_interface" {
82 | for_each = var.network_interfaces
83 |
84 | content {
85 | network_ip = google_compute_address.private[network_interface.key].address
86 | subnetwork = network_interface.value.subnetwork
87 |
88 | dynamic "access_config" {
89 | for_each = try(local.access_configs[network_interface.key] != null, false) ? ["one"] : []
90 | content {
91 | nat_ip = local.access_configs[network_interface.key].nat_ip
92 | public_ptr_domain_name = local.access_configs[network_interface.key].public_ptr_domain_name
93 | }
94 | }
95 |
96 | dynamic "alias_ip_range" {
97 | for_each = try(network_interface.value.alias_ip_ranges, [])
98 | content {
99 | ip_cidr_range = alias_ip_ranges.value.ip_cidr_range
100 | subnetwork_range_name = try(alias_ip_ranges.value.subnetwork_range_name, null)
101 | }
102 | }
103 | }
104 | }
105 |
106 | boot_disk {
107 | initialize_params {
108 | image = coalesce(var.custom_image, try(data.google_compute_image.vmseries[0].self_link, null))
109 | type = var.disk_type
110 | }
111 | }
112 |
113 | depends_on = [
114 | null_resource.dependency_getter
115 | ]
116 | }
117 |
118 | # The Deployment Guide Jan 2020 recommends per-zone instance groups (instead of regional IGMs).
119 | resource "google_compute_instance_group" "this" {
120 | count = var.create_instance_group ? 1 : 0
121 |
122 | name = "${var.name}-${var.zone}"
123 | zone = var.zone
124 | project = var.project
125 | instances = [google_compute_instance.this.self_link]
126 |
127 | dynamic "named_port" {
128 | for_each = var.named_ports
129 | content {
130 | name = named_port.value.name
131 | port = named_port.value.port
132 | }
133 | }
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/examples/standalone_vmseries_with_metadata_bootstrap/variables.tf:
--------------------------------------------------------------------------------
1 | # General
2 | variable "project" {
3 | description = "The project name to deploy the infrastructure in to."
4 | type = string
5 | default = null
6 | }
7 | variable "name_prefix" {
8 | description = "A string to prefix resource namings"
9 | type = string
10 | default = ""
11 | }
12 |
13 | # VPC
14 | variable "networks" {
15 | description = <<-EOF
16 | A map containing each network setting.
17 |
18 | Example of variable deployment :
19 |
20 | ```
21 | networks = {
22 | "vmseries-vpc" = {
23 | vpc_name = "firewall-vpc"
24 | create_network = true
25 | delete_default_routes_on_create = "false"
26 | mtu = "1460"
27 | routing_mode = "REGIONAL"
28 | subnetworks = {
29 | "vmseries-sub" = {
30 | name = "vmseries-subnet"
31 | create_subnetwork = true
32 | ip_cidr_range = "172.21.21.0/24"
33 | region = "us-central1"
34 | }
35 | }
36 | firewall_rules = {
37 | "allow-vmseries-ingress" = {
38 | name = "vmseries-mgmt"
39 | source_ranges = ["1.1.1.1/32", "2.2.2.2/32"]
40 | priority = "1000"
41 | allowed_protocol = "all"
42 | allowed_ports = []
43 | }
44 | }
45 | }
46 | ```
47 |
48 | For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/vpc#input_networks)
49 |
50 | Multiple keys can be added and will be deployed by the code
51 | EOF
52 | }
53 |
54 | variable "vmseries" {
55 | description = <<-EOF
56 | A map containing each individual vmseries setting.
57 |
58 | Example of variable deployment :
59 |
60 | ```
61 | vmseries = {
62 | "fw-vmseries-01" = {
63 | name = "fw-vmseries-01"
64 | zone = "us-central1-b"
65 | vmseries_image = "vmseries-flex-byol-1022h2"
66 | ssh_keys = "admin:"
67 | machine_type = "n2-standard-4"
68 | min_cpu_platform = "Intel Cascade Lake"
69 | tags = ["vmseries"]
70 | scopes = [
71 | "https://www.googleapis.com/auth/compute.readonly",
72 | "https://www.googleapis.com/auth/cloud.useraccounts.readonly",
73 | "https://www.googleapis.com/auth/devstorage.read_only",
74 | "https://www.googleapis.com/auth/logging.write",
75 | "https://www.googleapis.com/auth/monitoring.write",
76 | ]
77 | bootstrap_options = {
78 | panorama-server = "1.1.1.1" # Modify this value as per deployment requirements
79 | dns-primary = "8.8.8.8" # Modify this value as per deployment requirements
80 | dns-secondary = "8.8.4.4" # Modify this value as per deployment requirements
81 | }
82 | named_ports = [
83 | {
84 | name = "http"
85 | port = 80
86 | },
87 | {
88 | name = "https"
89 | port = 443
90 | }
91 | ]
92 | network_interfaces = [
93 | {
94 | vpc_network_key = "vmseries-vpc"
95 | subnetwork_key = "fw-mgmt-sub"
96 | private_ip = "10.10.10.2"
97 | create_public_ip = true
98 | }
99 | ]
100 | }
101 | }
102 | ```
103 | For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/vmseries#inputs)
104 |
105 | The bootstrap_template_map contains variables that will be applied to the bootstrap template. Each firewall Day 0 bootstrap will be parametrised based on these inputs.
106 | Multiple keys can be added and will be deployed by the code.
107 |
108 | EOF
109 | }
110 |
111 | variable "vmseries_common" {
112 | description = <<-EOF
113 | A map containing common vmseries setting.
114 |
115 | Example of variable deployment :
116 |
117 | ```
118 | vmseries_common = {
119 | ssh_keys = "admin:AAAABBBB..."
120 | vmseries_image = "vmseries-flex-byol-1022h2"
121 | machine_type = "n2-standard-4"
122 | min_cpu_platform = "Intel Cascade Lake"
123 | service_account_key = "sa-vmseries-01"
124 | bootstrap_options = {
125 | type = "dhcp-client"
126 | mgmt-interface-swap = "enable"
127 | }
128 | }
129 | ```
130 |
131 | Bootstrap options can be moved between vmseries individual instance variable (`vmseries`) and this common vmserie variable (`vmseries_common`).
132 | EOF
133 | default = {}
134 | }
--------------------------------------------------------------------------------
/modules/lb_external/variables.tf:
--------------------------------------------------------------------------------
1 | variable "project" {
2 | description = "The project to deploy to. If unset the default provider project is used."
3 | type = string
4 | default = ""
5 | }
6 |
7 | variable "region" {
8 | description = "GCP region to deploy to. If unset the default provider region is used."
9 | type = string
10 | default = null
11 | }
12 |
13 | variable "name" {
14 | description = "Name of the backend_service, target_pool and of the associated health check."
15 | type = string
16 | }
17 |
18 | variable "rules" {
19 | description = <<-EOF
20 | Map of objects, the keys are names of the external forwarding rules, each of the objects has the following attributes:
21 |
22 | - `port_range`: (Required) The port your service is listening on. Can be a number (80) or a range (8080-8089, or even 1-65535).
23 | - `ip_address`: (Optional) A public IP address on which to listen, must be in the same region as the LB and must be IPv4. If empty, automatically generates a new non-ephemeral IP on a PREMIUM tier.
24 | - `ip_protocol`: (Optional) The IP protocol for the frontend forwarding rule: TCP, UDP, ESP, ICMP, or L3_DEFAULT. Default is TCP.
25 | - `all_ports`: (Optional) Allows all ports to be forwarded to the Backend Service
26 |
27 | EOF
28 | }
29 |
30 | variable "instances" {
31 | description = "List of links to the instances. Expected to be empty when using an autoscaler, as the autoscaler inserts entries to the target pool dynamically. The nic0 of each instance gets the traffic. Even when this list is shifted or re-ordered, it doesn't re-create any resources and such modifications often proceed without any noticeable downtime."
32 | type = list(string)
33 | default = null
34 | }
35 |
36 | variable "backend_instance_groups" {
37 | description = "List of backend instance groups"
38 | default = []
39 | }
40 |
41 | variable "session_affinity" {
42 | description = <<-EOF
43 | Controls distribution of new connections (or fragmented UDP packets) from clients to the backends, can influence available connection tracking configurations.
44 | Valid values are: NONE (default), CLIENT_IP, CLIENT_IP_PROTO, CLIENT_IP_PORT_PROTO (only available for backend service based rules).
45 | EOF
46 | type = string
47 | default = "NONE"
48 | }
49 |
50 | variable "connection_tracking_policy" {
51 | description = <<-EOF
52 | Connection tracking policy settings, only available for backend service based rules. Following options are available:
53 | - `mode` - (Optional|string) `PER_CONNECTION` (default) or `PER_SESSION`
54 | - `persistence_on_unhealthy_backends` - (Optional|string) `DEFAULT_FOR_PROTOCOL` (default), `ALWAYS_PERSIST` or `NEVER_PERSIST`
55 |
56 | More information about supported configurations in conjunction with `session_affinity` is available in [Backend service-based external Network Load Balancing](https://cloud.google.com/load-balancing/docs/network/networklb-backend-service#connection-tracking) documentation.
57 | EOF
58 | default = null
59 | type = map(any)
60 | }
61 |
62 | variable "network_tier" {
63 | description = "The networking tier used for configuring this address. If this field is not specified, it is assumed to be PREMIUM. Possible values are PREMIUM and STANDARD."
64 | type = string
65 | default = "PREMIUM"
66 | }
67 |
68 | variable "create_health_check" {
69 | description = "Whether to create a health check on the target pool."
70 | type = bool
71 | default = true
72 | }
73 |
74 | variable "health_check_interval_sec" {
75 | description = "Health check parameter, see [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_http_health_check)"
76 | type = number
77 | default = null
78 | }
79 |
80 | variable "health_check_healthy_threshold" {
81 | description = "Health check parameter, see [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_http_health_check)"
82 | type = number
83 | default = null
84 | }
85 |
86 | variable "health_check_timeout_sec" {
87 | description = "Health check parameter, see [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_http_health_check)"
88 | type = number
89 | default = null
90 | }
91 |
92 | variable "health_check_unhealthy_threshold" {
93 | description = "Health check parameter, see [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_http_health_check)"
94 | type = number
95 | default = null
96 | }
97 |
98 | variable "health_check_http_port" {
99 | description = "Health check parameter, see [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_http_health_check)"
100 | type = number
101 | default = null
102 | }
103 |
104 | variable "health_check_http_request_path" {
105 | description = "Health check http request path, with the default adjusted to /php/login.php to be able to check the health of the PAN-OS webui."
106 | type = string
107 | default = "/php/login.php"
108 | }
109 |
110 | variable "health_check_http_host" {
111 | description = "Health check http request host header, with the default adjusted to localhost to be able to check the health of the PAN-OS webui."
112 | type = string
113 | default = "localhost"
114 | }
115 |
--------------------------------------------------------------------------------
/modules/lb_internal/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | description = "Name of the load balancer (that is, both the forwarding rule and the backend service)"
3 | type = string
4 | }
5 |
6 | variable "project" {
7 | description = "The project to deploy to. If unset the default provider project is used."
8 | type = string
9 | default = null
10 | }
11 |
12 | variable "region" {
13 | description = "Region to create ILB in."
14 | type = string
15 | default = null
16 | }
17 |
18 | variable "health_check_port" {
19 | description = "(Optional) Port number for TCP healthchecking, default 22. This setting is ignored when `health_check` is provided."
20 | default = 22
21 | type = number
22 | }
23 |
24 | variable "health_check" {
25 | description = "(Optional) Name of either the global google_compute_health_check or google_compute_region_health_check to use. Conflicts with health_check_port."
26 | default = null
27 | type = string
28 | }
29 |
30 | variable "backends" {
31 | description = "Names of primary backend groups (IGs or IGMs). Typically use `module.vmseries.instance_group_self_links` here."
32 | type = map(string)
33 | }
34 |
35 | variable "failover_backends" {
36 | description = "(Optional) Names of failover backend groups (IGs or IGMs). Failover groups are ignored unless the primary groups do not meet collective health threshold."
37 | default = {}
38 | type = map(string)
39 | }
40 |
41 | variable "subnetwork" {
42 | type = string
43 | }
44 |
45 | variable "ip_address" {
46 | default = null
47 | }
48 |
49 | variable "ip_protocol" {
50 | description = "The IP protocol for the frontend forwarding rule, valid values are TCP and UDP."
51 | default = "TCP"
52 | type = string
53 | }
54 |
55 | variable "all_ports" {
56 | description = "Forward all ports of the ip_protocol from the frontend to the backends. Needs to be null if `ports` are provided."
57 | default = null
58 | type = bool
59 | }
60 |
61 | variable "ports" {
62 | description = "Which port numbers are forwarded to the backends (up to 5 ports). Conflicts with all_ports."
63 | default = []
64 | type = list(number)
65 | }
66 |
67 | variable "network" {
68 | default = null
69 | }
70 |
71 | variable "session_affinity" {
72 | description = <<-EOF
73 | Controls distribution of new connections (or fragmented UDP packets) from clients to the backends, can influence available connection tracking configurations.
74 | Valid values are: NONE (default), CLIENT_IP_NO_DESTINATION, CLIENT_IP, CLIENT_IP_PROTO, CLIENT_IP_PORT_PROTO.
75 | EOF
76 | default = null
77 | type = string
78 | }
79 |
80 | variable "connection_tracking_policy" {
81 | description = <<-EOF
82 | Connection tracking policy settings. Following options are available:
83 | - `mode` - (Optional|string) `PER_CONNECTION` (default) or `PER_SESSION`
84 | - `idle_timeout_sec` - (Optional|number) Defaults to 600 seconds, can only be modified in specific conditions (see link below)
85 | - `persistence_on_unhealthy_backends` - (Optional|string) `DEFAULT_FOR_PROTOCOL` (default), `ALWAYS_PERSIST` or `NEVER_PERSIST`
86 |
87 | More information about supported configurations in conjunction with `session_affinity` is available in [Internal TCP/UDP Load Balancing](https://cloud.google.com/load-balancing/docs/internal#connection-tracking) documentation.
88 | EOF
89 | default = null
90 | type = map(any)
91 | }
92 |
93 | variable "timeout_sec" {
94 | description = "(Optional) How many seconds to wait for the backend before dropping the connection. Default is 30 seconds. Valid range is [1, 86400]."
95 | default = null
96 | type = number
97 | }
98 |
99 | variable "disable_connection_drain_on_failover" {
100 | description = "(Optional) On failover or failback, this field indicates whether connection drain will be honored. Setting this to true has the following effect: connections to the old active pool are not drained. Connections to the new active pool use the timeout of 10 min (currently fixed). Setting to false has the following effect: both old and new connections will have a drain timeout of 10 min. This can be set to true only if the protocol is TCP. The default is false."
101 | default = null
102 | type = bool
103 | }
104 |
105 | variable "drop_traffic_if_unhealthy" {
106 | description = "(Optional) Used only when no healthy VMs are detected in the primary and backup instance groups. When set to true, traffic is dropped. When set to false, new connections are sent across all VMs in the primary group. The default is false."
107 | default = null
108 | type = bool
109 | }
110 |
111 | variable "failover_ratio" {
112 | description = "(Optional) The value of the field must be in [0, 1]. If the ratio of the healthy VMs in the primary backend is at or below this number, traffic arriving at the load-balanced IP will be directed to the failover_backends. In case where 'failoverRatio' is not set or all the VMs in the backup backend are unhealthy, the traffic will be directed back to the primary backend in the `force` mode, where traffic will be spread to the healthy VMs with the best effort, or to all VMs when no VM is healthy. This field is only used with l4 load balancing."
113 | default = null
114 | type = number
115 | }
116 |
117 | variable "allow_global_access" {
118 | description = "(Optional) If true, clients can access ILB from all regions. By default false, only allow from the ILB's local region; useful if the ILB is a next hop of a route."
119 | default = false
120 | type = bool
121 | }
122 |
123 | variable "connection_draining_timeout_sec" {
124 | type = number
125 | description = "(Optional) Time for which instance will be drained (not accept new connections, but still work to finish started)."
126 | default = null
127 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are welcome, and they are greatly appreciated! Every little bit helps,
4 | and credit will always be given.
5 |
6 | ## Areas of contribution
7 |
8 | Contributions are welcome across the entire project:
9 |
10 | - Code
11 | - Documentation
12 | - Testing
13 | - Packaging/distribution
14 |
15 | ## Contributing workflow
16 |
17 | ### New Contributors
18 |
19 | 1. Search the [issues](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules.git/issues) to see if there is an existing issue. If not, please open one.
20 |
21 | 1. Fork the repository to your personal namespace (only needed to do this once).
22 |
23 | 1. Clone the repo from your personal namespace.
24 |
25 | `git clone https://github.com/{username}/terraform-google-vmseries-modules.git`
26 | Ensure that `{username}` is _your_ user name.
27 |
28 | 1. Add the source repository as an upsteam.
29 |
30 | `git remote add upstream https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules.git`
31 |
32 | 1. Create a branch which corresponds to the issue ID created in step 1.
33 |
34 | For example, if the issue ID is 101:
35 | `git checkout -b 101-updating-wildfire-templates`
36 |
37 | 1. Make the desired changes and commit to your local repository.
38 |
39 | 1. Run the `pre-commit` script. See the [tools](#tools) section for more information.
40 | *NOTE* If making changes that will update the Terraform docs, this will need to be run twice.
41 |
42 | 1. Push changes to _your_ repository
43 |
44 | `git push origin/101-updating-wildfire-templates`
45 |
46 | 1. Rebase with the upstream to resolve any potential conflicts.
47 |
48 | `git rebase upstream dev`
49 |
50 | 1. Open a Pull Request and link it to the issue (reference the issue, i.e. "fixes #233")
51 |
52 | 1. Once the PR has been merged, delete your local branch
53 |
54 | `git branch -D 101-updating-wildfire-templates`
55 |
56 | ### Existing Contributors
57 |
58 | 1. Search the [issues](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules.git/issues) to see if there is an existing issue. If not, open an issue (note the issue ID).
59 | 1. Update from the source repository.
60 |
61 | `git pull upstream dev`
62 |
63 | 1. Create a branch which corresponds to the issue ID created in step 1.
64 |
65 | For example, if the issue ID is 101:
66 | `git checkout -b 101-updating-wildfire-templates`
67 |
68 | 1. Make any changes, and ensure the commit messages are clear and consistent (reference the issue ID and type of change in all commit messages)
69 |
70 | 1. Document the changes (update the README and any other relevant documents)
71 |
72 | 1. Run the `pre-commit` script. See the [tools](#tools) section for more information.
73 | *NOTE* If making changes that will update the Terraform docs, this will need to be run twice.
74 |
75 | 1. Push changes to _your_ repository
76 |
77 | `git push origin/101-updating-wildfire-templates`
78 | 1. Rebase with the upstream to resolve any potential conflicts.
79 |
80 | `git rebase upstream dev`
81 |
82 | 1. Open a Pull Request and link it to the issue (reference the issue, i.e. "fixes #233")
83 |
84 | 1. Once the PR has been merged, delete your local branch
85 |
86 | `git branch -D 101-updating-wildfire-templates`
87 |
88 | ## Tools
89 |
90 | Any serious changes, especially any changes of variables or providers, require the
91 | `pre-commit` tool. Install the recommended versions:
92 |
93 | - pre-commit 2.9.3 - [installation instruction](https://pre-commit.com/#installation) (a Python3 package)
94 | - terraform-docs 0.12.1 - download the binary from [GitHub releases](https://github.com/terraform-docs/terraform-docs/releases)
95 | - tflint 0.20.2 - download the binary from [GitHub releases](https://github.com/terraform-linters/tflint/releases)
96 | - coreutils - required only on macOS (due to use of `realpath`), simply execute `brew install coreutils`
97 |
98 | For more details and a Docker-compatible alternative see the [official guide](https://github.com/antonbabenko/pre-commit-terraform#how-to-install) of the author of pre-commit-terraform, Anton Babenko.
99 |
100 | For these Contributors who prefer *not* to use the recommended git hooks, the command
101 | to fully update the auto-generated README files and to run formatters/tests:
102 |
103 | ```sh
104 | pre-commit run -a
105 | ```
106 |
107 | This command does not commit/add/push any changes to Git. It only changes local files.
108 |
109 | The first time `pre-commit` is run, it is possible to show "FAILED" if any docs were updated. This is expected behavior. Simply run `pre-commit` again and it should pass. Once all pre-commit tests pass, make another commit to check in those changes and push.
110 |
111 | ## Coding Standards
112 |
113 | Please follow the [Terraform conventions](https://github.com/PaloAltoNetworks/terraform-best-practices/blob/master/README.md).
114 |
115 | ## Publish a new release (for maintainers)
116 |
117 | ### Test the release process
118 |
119 | Testing the workflow requires node, npm, and semantic-release to be installed locally:
120 |
121 | ```sh
122 | npm install -g semantic-release@^17.1.1 @semantic-release/git@^9.0.0 @semantic-release/exec@^5.0.0 conventional-changelog-conventionalcommits@^4.4.0
123 | ```
124 |
125 | Run `semantic-release` on develop:
126 |
127 | ```sh
128 | semantic-release --dry-run --no-ci --branches=develop
129 | ```
130 |
131 | Verify in the output that the next version is set correctly, and the release notes are generated correctly.
132 |
133 | ### Merge develop to master and push
134 |
135 | ```sh
136 | git checkout master
137 | git merge develop
138 | git push origin master
139 | ```
140 |
141 | At this point, GitHub Actions builds and tags the release.
142 |
143 | ### Merge master to develop and push
144 |
145 | Now, sync develop to master to add any commits made by the release bot.
146 |
147 | ```sh
148 | git fetch --all --tags
149 | git pull origin master
150 | git checkout develop
151 | git merge master
152 | git push origin develop
153 | ```
154 |
--------------------------------------------------------------------------------
/examples/standalone_vmseries_with_metadata_bootstrap/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | show_in_hub: false
3 | ---
4 | # Palo Alto Networks VM-Series NGFW Module Example
5 |
6 | A Terraform module example for deploying a VM-Series NGFW in GCP using the [metadata](https://docs.paloaltonetworks.com/vm-series/10-2/vm-series-deployment/bootstrap-the-vm-series-firewall/choose-a-bootstrap-method#idf6412176-e973-488e-9d7a-c568fe1e33a9) bootstrap method.
7 |
8 | This example can be used to familarize oneself with both the VM-Series NGFW and Terraform - it creates a single instance of virtualized firewall in a Security VPC with a management-only interface and lacks any traffic inspection.
9 |
10 | ## Reference
11 |
12 | ### Requirements
13 |
14 | | Name | Version |
15 | |------|---------|
16 | | [terraform](#requirement\_terraform) | >= 1.3, < 2.0 |
17 |
18 | ### Providers
19 |
20 | No providers.
21 |
22 | ### Modules
23 |
24 | | Name | Source | Version |
25 | |------|--------|---------|
26 | | [vmseries](#module\_vmseries) | ../../modules/vmseries | n/a |
27 | | [vpc](#module\_vpc) | ../../modules/vpc | n/a |
28 |
29 | ### Resources
30 |
31 | No resources.
32 |
33 | ### Inputs
34 |
35 | | Name | Description | Type | Default | Required |
36 | |------|-------------|------|---------|:--------:|
37 | | [name\_prefix](#input\_name\_prefix) | A string to prefix resource namings | `string` | `""` | no |
38 | | [networks](#input\_networks) | A map containing each network setting.
Example of variable deployment :networks = {
"vmseries-vpc" = {
vpc_name = "firewall-vpc"
create_network = true
delete_default_routes_on_create = "false"
mtu = "1460"
routing_mode = "REGIONAL"
subnetworks = {
"vmseries-sub" = {
name = "vmseries-subnet"
create_subnetwork = true
ip_cidr_range = "172.21.21.0/24"
region = "us-central1"
}
}
firewall_rules = {
"allow-vmseries-ingress" = {
name = "vmseries-mgmt"
source_ranges = ["1.1.1.1/32", "2.2.2.2/32"]
priority = "1000"
allowed_protocol = "all"
allowed_ports = []
}
}
}For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/vpc#input_networks)
Multiple keys can be added and will be deployed by the code | `any` | n/a | yes |
39 | | [project](#input\_project) | The project name to deploy the infrastructure in to. | `string` | `null` | no |
40 | | [vmseries](#input\_vmseries) | A map containing each individual vmseries setting.
Example of variable deployment :vmseries = {
"fw-vmseries-01" = {
name = "fw-vmseries-01"
zone = "us-central1-b"
vmseries_image = "vmseries-flex-byol-1022h2"
ssh_keys = "admin:"
machine_type = "n2-standard-4"
min_cpu_platform = "Intel Cascade Lake"
tags = ["vmseries"]
scopes = [
"https://www.googleapis.com/auth/compute.readonly",
"https://www.googleapis.com/auth/cloud.useraccounts.readonly",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring.write",
]
bootstrap_options = {
panorama-server = "1.1.1.1" # Modify this value as per deployment requirements
dns-primary = "8.8.8.8" # Modify this value as per deployment requirements
dns-secondary = "8.8.4.4" # Modify this value as per deployment requirements
}
named_ports = [
{
name = "http"
port = 80
},
{
name = "https"
port = 443
}
]
network_interfaces = [
{
vpc_network_key = "vmseries-vpc"
subnetwork_key = "fw-mgmt-sub"
private_ip = "10.10.10.2"
create_public_ip = true
}
]
}
}For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/vmseries#inputs)
The bootstrap\_template\_map contains variables that will be applied to the bootstrap template. Each firewall Day 0 bootstrap will be parametrised based on these inputs.
Multiple keys can be added and will be deployed by the code. | `any` | n/a | yes |
41 | | [vmseries\_common](#input\_vmseries\_common) | A map containing common vmseries setting.
Example of variable deployment :vmseries_common = {
ssh_keys = "admin:AAAABBBB..."
vmseries_image = "vmseries-flex-byol-1022h2"
machine_type = "n2-standard-4"
min_cpu_platform = "Intel Cascade Lake"
service_account_key = "sa-vmseries-01"
bootstrap_options = {
type = "dhcp-client"
mgmt-interface-swap = "enable"
}
}Bootstrap options can be moved between vmseries individual instance variable (`vmseries`) and this common vmserie variable (`vmseries_common`). | `map` | `{}` | no |
42 |
43 | ### Outputs
44 |
45 | | Name | Description |
46 | |------|-------------|
47 | | [vmseries\_private\_ips](#output\_vmseries\_private\_ips) | Private IP addresses of the vmseries instances. |
48 | | [vmseries\_public\_ips](#output\_vmseries\_public\_ips) | Public IP addresses of the vmseries instances. |
49 |
--------------------------------------------------------------------------------
/modules/lb_external/main.tf:
--------------------------------------------------------------------------------
1 | data "google_client_config" "this" {}
2 |
3 | locals {
4 | # If we were told an exact region, use it, otherwise fall back to a client-default region
5 | region = coalesce(var.region, data.google_client_config.this.region)
6 |
7 | # Check for `L3_DEFAULT` as this requires `google_compute_region_backend_service` and `google_compute_region_health_check` resources.
8 | backend_service_needed = contains([for k, v in var.rules : lookup(v, "ip_protocol", null)], "L3_DEFAULT")
9 |
10 | # Check for protocols that require a `google_compute_target_pool` backend and `google_compute_http_health_check` health check
11 | target_pool_protocols = ["TCP", "UDP", "ESP", "AH", "SCTP", "ICMP"]
12 | target_pool_needed = contains([for k, v in var.rules : contains(local.target_pool_protocols, lookup(v, "ip_protocol", "TCP"))], true)
13 | }
14 |
15 | # Create external IP addresses if non-specified
16 | resource "google_compute_address" "this" {
17 | for_each = { for k, v in var.rules : k => v if !can(v.ip_address) }
18 |
19 | name = each.key
20 | address_type = "EXTERNAL"
21 | region = var.region
22 | project = var.project
23 | }
24 |
25 | # Create forwarding rule for each specified rule
26 | resource "google_compute_forwarding_rule" "rule" {
27 | for_each = var.rules
28 |
29 | name = each.key
30 | project = var.project
31 | region = local.region
32 |
33 | # Check if `ip_protocol` is specified (if not assume default of `TCP`) != `L3_DEFAULT` if true then use `google_compute_target_pool` as backend
34 | target = lookup(each.value, "ip_protocol", "TCP") != "L3_DEFAULT" ? google_compute_target_pool.this[0].self_link : null
35 |
36 | # Check if `ip_protocol` is specified (if not assume default of `TCP`) == `L3_DEFAULT` if true then use `google_compute_region_backend_service` as backend
37 | backend_service = lookup(each.value, "ip_protocol", "TCP") == "L3_DEFAULT" ? google_compute_region_backend_service.this[0].self_link : null
38 | load_balancing_scheme = "EXTERNAL"
39 |
40 | # Check if `ip_protocol` is specified (if not assume default of `TCP`) == `L3_DEFAULT`.
41 | # If true then set `all_ports` to `true`.
42 | # If false set value to the value of `all_ports`. If `all_ports` isn't specified, then set the value to `null`.
43 | all_ports = lookup(each.value, "ip_protocol", "TCP") == "L3_DEFAULT" ? true : lookup(each.value, "all_ports", null)
44 |
45 | # Check if `ip_protocol` is specified (if not assume default of `TCP`) == `L3_DEFAULT`.
46 | # If true then set `port_range` to `null`.
47 | # If false set value to the value of `port_range`. If `port_range` isn't specified, then set the value to `null`.
48 | port_range = lookup(each.value, "ip_protocol", "TCP") == "L3_DEFAULT" ? null : lookup(each.value, "port_range", null)
49 |
50 | ip_address = try(each.value.ip_address, google_compute_address.this[each.key].address)
51 | ip_protocol = lookup(each.value, "ip_protocol", "TCP")
52 | }
53 |
54 | # Create `google_compute_target_pool` if required by `var.rules`
55 | resource "google_compute_target_pool" "this" {
56 | count = local.target_pool_needed ? 1 : 0
57 |
58 | name = var.name
59 | project = var.project
60 | region = local.region
61 |
62 | instances = var.instances
63 | health_checks = var.create_health_check ? [google_compute_http_health_check.this[0].self_link] : []
64 | session_affinity = var.session_affinity
65 |
66 | lifecycle {
67 | # Ignore changes because autoscaler changes this in the background.
68 | ignore_changes = [instances]
69 | }
70 | }
71 |
72 | # Create `google_compute_http_health_check` if required by `var.rules`
73 | resource "google_compute_http_health_check" "this" {
74 | count = var.create_health_check && local.target_pool_needed ? 1 : 0
75 |
76 | name = "${var.name}-${local.region}"
77 | check_interval_sec = var.health_check_interval_sec
78 | healthy_threshold = var.health_check_healthy_threshold
79 | timeout_sec = var.health_check_timeout_sec
80 | unhealthy_threshold = var.health_check_unhealthy_threshold
81 | port = var.health_check_http_port
82 | request_path = var.health_check_http_request_path
83 | host = var.health_check_http_host
84 | project = var.project
85 | }
86 |
87 | # Create `google_compute_region_backend_service` if require by `var.rules`
88 | resource "google_compute_region_backend_service" "this" {
89 | provider = google-beta
90 |
91 | count = local.backend_service_needed ? 1 : 0
92 |
93 | name = var.name
94 | project = var.project
95 | region = local.region
96 |
97 | load_balancing_scheme = "EXTERNAL"
98 | health_checks = var.create_health_check ? [google_compute_region_health_check.this[0].self_link] : []
99 | protocol = "UNSPECIFIED"
100 | session_affinity = var.session_affinity
101 |
102 | dynamic "backend" {
103 | for_each = var.backend_instance_groups
104 | content {
105 | group = backend.value
106 | }
107 | }
108 |
109 | # This feature requires beta provider as of 2023-03-16
110 | dynamic "connection_tracking_policy" {
111 | for_each = var.connection_tracking_policy != null ? ["this"] : []
112 | content {
113 | tracking_mode = try(var.connection_tracking_policy.mode, null)
114 | idle_timeout_sec = try(var.connection_tracking_policy.idle_timeout_sec, null)
115 | connection_persistence_on_unhealthy_backends = try(var.connection_tracking_policy.persistence_on_unhealthy_backends, null)
116 | }
117 | }
118 | }
119 |
120 | # Create `google_compute_region_backend_service` if require by `var.rules`
121 | resource "google_compute_region_health_check" "this" {
122 | count = var.create_health_check && local.backend_service_needed ? 1 : 0
123 |
124 | name = "${var.name}-${local.region}"
125 | project = var.project
126 | region = local.region
127 | check_interval_sec = var.health_check_interval_sec
128 | healthy_threshold = var.health_check_healthy_threshold
129 | timeout_sec = var.health_check_timeout_sec
130 | unhealthy_threshold = var.health_check_unhealthy_threshold
131 |
132 | http_health_check {
133 | port = var.health_check_http_port
134 | request_path = var.health_check_http_request_path
135 | host = var.health_check_http_host
136 | }
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/modules/vpn/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | secret = random_id.secret.b64_url
3 |
4 | tunnels_tmp = flatten([
5 | for vpn_instance_name, vpn_instance_config in var.vpn_config.instances : [
6 | for tunnel_name, tunnel_config in vpn_instance_config.tunnels : {
7 | tunnel_name = "${vpn_instance_name}-${tunnel_name}"
8 | tunnel_config = merge(
9 | tunnel_config,
10 | {
11 | vpn_instance_name = vpn_instance_name
12 | peer_external_gateway = try(vpn_instance_config.peer_external_gateway, null)
13 | peer_gcp_gateway = try(vpn_instance_config.peer_gcp_gateway, null)
14 | }
15 | )
16 | }
17 | ]
18 | ])
19 |
20 | tunnels = {
21 | for k, v in local.tunnels_tmp : v.tunnel_name => v.tunnel_config
22 | }
23 | }
24 |
25 | resource "google_compute_ha_vpn_gateway" "ha_gateway" {
26 | name = var.vpn_gateway_name
27 | project = var.project
28 | region = var.region
29 | network = var.network
30 | }
31 |
32 | resource "google_compute_router" "router" {
33 | name = coalesce(var.router_name, "${var.vpn_gateway_name}-rtr")
34 | project = var.project
35 | region = var.region
36 | network = var.network
37 | bgp {
38 | advertise_mode = (
39 | var.vpn_config.router_advertise_config == null
40 | ? null
41 | : var.vpn_config.router_advertise_config.mode
42 | )
43 | advertised_groups = (
44 | var.vpn_config.router_advertise_config == null ? null : (
45 | var.vpn_config.router_advertise_config.mode != "CUSTOM"
46 | ? null
47 | : var.vpn_config.router_advertise_config.groups
48 | )
49 | )
50 | dynamic "advertised_ip_ranges" {
51 | for_each = (
52 | var.vpn_config.router_advertise_config == null ? {} : (
53 | var.vpn_config.router_advertise_config.mode != "CUSTOM"
54 | ? {}
55 | : var.vpn_config.router_advertise_config.ip_ranges
56 | )
57 | )
58 | iterator = range
59 | content {
60 | range = range.key
61 | description = range.value
62 | }
63 | }
64 | asn = var.vpn_config.router_asn
65 | keepalive_interval = try(var.vpn_config.keepalive_interval, 20)
66 | }
67 | }
68 |
69 | # Represents a VPN gateway managed outside of GCP
70 | resource "google_compute_external_vpn_gateway" "external_gateway" {
71 | for_each = { for k, v in var.vpn_config.instances : k => v if try(v.peer_external_gateway, null) != null }
72 |
73 | name = try(each.value.peer_external_gateway.name, null) != null ? each.value.peer_external_gateway.name : "${each.value.name}-external-gw"
74 | project = var.project
75 | redundancy_type = each.value.peer_external_gateway.redundancy_type
76 | description = try(each.value.external_vpn_gateway_description, null)
77 | labels = var.labels
78 | dynamic "interface" {
79 | for_each = each.value.peer_external_gateway.interfaces
80 | content {
81 | id = interface.value.id
82 | ip_address = interface.value.ip_address
83 | }
84 | }
85 | }
86 |
87 | resource "google_compute_router_peer" "bgp_peer" {
88 | for_each = local.tunnels
89 | region = var.region
90 | project = var.project
91 | name = try(each.value.bgp_session_name, null) != null ? each.value.bgp_session_name : "${var.vpn_gateway_name}-${each.key}"
92 | router = google_compute_router.router.name
93 | peer_ip_address = each.value.bgp_peer.address
94 | peer_asn = each.value.bgp_peer.asn
95 | ip_address = each.value.bgp_peer_options == null ? null : each.value.bgp_peer_options.ip_address
96 | advertised_route_priority = (
97 | each.value.bgp_peer_options == null ? try(each.value.route_priority, 1000) : (
98 | each.value.bgp_peer_options.route_priority == null
99 | ? each.value.route_priority
100 | : each.value.bgp_peer_options.route_priority
101 | )
102 | )
103 | advertise_mode = (
104 | each.value.bgp_peer_options == null ? null : each.value.bgp_peer_options.advertise_mode
105 | )
106 | advertised_groups = (
107 | each.value.bgp_peer_options == null ? null : (
108 | each.value.bgp_peer_options.advertise_mode != "CUSTOM"
109 | ? null
110 | : each.value.bgp_peer_options.advertise_groups
111 | )
112 | )
113 | dynamic "advertised_ip_ranges" {
114 | for_each = (
115 | each.value.bgp_peer_options == null ? {} : (
116 | each.value.bgp_peer_options.advertise_mode != "CUSTOM"
117 | ? {}
118 | : each.value.bgp_peer_options.advertise_ip_ranges
119 | )
120 | )
121 | iterator = range
122 | content {
123 | range = range.key
124 | description = range.value
125 | }
126 | }
127 | interface = google_compute_router_interface.router_interface[each.key].name
128 | }
129 |
130 | resource "google_compute_router_interface" "router_interface" {
131 | for_each = local.tunnels
132 | project = var.project
133 | region = var.region
134 | name = try(each.value.bgp_session_name, null) != null ? each.value.bgp_session_name : each.key
135 | router = google_compute_router.router.name
136 | ip_range = each.value.bgp_session_range == "" ? null : each.value.bgp_session_range
137 | vpn_tunnel = google_compute_vpn_tunnel.tunnels[each.key].name
138 | }
139 |
140 | resource "google_compute_vpn_tunnel" "tunnels" {
141 | provider = google-beta
142 | for_each = local.tunnels
143 | project = var.project
144 | region = var.region
145 | name = "${var.vpn_gateway_name}-${each.key}"
146 | router = google_compute_router.router.name
147 | peer_external_gateway = try(google_compute_external_vpn_gateway.external_gateway[each.value.vpn_instance_name].self_link, null)
148 | peer_external_gateway_interface = each.value.peer_external_gateway_interface
149 | peer_gcp_gateway = each.value.peer_gcp_gateway
150 | vpn_gateway_interface = each.value.vpn_gateway_interface
151 | ike_version = each.value.ike_version
152 | shared_secret = each.value.shared_secret == "" ? local.secret : each.value.shared_secret
153 | vpn_gateway = google_compute_ha_vpn_gateway.ha_gateway.self_link
154 | labels = var.labels
155 | }
156 |
157 | resource "random_id" "secret" {
158 | byte_length = 16
159 | }
160 |
--------------------------------------------------------------------------------
/modules/panorama/variables.tf:
--------------------------------------------------------------------------------
1 | variable "region" {
2 | description = "Google Cloud region to deploy the resources into."
3 | type = string
4 | }
5 |
6 | variable "zone" {
7 | description = "Deployment area for Google Cloud resources within a region."
8 | type = string
9 | }
10 |
11 | variable "subnet" {
12 | description = "A regional resource, defining a range of IPv4 addresses. In Google Cloud, the terms subnet and subnetwork are synonymous."
13 | type = string
14 | }
15 |
16 | variable "project" {
17 | description = "The ID of the project in which the resource belongs. If it is not provided, the provider project is used."
18 | default = null
19 | type = string
20 | }
21 |
22 | variable "name" {
23 | description = "Name of the Panorama instance."
24 | type = string
25 | default = "panorama"
26 | }
27 |
28 | variable "private_static_ip" {
29 | description = < configure
70 | Entering configuration mode
71 | [edit]
72 | admin@Panorama# set mgt-config users admin password
73 | Enter password :
74 | Confirm password :
75 |
76 | [edit]
77 | admin@Panorama# commit
78 | Configuration committed successfully
79 | ```
80 |
81 | ## Check access via web UI
82 |
83 | Use a web browser to access https://x.x.x.x and login with admin and your previously configured password
84 |
85 | ## Reference
86 |
87 | ### Requirements
88 |
89 | | Name | Version |
90 | |------|---------|
91 | | [terraform](#requirement\_terraform) | >= 1.3, < 2.0 |
92 |
93 | ### Providers
94 |
95 | No providers.
96 |
97 | ### Modules
98 |
99 | | Name | Source | Version |
100 | |------|--------|---------|
101 | | [panorama](#module\_panorama) | ../../modules/panorama | n/a |
102 | | [vpc](#module\_vpc) | ../../modules/vpc | n/a |
103 |
104 | ### Resources
105 |
106 | No resources.
107 |
108 | ### Inputs
109 |
110 | | Name | Description | Type | Default | Required |
111 | |------|-------------|------|---------|:--------:|
112 | | [name\_prefix](#input\_name\_prefix) | A string to prefix resource namings | `string` | `""` | no |
113 | | [networks](#input\_networks) | A map containing each network setting.
Example of variable deployment :networks = {
"panorama-vpc" = {
vpc_name = "firewall-vpc"
create_network = true
delete_default_routes_on_create = "false"
mtu = "1460"
routing_mode = "REGIONAL"
subnetworks = {
"panorama-sub" = {
name = "panorama-subnet"
create_subnetwork = true
ip_cidr_range = "172.21.21.0/24"
region = "us-central1"
}
}
firewall_rules = {
"allow-panorama-ingress" = {
name = "panorama-mgmt"
source_ranges = ["1.1.1.1/32", "2.2.2.2/32"]
priority = "1000"
allowed_protocol = "all"
allowed_ports = []
}
}
}For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/vpc#input_networks)
Multiple keys can be added and will be deployed by the code | `any` | n/a | yes |
114 | | [panoramas](#input\_panoramas) | A map containing each panorama setting.
Example of variable deployment :panoramas = {
"panorama-01" = {
panorama_name = "panorama-01"
panorama_vpc = "panorama-vpc"
panorama_subnet = "panorama-subnet"
panorama_version = "panorama-byol-1000"
ssh_keys = "admin:PUBLIC-KEY"
attach_public_ip = true
private_static_ip = "172.21.21.2"
}
}For a full list of available configuration items - please refer to [module documentation](https://github.com/PaloAltoNetworks/terraform-google-vmseries-modules/tree/main/modules/panorama#inputs)
Multiple keys can be added and will be deployed by the code | `any` | n/a | yes |
115 | | [project](#input\_project) | The project name to deploy the infrastructure in to. | `string` | `null` | no |
116 | | [region](#input\_region) | The region into which to deploy the infrastructure in to | `string` | `"us-central1"` | no |
117 |
118 | ### Outputs
119 |
120 | | Name | Description |
121 | |------|-------------|
122 | | [panorama\_private\_ips](#output\_panorama\_private\_ips) | Private IP address of the Panorama instance. |
123 | | [panorama\_public\_ips](#output\_panorama\_public\_ips) | Public IP address of the Panorama instance. |
124 |
125 |
--------------------------------------------------------------------------------
/modules/vpc/variables.tf:
--------------------------------------------------------------------------------
1 | variable "project_id" {
2 | description = "Project in which to create or look for VPCs and subnets"
3 | default = null
4 | type = string
5 | }
6 |
7 | variable "name" {
8 | description = "The name of the created or already existing VPC Network."
9 | type = string
10 | }
11 |
12 | variable "create_network" {
13 | description = <<-EOF
14 | A flag to indicate the creation or import of a VPC network.
15 | Setting this to `true` will create a new network managed by Terraform.
16 | Setting this to `false` will try to read the existing network identified by `name` and `project` variables.
17 | EOF
18 | default = true
19 | type = bool
20 | }
21 |
22 | variable "subnetworks" {
23 | description = <<-EOF
24 | A map containing subnetworks configuration. Subnets can belong to different regions.
25 | List of available attributes of each subnetwork entry:
26 | - `name` : Name of the subnetwork.
27 | - `create_subnetwork` : Boolean value to control the creation or reading of the subnetwork. If set to `true` - this will create the subnetwork. If set to `false` - this will read a subnet with provided information.
28 | - `ip_cidr_range` : A string that contains the subnetwork to create. Only IPv4 format is supported.
29 | - `region` : Region where to configure or import the subnet.
30 |
31 | Example:
32 | ```
33 | subnetworks = {
34 | my-sub = {
35 | name = "my-sub"
36 | create_subnetwork = true
37 | ip_cidr_range = "192.168.0.0/24"
38 | region = "us-east1"
39 | }
40 | }
41 | ```
42 | EOF
43 | default = {}
44 | type = map(object({
45 | name = string
46 | create_subnetwork = optional(bool, true)
47 | ip_cidr_range = string
48 | region = string
49 | }))
50 | }
51 |
52 | variable "firewall_rules" {
53 | description = <<-EOF
54 | A map containing each firewall rule configuration.
55 | Action of the firewall rule is always `allow`.
56 | The only possible direction of the firewall rule is `INGRESS`.
57 |
58 | List of available attributes of each firewall rule entry:
59 | - `name` : Name of the firewall rule.
60 | - `source_ranges` : (Optional) A list of strings containing the source IP ranges to be allowed on the firewall rule.
61 | - `source_tags` : (Optional) A list of strings containing the source network tags to be allowed on the firewall rule.
62 | - `source_service_accounts` : (Optional) A list of strings containg the source servce accounts to be allowed on the firewall rule.
63 | - `target_service_accounts` : (Optional) A list of strings containing the service accounts for which the firewall rule applies to.
64 | - `target_tags` : (Optional) A list of strings containing the network tags for which the firewall rule applies to.
65 | - `allowed_protocol` : The protocol type to match in the firewall rule. Possible values are: `tcp`, `udp`, `icmp`, `esp`, `ah`, `sctp`, `ipip`, `all`.
66 | - `ports` : A list of strings containing TCP or UDP port numbers to match in the firewall rule. This type of setting can only be configured if allowing TCP and UDP as protocols.
67 | - `priority` : (Optional) A priority value for the firewall rule. The lower the number - the more preferred the rule is.
68 | - `log_metadata` : (Optional) This field denotes whether to include or exclude metadata for firewall logs. Possible values are: `EXCLUDE_ALL_METADATA`, `INCLUDE_ALL_METADATA`.
69 |
70 | Example :
71 | ```
72 | firewall_rules = {
73 | firewall-rule-1 = {
74 | name = "first-rule"
75 | source_ranges = ["10.10.10.0/24", "1.1.1.0/24"]
76 | priority = "2000"
77 | target_tags = ["vmseries-firewalls"]
78 | allowed_protocol = "TCP"
79 | allowed_ports = ["443", "22"]
80 | }
81 | }
82 | ```
83 | EOF
84 | default = {}
85 | type = map(object({
86 | name = string
87 | source_ranges = optional(list(string))
88 | source_tags = optional(list(string))
89 | source_service_accounts = optional(list(string))
90 | allowed_protocol = string
91 | allowed_ports = list(string)
92 | priority = optional(string)
93 | target_service_accounts = optional(list(string))
94 | target_tags = optional(list(string))
95 | log_metadata = optional(string)
96 | }))
97 | validation {
98 | condition = length(var.firewall_rules) > 0 ? alltrue([
99 | for rule in var.firewall_rules : (
100 | (rule.source_ranges != null && rule.source_tags == null && rule.source_service_accounts == null) ||
101 | (rule.source_ranges == null && rule.source_tags != null && rule.source_service_accounts == null) ||
102 | (rule.source_ranges == null && rule.source_tags == null && rule.source_service_accounts != null)
103 | )
104 | ]) : true
105 | error_message = "Only one of the following source types can be selected per firewall rule : source_ranges, source_tags or source_service_accounts ."
106 | }
107 | validation {
108 | condition = length(var.firewall_rules) > 0 ? alltrue([
109 | for rule in var.firewall_rules : (
110 | (rule.target_tags != null && rule.target_service_accounts == null) ||
111 | (rule.target_tags == null && rule.target_service_accounts != null) ||
112 | (rule.target_tags == null && rule.target_service_accounts == null)
113 | )
114 | ]) : true
115 | error_message = "Only one of the following target types can be selected per firewall rule : target_tags, target_service_accounts or neither (not configuring either of them will apply the firewall rule to all instances in the network)."
116 | }
117 | }
118 |
119 | variable "delete_default_routes_on_create" {
120 | description = <<-EOF
121 | A flag to indicate the deletion of the default routes at VPC creation.
122 | Setting this to `true` the default route `0.0.0.0/0` will be deleted upon network creation.
123 | Setting this to `false` the default route `0.0.0.0/0` will be not be deleted upon network creation.
124 | EOF
125 | default = false
126 | type = bool
127 | }
128 |
129 | variable "mtu" {
130 | description = <<-EOF
131 | MTU value for VPC Network. Acceptable values are between 1300 and 8896.
132 | EOF
133 | default = 1460
134 | type = number
135 | validation {
136 | condition = var.mtu >= 1300 && var.mtu <= 8896
137 | error_message = "MTU value must be between 1300 and 8896."
138 | }
139 | }
140 |
141 | variable "routing_mode" {
142 | description = <<-EOF
143 | Type of network-wide routing mode to use. Possible types are: REGIONAL and GLOBAL.
144 | REGIONAL routing mode will set the cloud routers to only advertise subnetworks within the same region as the router.
145 | GLOBAL routing mode will set the cloud routers to advertise all the subnetworks that belong to this network.
146 | EOF
147 | default = "REGIONAL"
148 | type = string
149 | validation {
150 | condition = var.routing_mode == "REGIONAL" || var.routing_mode == "GLOBAL"
151 | error_message = "Routing mode must be either 'REGIONAL' or 'GLOBAL'."
152 | }
153 | }
--------------------------------------------------------------------------------
/modules/vpc/README.md:
--------------------------------------------------------------------------------
1 | # VPC Network Module for GCP
2 |
3 | A Terraform module for deploying a VPC and associated subnetworks and firewall rules in GCP.
4 |
5 | One advantage of this module over the [terraform-google-network](https://github.com/terraform-google-modules/terraform-google-network/tree/master) module is that this module lets you use existing VPC networks and subnetworks to support brownfield deployments.
6 |
7 | ## Reference
8 |
9 | ### Requirements
10 |
11 | | Name | Version |
12 | |------|---------|
13 | | [terraform](#requirement\_terraform) | >= 1.3, < 2.0 |
14 | | [google](#requirement\_google) | ~> 4.54 |
15 |
16 | ### Providers
17 |
18 | | Name | Version |
19 | |------|---------|
20 | | [google](#provider\_google) | ~> 4.54 |
21 |
22 | ### Modules
23 |
24 | No modules.
25 |
26 | ### Resources
27 |
28 | | Name | Type |
29 | |------|------|
30 | | [google_compute_firewall.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource |
31 | | [google_compute_network.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_network) | resource |
32 | | [google_compute_subnetwork.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_subnetwork) | resource |
33 | | [google_compute_network.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_network) | data source |
34 | | [google_compute_subnetwork.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_subnetwork) | data source |
35 |
36 | ### Inputs
37 |
38 | | Name | Description | Type | Default | Required |
39 | |------|-------------|------|---------|:--------:|
40 | | [create\_network](#input\_create\_network) | A flag to indicate the creation or import of a VPC network.
Setting this to `true` will create a new network managed by Terraform.
Setting this to `false` will try to read the existing network identified by `name` and `project` variables. | `bool` | `true` | no |
41 | | [delete\_default\_routes\_on\_create](#input\_delete\_default\_routes\_on\_create) | A flag to indicate the deletion of the default routes at VPC creation.
Setting this to `true` the default route `0.0.0.0/0` will be deleted upon network creation.
Setting this to `false` the default route `0.0.0.0/0` will be not be deleted upon network creation. | `bool` | `false` | no |
42 | | [firewall\_rules](#input\_firewall\_rules) | A map containing each firewall rule configuration.
Action of the firewall rule is always `allow`.
The only possible direction of the firewall rule is `INGRESS`.
List of available attributes of each firewall rule entry:
- `name` : Name of the firewall rule.
- `source_ranges` : (Optional) A list of strings containing the source IP ranges to be allowed on the firewall rule.
- `source_tags` : (Optional) A list of strings containing the source network tags to be allowed on the firewall rule.
- `source_service_accounts` : (Optional) A list of strings containg the source servce accounts to be allowed on the firewall rule.
- `target_service_accounts` : (Optional) A list of strings containing the service accounts for which the firewall rule applies to.
- `target_tags` : (Optional) A list of strings containing the network tags for which the firewall rule applies to.
- `allowed_protocol` : The protocol type to match in the firewall rule. Possible values are: `tcp`, `udp`, `icmp`, `esp`, `ah`, `sctp`, `ipip`, `all`.
- `ports` : A list of strings containing TCP or UDP port numbers to match in the firewall rule. This type of setting can only be configured if allowing TCP and UDP as protocols.
- `priority` : (Optional) A priority value for the firewall rule. The lower the number - the more preferred the rule is.
- `log_metadata` : (Optional) This field denotes whether to include or exclude metadata for firewall logs. Possible values are: `EXCLUDE_ALL_METADATA`, `INCLUDE_ALL_METADATA`.
Example :firewall_rules = {
firewall-rule-1 = {
name = "first-rule"
source_ranges = ["10.10.10.0/24", "1.1.1.0/24"]
priority = "2000"
target_tags = ["vmseries-firewalls"]
allowed_protocol = "TCP"
allowed_ports = ["443", "22"]
}
} | map(object({
name = string
source_ranges = optional(list(string))
source_tags = optional(list(string))
source_service_accounts = optional(list(string))
allowed_protocol = string
allowed_ports = list(string)
priority = optional(string)
target_service_accounts = optional(list(string))
target_tags = optional(list(string))
log_metadata = optional(string)
})) | `{}` | no |
43 | | [mtu](#input\_mtu) | MTU value for VPC Network. Acceptable values are between 1300 and 8896. | `number` | `1460` | no |
44 | | [name](#input\_name) | The name of the created or already existing VPC Network. | `string` | n/a | yes |
45 | | [project\_id](#input\_project\_id) | Project in which to create or look for VPCs and subnets | `string` | `null` | no |
46 | | [routing\_mode](#input\_routing\_mode) | Type of network-wide routing mode to use. Possible types are: REGIONAL and GLOBAL.
REGIONAL routing mode will set the cloud routers to only advertise subnetworks within the same region as the router.
GLOBAL routing mode will set the cloud routers to advertise all the subnetworks that belong to this network. | `string` | `"REGIONAL"` | no |
47 | | [subnetworks](#input\_subnetworks) | A map containing subnetworks configuration. Subnets can belong to different regions.
List of available attributes of each subnetwork entry:
- `name` : Name of the subnetwork.
- `create_subnetwork` : Boolean value to control the creation or reading of the subnetwork. If set to `true` - this will create the subnetwork. If set to `false` - this will read a subnet with provided information.
- `ip_cidr_range` : A string that contains the subnetwork to create. Only IPv4 format is supported.
- `region` : Region where to configure or import the subnet.
Example:subnetworks = {
my-sub = {
name = "my-sub"
create_subnetwork = true
ip_cidr_range = "192.168.0.0/24"
region = "us-east1"
}
} | map(object({
name = string
create_subnetwork = optional(bool, true)
ip_cidr_range = string
region = string
})) | `{}` | no |
48 |
49 | ### Outputs
50 |
51 | | Name | Description |
52 | |------|-------------|
53 | | [network](#output\_network) | Created or read network attributes. |
54 | | [subnetworks](#output\_subnetworks) | Map containing key, value pairs of created or read subnetwork attributes. |
55 |
56 |
--------------------------------------------------------------------------------