{"id":24233830,"url":"https://github.com/memes/terraform-google-private-bastion","last_synced_at":"2026-03-06T16:02:23.479Z","repository":{"id":37983829,"uuid":"453248550","full_name":"memes/terraform-google-private-bastion","owner":"memes","description":"Terraform module to provision a bastion that initialises from artifact or container registry","archived":false,"fork":false,"pushed_at":"2025-12-22T17:27:49.000Z","size":254,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-24T06:27:46.448Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/memes.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-01-28T23:38:18.000Z","updated_at":"2025-12-22T17:27:28.000Z","dependencies_parsed_at":"2023-02-12T20:15:47.798Z","dependency_job_id":"fca858b6-f831-40ae-9d36-91dc29a4a004","html_url":"https://github.com/memes/terraform-google-private-bastion","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":"memes/repo-template","purl":"pkg:github/memes/terraform-google-private-bastion","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memes%2Fterraform-google-private-bastion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memes%2Fterraform-google-private-bastion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memes%2Fterraform-google-private-bastion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memes%2Fterraform-google-private-bastion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/memes","download_url":"https://codeload.github.com/memes/terraform-google-private-bastion/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/memes%2Fterraform-google-private-bastion/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30184874,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T14:42:24.748Z","status":"ssl_error","status_checked_at":"2026-03-06T14:42:14.925Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-14T16:01:55.554Z","updated_at":"2026-03-06T16:02:23.473Z","avatar_url":"https://github.com/memes.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Private Bastion Terraform module for Google Cloud\n\n![GitHub release](https://img.shields.io/github/v/release/memes/terraform-google-private-bastion?sort=semver)\n![Maintenance](https://img.shields.io/maintenance/yes/2024)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)\n\nThis module deploys a bastion VM that can function correctly when deployed in a\nprivate VPC without public internet access, by pulling a container image\ncontaining a forward-proxy from a Google Artifact or Container Registry.\n\nAs such, the scope of services this module provides is restricted to\n\n* An IAP protected forward-proxy deployed from Container Registry or Artifact Registry\n* An IAP protected SSH login\n* An optional set of Firewall Rules to allow bastion to connect to other GCP\n  resources specified as destination CIDRs or target service accounts\n\n\u003e NOTE: This module is opinionated and deliberately inflexible; the Google\n\u003e [bastion-host](https://registry.terraform.io/modules/terraform-google-modules/bastion-host/google/latest)\n\u003e (and related submodules) make a much better general purpose bastion for GCP.\n\nThis module was born out of a repeated pattern of deployment; launch a bastion VM\nwith custom onboarding script to pull [tinyproxy](https://tinyproxy.github.io/)\nfrom a source accessible to the VM.\n\nA few iterations later, and now the module will launch a\n[Container-Optimized OS](https://cloud.google.com/container-optimized-os/docs/concepts/features-and-benefits)\nVM with a `cloud-init` declaration that pulls a `forward-proxy` container from\nthe private repo you specify. The Terraform will apply an appropriate IAM repo\nreader role to the created bastion service account so it can automatically pull\nthe container as needed.\n\n## Usage\n\n1. Meet the prerequisites listed in [Prerequisites](#prerequisites) section\n\n    The `forward-proxy` container must already exist in a private repo for which\n    you have the ability to assign IAM roles.\n\n2. Clone the [quick-start](examples/quick-start/) as a starting point.\n\n    ```shell\n    mkdir private-bastion\n    cd private-bastion\n    terraform init -from-module memes/private-bastion/google//examples/quick-start\n    ```\n\n3. Create a `terraform.tfvars` file\n\n    Use the file to set the required parameters; see [Inputs](#inputs) for\n    other optional values to set, and the [quick-start](examples/quick-start/README.md) documentation for more details.\n\n    \u003e NOTE: It is recommended to include `bastion_targets` to ensure GCP Firewall\n    \u003e rules are created to allow bastion =\u003e other resource traffic. Just add the\n    \u003e target service accounts, or CIDRs to the input. If you leave\n    \u003e `bastion_targets` at the default value, you will need to create bastion to\n    \u003e resource firewall rules outside of this module.\n\n    ```hcl\n    name                  = \"example\"\n    project_id            = \"my-project-id\"\n    zone                  = \"us-west1-c\"\n    subnet                = \"https://www.googleapis.com/compute/v1/projects/my-project-id/regions/us-west1/subnetworks/my-subnet\"\n\n    # This needs to point to your private repo copy of forward-proxy\n    proxy_container_image = \"us-docker.pkg.dev/my-project-id/forward-proxy:my-build\"\n\n    # Allow the bastion to connect to GKE master nodes at 172.19.0.0/28\n    bastion_targets       = {\n        service_accounts = null\n        cidrs            = [\"172.19.0.0/28\"]\n        priority         = null\n    }\n    ```\n\n4. Launch the VM\n\n    ```shell\n    terraform apply\n    ```\n\n5. Connect to the bastion\n\n    The `gcloud` commands to connect to the bastion are included as Terraform\n    outputs.\n\n    1. SSH\n\n        ```shell\n        eval $(terraform output -raw ssh_command)\n        ```\n\n    2. HTTP/HTTPS tunnel\n\n        ```shell\n        eval $(terraform output -raw tunnel_command)\n        ```\n\n        While the tunnel is up and running, you can set your browser or tools to\n        connect via the tunnel. For example, if you've have a GKE kubeconfig for a\n        private cluster loaded:\n\n        ```shell\n        HTTPS_PROXY=127.0.0.1:8888 kubectl get pods\n        ```\n\n6. Teardown the bastion\n\n    ```shell\n    terraform destroy -auto-approve\n    ```\n\n### Prerequisites\n\nA copy of the [forward-proxy](containers/forward-proxy/) container needs to be\ncreated or copied to a suitable private repository. The instructions below use\na Cloud Build script to create the container and upload it to the private\nrepository. Of course, you can use `docker build` or `podman build` too, so long\nas you have authenticated to the target repository.\n\n\u003e Alternatively, you can use the [container-stager](https://github.com/f5devcentral/terraform-google-f5-automation-factory/tree/main/archetype/container-stager)\n\u003e archetype published as part of F5 DevCentral's [Google Automation Factory](https://github.com/f5devcentral/terraform-google-f5-automation-factory/) to copy my public image\n\u003e from [Docker Hub](https://hub.docker.com/r/memes/forward-proxy) or [GitHub Container Registry](https://github.com/memes/terraform-google-private-bastion/pkgs/container/terraform-google-private-bastion%2Fforward-proxy) to your private container\n\u003e registry. [Google Automation Factory](https://github.com/f5devcentral/terraform-google-f5-automation-factory/)\n\u003e can also be used to create and manage a shared Artifact Repository with automatic\n\u003e update triggers.\n\n1. Enable one of Artifact Registry API or Container Registry API,\n    if needed, and create an registry as needed\n\n    See the GCP documentation for [Artifact management](https://cloud.google.com/artifact-management/docs/overview)\n    for more details.\n\n    Make a note of the repo path for your repository. For example, the `US` located\n    Artifact Repository `f5-automation-factory-container` deployed to project\n    `my-gcp-project` will have repo `us-docker.pkg.dev/my-project-id/f5-automation-factory-container`\n\n2. Review the source of the [forward-proxy](containers/forward-proxy/) container\n\n    You wouldn't deploy an unverified source, would you? :)\n\n    Make any required changes; if you think it'd be a good addition to the base\n    image or experience a bug, please open an issue and/or PR against this repo.\n    See [CONTRIBUTING](CONTRIBUTING.md) for more details.\n\n3. Build the image and upload to your private repository\n\n    Trigger a Cloud Build run from this source, giving the repo path and a custom\n    tag.\n\n    ```shell\n    gcloud builds submit \\\n        --config cloudbuild.yml \\\n        --project my-project-id \\\n        --substitutions _CONTAINER_REGISTRY=us-docker.pkg.dev/my-project-id/f5-automation-factory-container,TAG_NAME=my-build\n    ```\n\n    Cloud Build will create the container, tag it with `latest` and `my-build`,\n    and upload to your private registry.\n\n\u003c!-- markdownlint-disable no-inline-html no-bare-urls --\u003e\n\u003c!-- BEGIN_TF_DOCS --\u003e\n## Requirements\n\n| Name | Version |\n|------|---------|\n| \u003ca name=\"requirement_terraform\"\u003e\u003c/a\u003e [terraform](#requirement\\_terraform) | \u003e= 1.5 |\n| \u003ca name=\"requirement_google\"\u003e\u003c/a\u003e [google](#requirement\\_google) | \u003e= 6.9 |\n\n## Modules\n\nNo modules.\n\n## Resources\n\n| Name | Type |\n|------|------|\n| [google_artifact_registry_repository_iam_member.bastion](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/artifact_registry_repository_iam_member) | resource |\n| [google_compute_firewall.access_bastion](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource |\n| [google_compute_firewall.bastion_cidrs](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource |\n| [google_compute_firewall.bastion_service_accounts](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource |\n| [google_compute_firewall.iap](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource |\n| [google_compute_instance.bastion](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance) | resource |\n| [google_iap_tunnel_instance_iam_binding.members](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iap_tunnel_instance_iam_binding) | resource |\n| [google_project_iam_member.bastion](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_member) | resource |\n| [google_service_account.bastion](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource |\n| [google_service_account_iam_binding.members](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_iam_binding) | resource |\n| [google_storage_bucket_iam_member.bastion](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_member) | resource |\n| [google_compute_image.disk](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_image) | data source |\n| [google_compute_subnetwork.subnet](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_subnetwork) | data source |\n\n## Inputs\n\n| Name | Description | Type | Default | Required |\n|------|-------------|------|---------|:--------:|\n| \u003ca name=\"input_name\"\u003e\u003c/a\u003e [name](#input\\_name) | The name to use when creating resources managed by this module. Must be RFC1035 compliant and between 5 and 30 characters in length, inclusive. | `string` | n/a | yes |\n| \u003ca name=\"input_project_id\"\u003e\u003c/a\u003e [project\\_id](#input\\_project\\_id) | The GCP project identifier where the bastion instance will be deployed. | `string` | n/a | yes |\n| \u003ca name=\"input_proxy_container_image\"\u003e\u003c/a\u003e [proxy\\_container\\_image](#input\\_proxy\\_container\\_image) | The qualified container image to use as a forward-proxy through this bastion.\u003cbr/\u003eYou must supply this value with a valid private Artifact or Container Repository\u003cbr/\u003eidentifier, or a public repo identifier. | `string` | n/a | yes |\n| \u003ca name=\"input_subnet\"\u003e\u003c/a\u003e [subnet](#input\\_subnet) | The fully-qualified subnetwork self-link to which the bastion instance will be\u003cbr/\u003eattached. | `string` | n/a | yes |\n| \u003ca name=\"input_zone\"\u003e\u003c/a\u003e [zone](#input\\_zone) | The compute zone where where the bastion instance will be deployed. | `string` | n/a | yes |\n| \u003ca name=\"input_additional_bastion_roles\"\u003e\u003c/a\u003e [additional\\_bastion\\_roles](#input\\_additional\\_bastion\\_roles) | An optional list of roles that will be assigned to the generated bastion service\u003cbr/\u003eaccount in addition to the standard logging, metrics, and OS Login roles. Default\u003cbr/\u003eis an empty list. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_additional_ports\"\u003e\u003c/a\u003e [additional\\_ports](#input\\_additional\\_ports) | A list of additional TCP ports that will be allowed to receive IAP tunneled\u003cbr/\u003etraffic, in addition to the forward-proxy listener port (see `remote_port`) and SSH.\u003cbr/\u003eDefault is an empty list. | `list(number)` | `[]` | no |\n| \u003ca name=\"input_bastion_targets\"\u003e\u003c/a\u003e [bastion\\_targets](#input\\_bastion\\_targets) | An optional set of firewall targets that will be used to create GCP Firewall Rules\u003cbr/\u003ethat allow the targets to receive _ALL_ ingress traffic from the bastion instance.\u003cbr/\u003eTargets are specified as a list of service account emails and  destination CIDRs.\u003cbr/\u003eIf a priority is unspecified, the rules will be created at the default priority (1000).\u003cbr/\u003e\u003cbr/\u003eLeave this variable at the default empty value to manage firewall rules outside\u003cbr/\u003ethis module. | \u003cpre\u003eobject({\u003cbr/\u003e    service_accounts = list(string)\u003cbr/\u003e    cidrs            = list(string)\u003cbr/\u003e    priority         = number\u003cbr/\u003e  })\u003c/pre\u003e | \u003cpre\u003e{\u003cbr/\u003e  \"cidrs\": null,\u003cbr/\u003e  \"priority\": null,\u003cbr/\u003e  \"service_accounts\": null\u003cbr/\u003e}\u003c/pre\u003e | no |\n| \u003ca name=\"input_disk_size_gb\"\u003e\u003c/a\u003e [disk\\_size\\_gb](#input\\_disk\\_size\\_gb) | The size of the bastion boot disk in GB. Default is 20. | `number` | `20` | no |\n| \u003ca name=\"input_external_ip\"\u003e\u003c/a\u003e [external\\_ip](#input\\_external\\_ip) | Boolean flag to toggle provisioning of an ephemeral public IP on the bastion\u003cbr/\u003einstance; default is false. | `bool` | `false` | no |\n| \u003ca name=\"input_image\"\u003e\u003c/a\u003e [image](#input\\_image) | Specifies the image family and project id to use for bastion. Default will launch\u003cbr/\u003ethe latest stable COS image with Confidential VM support. | \u003cpre\u003eobject({\u003cbr/\u003e    family     = string\u003cbr/\u003e    project_id = string\u003cbr/\u003e  })\u003c/pre\u003e | \u003cpre\u003e{\u003cbr/\u003e  \"family\": \"cos-stable\",\u003cbr/\u003e  \"project_id\": \"confidential-vm-images\"\u003cbr/\u003e}\u003c/pre\u003e | no |\n| \u003ca name=\"input_labels\"\u003e\u003c/a\u003e [labels](#input\\_labels) | An optional map of labels to apply to resources created by this module. Default is empty. | `map(string)` | `{}` | no |\n| \u003ca name=\"input_local_port\"\u003e\u003c/a\u003e [local\\_port](#input\\_local\\_port) | The local TCP port that will be embedded in the IAP tunnel command output. This\u003cbr/\u003eis the value to which HTTP/HTTPS proxies should use; e.g. HTTP\\_PROXY=http://localhost:LOCAL_PORT,\u003cbr/\u003ewhere LOCAL\\_PORT is the value of `local_port` variable. Default value is 8888. | `number` | `8888` | no |\n| \u003ca name=\"input_machine_type\"\u003e\u003c/a\u003e [machine\\_type](#input\\_machine\\_type) | The Compute Engine machine type to use for bastion. Default is 'e2-medium'. | `string` | `\"e2-medium\"` | no |\n| \u003ca name=\"input_members\"\u003e\u003c/a\u003e [members](#input\\_members) | An optional list of user/group/serviceAccount emails that will be added as IAP\u003cbr/\u003emembers for _this_ bastion. Default is empty. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_remote_port\"\u003e\u003c/a\u003e [remote\\_port](#input\\_remote\\_port) | The remote TCP port that the forward-proxy container will be listening on.\u003cbr/\u003eDefault value is 8888. | `number` | `8888` | no |\n| \u003ca name=\"input_source_cidrs\"\u003e\u003c/a\u003e [source\\_cidrs](#input\\_source\\_cidrs) | An optional list of CIDRs that will be permitted to access the bastion on ports 22, `remote_port` (default 8888), and\u003cbr/\u003eany listed in `additional_ports` directly via public IP when the `external_ip` flag is set to true. Default is an empty\u003cbr/\u003elist. | `list(string)` | `[]` | no |\n| \u003ca name=\"input_tags\"\u003e\u003c/a\u003e [tags](#input\\_tags) | An optional list of network tags to apply to resources created by this module. Default is empty. | `list(string)` | `[]` | no |\n\n## Outputs\n\n| Name | Description |\n|------|-------------|\n| \u003ca name=\"output_ip_address\"\u003e\u003c/a\u003e [ip\\_address](#output\\_ip\\_address) | The private IP address of the bastion instance. |\n| \u003ca name=\"output_public_ip_address\"\u003e\u003c/a\u003e [public\\_ip\\_address](#output\\_public\\_ip\\_address) | The public IP address of the bastion, if applicable. |\n| \u003ca name=\"output_self_link\"\u003e\u003c/a\u003e [self\\_link](#output\\_self\\_link) | The self-link of the bastion instance. |\n| \u003ca name=\"output_service_account\"\u003e\u003c/a\u003e [service\\_account](#output\\_service\\_account) | The service account created for the bastion. |\n| \u003ca name=\"output_ssh_command\"\u003e\u003c/a\u003e [ssh\\_command](#output\\_ssh\\_command) | A gcloud command that will SSH via IAP to bastion host. |\n| \u003ca name=\"output_tunnel_command\"\u003e\u003c/a\u003e [tunnel\\_command](#output\\_tunnel\\_command) | A gcloud command that create a tunnel between localhost and bastion via IAP;\u003cbr/\u003econnections to localhost:PORT will be tunneled to bastion forward-proxy. The value\u003cbr/\u003eof PORT will be taken from `local_port` variable, with 8888 as the default. |\n\u003c!-- END_TF_DOCS --\u003e\n\u003c!-- markdownlint-enable no-inline-html no-bare-urls --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmemes%2Fterraform-google-private-bastion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmemes%2Fterraform-google-private-bastion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmemes%2Fterraform-google-private-bastion/lists"}