├── .terraform-cache-dir └── .gitkeep ├── .tool-versions ├── stacks ├── prod │ ├── cloud-runs │ │ ├── app1 │ │ │ ├── config.tm.hcl │ │ │ ├── _terramate_generated_backend.tf │ │ │ ├── stack.tm.hcl │ │ │ ├── _terramate_generated_providers.tf │ │ │ └── _terramate_generated_cloud_run.tf │ │ ├── app2 │ │ │ ├── config.tm.hcl │ │ │ ├── _terramate_generated_backend.tf │ │ │ ├── stack.tm.hcl │ │ │ ├── _terramate_generated_providers.tf │ │ │ └── _terramate_generated_cloud_run.tf │ │ └── import_cloud_run.tm.hcl │ ├── service-accounts │ │ ├── cloud-run │ │ │ ├── stack.tm.hcl │ │ │ ├── _terramate_generated_backend.tf │ │ │ ├── _terramate_generated_service_account.tf │ │ │ └── _terramate_generated_providers.tf │ │ └── import_service_account.tm.hcl │ └── config.tm.hcl ├── staging │ ├── cloud-runs │ │ ├── app1 │ │ │ ├── config.tm.hcl │ │ │ ├── stack.tm.hcl │ │ │ ├── _terramate_generated_backend.tf │ │ │ ├── _terramate_generated_providers.tf │ │ │ └── _terramate_generated_cloud_run.tf │ │ ├── app2 │ │ │ ├── config.tm.hcl │ │ │ ├── stack.tm.hcl │ │ │ ├── _terramate_generated_backend.tf │ │ │ ├── _terramate_generated_providers.tf │ │ │ └── _terramate_generated_cloud_run.tf │ │ └── import_cloud_run.tm.hcl │ ├── service-accounts │ │ ├── cloud-run │ │ │ ├── stack.tm.hcl │ │ │ ├── _terramate_generated_backend.tf │ │ │ ├── _terramate_generated_service_account.tf │ │ │ └── _terramate_generated_providers.tf │ │ └── import_service_account.tm.hcl │ └── config.tm.hcl ├── backend.tm.hcl ├── providers.tm.hcl └── config.tm.hcl ├── modules ├── cloud-run │ ├── outputs.tf │ ├── main.tf │ ├── variables.tf │ └── cloud_run.tm.hcl └── service-account │ └── service_account.tm.hcl ├── terramate.tm.hcl ├── .gitignore ├── .github └── workflows │ └── ci.yml └── README.md /.terraform-cache-dir/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | terraform 1.8.0 2 | terramate 0.11.1 3 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app1/config.tm.hcl: -------------------------------------------------------------------------------- 1 | globals { 2 | app_image = "gcr.io/cloudrun/hello:latest" 3 | } 4 | -------------------------------------------------------------------------------- /stacks/prod/service-accounts/cloud-run/stack.tm.hcl: -------------------------------------------------------------------------------- 1 | stack { 2 | name = "Cloud Run - Service Account" 3 | } 4 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app1/config.tm.hcl: -------------------------------------------------------------------------------- 1 | globals { 2 | app_image = "gcr.io/cloudrun/hello:latest" 3 | } 4 | -------------------------------------------------------------------------------- /stacks/staging/service-accounts/cloud-run/stack.tm.hcl: -------------------------------------------------------------------------------- 1 | stack { 2 | name = "Cloud Run - Service Account" 3 | } 4 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app2/config.tm.hcl: -------------------------------------------------------------------------------- 1 | globals { 2 | app_image = "gcr.io/kubernetes-e2e-test-images/echoserver:2.2" 3 | } 4 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app2/config.tm.hcl: -------------------------------------------------------------------------------- 1 | globals { 2 | app_image = "gcr.io/kubernetes-e2e-test-images/echoserver:2.2" 3 | } 4 | -------------------------------------------------------------------------------- /stacks/prod/config.tm.hcl: -------------------------------------------------------------------------------- 1 | globals { 2 | # All stacks reachable within this sub tree will be 3 | # in our production environment 4 | environment = "prod" 5 | } 6 | -------------------------------------------------------------------------------- /stacks/staging/config.tm.hcl: -------------------------------------------------------------------------------- 1 | globals { 2 | # All stacks reachable within this sub tree will be 3 | # in our staging environment 4 | environment = "staging" 5 | } 6 | -------------------------------------------------------------------------------- /modules/cloud-run/outputs.tf: -------------------------------------------------------------------------------- 1 | output "service" { 2 | description = "All `google_cloud_run_service` resource attributes." 3 | value = module.cloud_run.service 4 | } 5 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app1/stack.tm.hcl: -------------------------------------------------------------------------------- 1 | stack { 2 | name = "Application 1 - staging" 3 | 4 | after = [ 5 | "/stacks/staging/service-accounts/cloud-run", 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app2/stack.tm.hcl: -------------------------------------------------------------------------------- 1 | stack { 2 | name = "Application 2 - staging" 3 | 4 | after = [ 5 | "/stacks/staging/service-accounts/cloud-run", 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app1/_terramate_generated_backend.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | terraform { 4 | backend "local" { 5 | path = "terraform.tfstate" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app2/_terramate_generated_backend.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | terraform { 4 | backend "local" { 5 | path = "terraform.tfstate" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app1/_terramate_generated_backend.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | terraform { 4 | backend "local" { 5 | path = "terraform.tfstate" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app2/_terramate_generated_backend.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | terraform { 4 | backend "local" { 5 | path = "terraform.tfstate" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /stacks/prod/service-accounts/cloud-run/_terramate_generated_backend.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | terraform { 4 | backend "local" { 5 | path = "terraform.tfstate" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /stacks/staging/service-accounts/cloud-run/_terramate_generated_backend.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | terraform { 4 | backend "local" { 5 | path = "terraform.tfstate" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /terramate.tm.hcl: -------------------------------------------------------------------------------- 1 | terramate { 2 | required_version = "~> 0.11.1" 3 | 4 | config { 5 | run { 6 | env { 7 | TF_PLUGIN_CACHE_DIR = "${terramate.root.path.fs.absolute}/.terraform-cache-dir" 8 | } 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /stacks/prod/service-accounts/cloud-run/_terramate_generated_service_account.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | module "terraform-google-service-account" { 4 | account_id = "cloud-run" 5 | display_name = "Cloud Run - Service Account" 6 | source = "github.com/mineiros-io/terraform-google-service-account?ref=v0.0.11" 7 | } 8 | -------------------------------------------------------------------------------- /stacks/staging/service-accounts/cloud-run/_terramate_generated_service_account.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | module "terraform-google-service-account" { 4 | account_id = "cloud-run" 5 | display_name = "Cloud Run - Service Account" 6 | source = "github.com/mineiros-io/terraform-google-service-account?ref=v0.0.11" 7 | } 8 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app1/stack.tm.hcl: -------------------------------------------------------------------------------- 1 | stack { 2 | name = "Application 1 - prod" 3 | 4 | after = [ 5 | # ensure staging app is deployed before production if both are to be deployed together 6 | "/stacks/staging/cloud-runs/app1", 7 | 8 | # ensure the service account used is deployed before the app 9 | "/stacks/prod/service-accounts/cloud-run", 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app2/stack.tm.hcl: -------------------------------------------------------------------------------- 1 | stack { 2 | name = "Application 2 - prod" 3 | 4 | after = [ 5 | # ensure staging app is deployed before production if both are to be deployed together 6 | "/stacks/staging/cloud-runs/app2", 7 | 8 | # ensure the service account used is deployed before the app 9 | "/stacks/prod/service-accounts/cloud-run", 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app1/_terramate_generated_providers.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | provider "google" { 4 | project = "example-prod" 5 | region = "europe-north1" 6 | } 7 | terraform { 8 | required_providers { 9 | google = { 10 | source = "hashicorp/google" 11 | version = "~> 4.0" 12 | } 13 | } 14 | } 15 | terraform { 16 | required_version = "~> 1.0" 17 | } 18 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app2/_terramate_generated_providers.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | provider "google" { 4 | project = "example-prod" 5 | region = "europe-north1" 6 | } 7 | terraform { 8 | required_providers { 9 | google = { 10 | source = "hashicorp/google" 11 | version = "~> 4.0" 12 | } 13 | } 14 | } 15 | terraform { 16 | required_version = "~> 1.0" 17 | } 18 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app1/_terramate_generated_providers.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | provider "google" { 4 | project = "example-staging" 5 | region = "europe-north1" 6 | } 7 | terraform { 8 | required_providers { 9 | google = { 10 | source = "hashicorp/google" 11 | version = "~> 4.0" 12 | } 13 | } 14 | } 15 | terraform { 16 | required_version = "~> 1.0" 17 | } 18 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app2/_terramate_generated_providers.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | provider "google" { 4 | project = "example-staging" 5 | region = "europe-north1" 6 | } 7 | terraform { 8 | required_providers { 9 | google = { 10 | source = "hashicorp/google" 11 | version = "~> 4.0" 12 | } 13 | } 14 | } 15 | terraform { 16 | required_version = "~> 1.0" 17 | } 18 | -------------------------------------------------------------------------------- /stacks/prod/service-accounts/cloud-run/_terramate_generated_providers.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | provider "google" { 4 | project = "example-prod" 5 | region = "europe-north1" 6 | } 7 | terraform { 8 | required_providers { 9 | google = { 10 | source = "hashicorp/google" 11 | version = "~> 4.0" 12 | } 13 | } 14 | } 15 | terraform { 16 | required_version = "~> 1.0" 17 | } 18 | -------------------------------------------------------------------------------- /stacks/staging/service-accounts/cloud-run/_terramate_generated_providers.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | provider "google" { 4 | project = "example-staging" 5 | region = "europe-north1" 6 | } 7 | terraform { 8 | required_providers { 9 | google = { 10 | source = "hashicorp/google" 11 | version = "~> 4.0" 12 | } 13 | } 14 | } 15 | terraform { 16 | required_version = "~> 1.0" 17 | } 18 | -------------------------------------------------------------------------------- /modules/cloud-run/main.tf: -------------------------------------------------------------------------------- 1 | # To demo the use of local modules and to show terramate orchestration capabilities 2 | # we define a very simple wrapper to a Mineiros Terraform Module that deploys a 3 | # cloud run application 4 | 5 | module "cloud_run" { 6 | source = "github.com/mineiros-io/terraform-google-cloud-run?ref=v0.0.2" 7 | 8 | name = var.name 9 | location = var.location 10 | project = var.project 11 | iam = var.iam 12 | 13 | template = { 14 | spec = { 15 | service_account_name = var.service_account_name 16 | 17 | containers = [ 18 | { 19 | image = var.image 20 | } 21 | ] 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/import_cloud_run.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | # This configuration triggers usage of "/modules/cloud-run/cloud_run.tm.hcl" 9 | # and demonstrate deduplication of code between staging and prod environment. 10 | # The downside of this example is that staging and prod will be changed at the same time 11 | # if the mentioned file is changed. 12 | 13 | import { 14 | source = "/modules/cloud-run/cloud_run.tm.hcl" 15 | } 16 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/import_cloud_run.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | # This configuration triggers usage of "/modules/cloud-run/cloud_run.tm.hcl" 9 | # and demonstrate deduplication of code between staging and prod environment. 10 | # The downside of this example is that staging and prod will be changed at the same time 11 | # if the mentioned file is changed. 12 | 13 | import { 14 | source = "/modules/cloud-run/cloud_run.tm.hcl" 15 | } 16 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app1/_terramate_generated_cloud_run.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | module "cloud_run_app" { 4 | iam = [ 5 | { 6 | members = [ 7 | "allUsers", 8 | ] 9 | role = "roles/run.invoker" 10 | }, 11 | ] 12 | image = "gcr.io/cloudrun/hello:latest" 13 | location = "europe-north1" 14 | name = "terramate-app1-prod" 15 | project = "example-prod" 16 | service_account_name = "cloud-run@example-prod.iam.gserviceaccount.com" 17 | source = "../../../../modules/cloud-run" 18 | } 19 | output "url" { 20 | description = "URL of terramate-app1-prod" 21 | value = module.cloud_run_app.service.status[0].url 22 | } 23 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app1/_terramate_generated_cloud_run.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | module "cloud_run_app" { 4 | iam = [ 5 | { 6 | members = [ 7 | "allUsers", 8 | ] 9 | role = "roles/run.invoker" 10 | }, 11 | ] 12 | image = "gcr.io/cloudrun/hello:latest" 13 | location = "europe-north1" 14 | name = "terramate-app1-staging" 15 | project = "example-staging" 16 | service_account_name = "cloud-run@example-staging.iam.gserviceaccount.com" 17 | source = "../../../../modules/cloud-run" 18 | } 19 | output "url" { 20 | description = "URL of terramate-app1-staging" 21 | value = module.cloud_run_app.service.status[0].url 22 | } 23 | -------------------------------------------------------------------------------- /stacks/prod/cloud-runs/app2/_terramate_generated_cloud_run.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | module "cloud_run_app" { 4 | iam = [ 5 | { 6 | members = [ 7 | "allUsers", 8 | ] 9 | role = "roles/run.invoker" 10 | }, 11 | ] 12 | image = "gcr.io/kubernetes-e2e-test-images/echoserver:2.2" 13 | location = "europe-north1" 14 | name = "terramate-app2-prod" 15 | project = "example-prod" 16 | service_account_name = "cloud-run@example-prod.iam.gserviceaccount.com" 17 | source = "../../../../modules/cloud-run" 18 | } 19 | output "url" { 20 | description = "URL of terramate-app2-prod" 21 | value = module.cloud_run_app.service.status[0].url 22 | } 23 | -------------------------------------------------------------------------------- /stacks/prod/service-accounts/import_service_account.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | # This configuration triggers usage of "/modules/service-account/service-account.tm.hcl" 9 | # and demonstrate deduplication of code between staging and prod environment. 10 | # The downside of this example is that staging and prod will be changed at the same time 11 | # if the mentioned file is changed. 12 | 13 | import { 14 | source = "/modules/service-account/service_account.tm.hcl" 15 | } 16 | -------------------------------------------------------------------------------- /stacks/staging/service-accounts/import_service_account.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | # This configuration triggers usage of "/modules/service-account/service-account.tm.hcl" 9 | # and demonstrate deduplication of code between staging and prod environment. 10 | # The downside of this example is that staging and prod will be changed at the same time 11 | # if the mentioned file is changed. 12 | 13 | import { 14 | source = "/modules/service-account/service_account.tm.hcl" 15 | } 16 | -------------------------------------------------------------------------------- /stacks/staging/cloud-runs/app2/_terramate_generated_cloud_run.tf: -------------------------------------------------------------------------------- 1 | // TERRAMATE: GENERATED AUTOMATICALLY DO NOT EDIT 2 | 3 | module "cloud_run_app" { 4 | iam = [ 5 | { 6 | members = [ 7 | "allUsers", 8 | ] 9 | role = "roles/run.invoker" 10 | }, 11 | ] 12 | image = "gcr.io/kubernetes-e2e-test-images/echoserver:2.2" 13 | location = "europe-north1" 14 | name = "terramate-app2-staging" 15 | project = "example-staging" 16 | service_account_name = "cloud-run@example-staging.iam.gserviceaccount.com" 17 | source = "../../../../modules/cloud-run" 18 | } 19 | output "url" { 20 | description = "URL of terramate-app2-staging" 21 | value = module.cloud_run_app.service.status[0].url 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | .terraform.lock.hcl 8 | 9 | # Crash log files 10 | crash.log 11 | 12 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 13 | # .tfvars files are managed as part of configuration and so should be included in 14 | # version control. 15 | # 16 | # example.tfvars 17 | 18 | # Ignore override files as they are usually used to override resources locally and so 19 | # are not checked in 20 | override.tf 21 | override.tf.json 22 | *_override.tf 23 | *_override.tf.json 24 | 25 | # Include override files you do wish to add to version control using negated pattern 26 | # 27 | # !example_override.tf 28 | 29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 30 | # example: *tfplan* 31 | .terraform-cache-dir 32 | -------------------------------------------------------------------------------- /stacks/backend.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | ############################################################################## 9 | # Generate '_terramate_generated_backend.tf' in each stack 10 | # All globals will be replaced with the final value that is known by the stack 11 | # Any terraform code can be defined within the content block 12 | generate_hcl "_terramate_generated_backend.tf" { 13 | content { 14 | terraform { 15 | 16 | # for the sake of the demo we define a local backend 17 | # the path is defined in the global config and 18 | # defaults to terraforms default 19 | # the default values of globals are defined in config.tm.hcl in this directory 20 | backend "local" { 21 | path = global.local_tfstate_path 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /modules/cloud-run/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | description = "(Required) Name must be unique within a namespace, within a Cloud Run region. Is required when creating resources. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated." 3 | type = string 4 | } 5 | 6 | variable "location" { 7 | description = "(Required) The location of the cloud run instance. eg us-central1." 8 | type = string 9 | } 10 | 11 | variable "image" { 12 | description = "(Required) The container image that will be run." 13 | type = string 14 | } 15 | 16 | variable "project" { 17 | description = "(Optional) The project to deploy resources in. If not set, the provider project will be used." 18 | type = string 19 | default = null 20 | } 21 | 22 | variable "iam" { 23 | description = "(Optional) IAM configuration on the cloud run" 24 | type = any 25 | default = [] 26 | } 27 | 28 | variable "service_account_name" { 29 | description = "(Optional) specify a service account to use as execution account" 30 | type = string 31 | default = null 32 | } 33 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI Pipeline 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | tests: 13 | runs-on: "ubuntu-20.04" 14 | 15 | name: Run Tests 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Set up Terramate 23 | uses: terramate-io/terramate-action@v2 24 | 25 | - name: Set up Terraform 26 | uses: hashicorp/setup-terraform@v1 27 | with: 28 | terraform_version: 1.8.0 29 | terraform_wrapper: false 30 | 31 | - name: Check Terraform Formatting 32 | run: terraform fmt -recursive -check -no-color 33 | 34 | - name: Check Terramate Formatting 35 | run: terramate fmt --check 36 | 37 | - name: List all Stacks 38 | run: terramate list 39 | 40 | - name: Initialize Stacks 41 | run: terramate run -- terraform init 42 | 43 | - name: Validate Stacks 44 | run: terramate run -- terraform validate 45 | 46 | done: 47 | name: Static Analysis 48 | runs-on: ubuntu-20.04 49 | needs: tests 50 | 51 | steps: 52 | - name: Done 53 | run: "true" 54 | -------------------------------------------------------------------------------- /stacks/providers.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | ############################################################################## 9 | # Generate '_terramate_generated_providers.tf' in each stack 10 | # All globals will be replaced with the final value that is known by the stack 11 | # Any terraform code can be defined within the content block 12 | generate_hcl "_terramate_generated_providers.tf" { 13 | content { 14 | 15 | # the default values of globals are defined in config.tm.hcl in this directory 16 | 17 | provider "google" { 18 | project = global.terraform_google_provider_project 19 | region = global.terraform_google_provider_region 20 | } 21 | 22 | terraform { 23 | required_providers { 24 | google = { 25 | source = "hashicorp/google" 26 | version = global.terraform_google_provider_version 27 | } 28 | } 29 | } 30 | 31 | terraform { 32 | required_version = global.terraform_version 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /stacks/config.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | globals { 9 | ### TERRAFORM ############################################################### 10 | 11 | ### global variables for use when generating providers 12 | # all variables defined here can be overwritten in any sub-directory and on the 13 | # stack level 14 | 15 | # The global terraform version to use 16 | terraform_version = "~> 1.0" 17 | 18 | # provider settings and defaults 19 | terraform_google_provider_version = "~> 4.0" 20 | terraform_google_provider_region = "europe-north1" 21 | 22 | # this should be adjusted when testing actual deployemnts 23 | terraform_google_provider_project = "example-${global.environment}" 24 | 25 | ### global variables for use when generating backend 26 | # all variables defined here can be overwritten in any sub-directory and on the 27 | # stack level 28 | 29 | # to demonstrate how to use gloabls in backend configuration 30 | # the same way you could define state buckets and path within the bucket 31 | # e.g. setting prefix to terramate.path 32 | # we use terraforms default for local backends here 33 | local_tfstate_path = "terraform.tfstate" 34 | 35 | ### GLOBALS ################################################################## 36 | 37 | # global variables for use in terraform code within stacks 38 | # we use providers project and location by default 39 | project = global.terraform_google_provider_project 40 | location = global.terraform_google_provider_region 41 | } 42 | -------------------------------------------------------------------------------- /modules/service-account/service_account.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | # This file is defined on a very high level to simulate code deduplication between 9 | # different environments. The same code will be used in staging and production 10 | # triggered by /stacks/{environment}/service-accounts/use_service_account.tm.hcl 11 | 12 | ############################################################################## 13 | # Defaults for each service account that can be overwritten in stacks below 14 | globals { 15 | 16 | # The default acount ID to use: the stacks directory basename 17 | sa_account_id = terramate.stack.path.basename 18 | 19 | # The default display name of the Service Account: The name of the configured stack 20 | sa_display_name = terramate.name 21 | } 22 | 23 | ############################################################################## 24 | # Generate '_terramate_generated_service_account.tf' in each stack 25 | # that has a global variable 'enable_service_account' set to true 26 | # All globals will be replaced with the final value that is known by the stack 27 | # Any terraform code can be defined within the content block 28 | generate_hcl "_terramate_generated_service_account.tf" { 29 | content { 30 | # we use a remote Mineiros Module for service account creation 31 | module "terraform-google-service-account" { 32 | source = "github.com/mineiros-io/terraform-google-service-account?ref=v0.0.11" 33 | 34 | account_id = global.sa_account_id 35 | display_name = global.sa_display_name 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /modules/cloud-run/cloud_run.tm.hcl: -------------------------------------------------------------------------------- 1 | # This file is part of Terramate Configuration. 2 | # Terramate is an orchestrator and code generator for Terraform. 3 | # Please see https://github.com/terramate-io/terramate for more information. 4 | # 5 | # To generate/update Terraform code within the stacks 6 | # run `terramate generate` from root directory of the repository. 7 | 8 | ############################################################################## 9 | # Defaults for each service account that can be overwritten in stacks below 10 | globals { 11 | # The default name of a cloud run application is terramate-{stack_basename}-{environment} 12 | app_name = "terramate-${terramate.stack.path.basename}-${global.environment}" 13 | 14 | # The location where the cloud run will be deployed defaults to the global location default. 15 | app_location = global.location 16 | 17 | # The default docker image to deploy asthe application 18 | # This should be set to null or not set at all to force each stack to define a specific image 19 | app_image = "gcr.io/kubernetes-e2e-test-images/echoserver:2.2" 20 | 21 | # By default make the cloud run application public. 22 | # This is not a sane default and should not be used outside of this example 23 | app_invokers = ["allUsers"] 24 | 25 | # We are using the service account generated for cloud run workloads 26 | # the service account does not have any access assigned 27 | # each application can overwrite this global variable to specify a service account 28 | # that has application specific access rights 29 | app_service_account_name = "cloud-run@${global.project}.iam.gserviceaccount.com" 30 | } 31 | 32 | ############################################################################## 33 | # Generate '_terramate_generated_cloud_run.tf' in each stack 34 | # All globals will be replaced with the final value that is known by the stack 35 | # Any terraform code can be defined within the content block 36 | generate_hcl "_terramate_generated_cloud_run.tf" { 37 | content { 38 | 39 | # We are invoking our local wrapper to the module 40 | # to also demonstrate terramate orchestration capabilities 41 | module "cloud_run_app" { 42 | source = "${terramate.stack.path.to_root}/modules/cloud-run" 43 | 44 | project = global.project 45 | 46 | name = global.app_name 47 | location = global.app_location 48 | image = global.app_image 49 | service_account_name = global.app_service_account_name 50 | 51 | iam = [ 52 | { 53 | role = "roles/run.invoker" 54 | members = global.app_invokers 55 | } 56 | ] 57 | } 58 | 59 | # An output to show the cloud run url after a successful terraform apply 60 | output "url" { 61 | description = "URL of ${global.app_name}" 62 | value = module.cloud_run_app.service.status[0].url 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # terramate-example-code-generation 2 | 3 | ![CI Status](https://github.com/terramate-io/terramate-example-code-generation/actions/workflows/ci.yml/badge.svg) 4 | [![Join Discord](https://img.shields.io/discord/1088753599951151154?label=Discord&logo=discord&logoColor=white)](https://terramate.io/discord) 5 | 6 | This project shows an example file/dir structure you can use with 7 | [Terramate](https://github.com/terramate-io/terramate) to keep your Terraform 8 | code DRY. 9 | 10 | Be sure to read through the [Terramate documentation](https://github.com/terramate-io/terramate) 11 | to understand the features of Terramate used here. 12 | 13 | The example is organized as two environments, each environment will have: 14 | 15 | - Its own [Google Cloud Project](https://cloud.google.com/storage/docs/projects). 16 | - Service account to be used when deploying Cloud Run services. 17 | - Two [Cloud Run](https://cloud.google.com/run) applications. 18 | 19 | The [Cloud Run](https://cloud.google.com/run) applications are simple 20 | echo servers that will be reachable through public URLs provided by 21 | [Cloud Run](https://cloud.google.com/run). 22 | 23 | Note: This code is solely for demonstration purposes. 24 | This is not production-ready code, so use it at your own risk. 25 | 26 | # How to use this project? 27 | 28 | ## Pre-Requisites 29 | 30 | - [Terraform](https://www.terraform.io/) `~> 1.8` 31 | - [Terramate](https://github.com/terramate-io/terramate) `~> 0.11.1` 32 | - Configure your Google Cloud credentials using one of the supported [authentication mechanisms](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#authentication) 33 | - Google Cloud Provider account 34 | - At least one [Google Cloud project](https://cloud.google.com/storage/docs/projects) 35 | - The Google Cloud project has a proper billing account configured 36 | 37 | # How is the code organized? 38 | 39 | This is the overall structure of the project: 40 | 41 | ``` 42 | ├── modules 43 | │   ├── cloud-run 44 | │   └── service-account 45 | └── stacks 46 | ├── prod 47 | │   ├── cloud-runs 48 | │   │   ├── app1 49 | │   │   ├── app2 50 | │   └── service-accounts 51 | │   └── cloud-run 52 | └── staging 53 | ├── cloud-runs 54 | │   ├── app1 55 | │   ├── app2 56 | └── service-accounts 57 | └── cloud-run 58 | ``` 59 | 60 | - `modules/cloud-run`: Local module, useful to showcase change detection and DRY code generation. 61 | - `modules/service-account`: Local Terramate config that keeps code generation DRY between environments. 62 | - `stacks/prod`: All stacks belonging to the prod environment. 63 | - `stacks/staging`: All stacks belonging to the staging environment. 64 | - `stacks//service-accounts/cloud-run`: Stack that creates service accounts used to execute the cloud run services. 65 | - `stacks//cloud-runs/{app1,app2}`: Stacks that create Cloud Run services. 66 | 67 | As you navigate the project you will find multiple Terramate configuration files. 68 | Each file will have documentation guiding you through its purpose and usage. 69 | 70 | ## Listing Stacks 71 | 72 | To check if your Terramate installation is working and get an overview of the 73 | available stacks just run: 74 | 75 | ```sh 76 | terramate list 77 | ``` 78 | 79 | To check how each stack is defined in detail you can use `terramate run`: 80 | 81 | ```sh 82 | terramate run -- cat stack.tm.hcl 83 | ``` 84 | 85 | This will run on each stack directory the command `cat stack.tm.hcl`. 86 | The output will be the definition of all stacks. 87 | 88 | Later we are going to use the same mechanism to create and destroy all stacks. 89 | 90 | ## Deploying Stacks 91 | 92 | Before we try to deploy any stacks, beware that this will require you 93 | to have [Google Cloud credentials](https://cloud.google.com/docs/authentication/getting-started) 94 | and deploying infrastructure will incur costs (check the 95 | [pre-requisites](#pre-requisites) section for more details). 96 | 97 | On `stacks/config.tm.hcl` you will find the `terraform_google_provider_project` 98 | global which configures the project where infrastructure will be created. 99 | 100 | It is important to change that to a [Google Cloud project](https://cloud.google.com/storage/docs/projects) 101 | where you have appropriate permissions. 102 | 103 | Once the configuration is changed we need to update the generated code by running: 104 | At this point, since our project has uncommitted changes Terramate will prevent us 105 | from running any commands. Create a branch (or use the flag `--disable-check-git-uncommitted` 106 | to disable the git checks): 107 | 108 | ```sh 109 | git checkout -b 110 | ``` 111 | 112 | And commit all the changed files. 113 | 114 | Now we initialize all our stacks: 115 | 116 | ```sh 117 | terramate run -- terraform init 118 | ``` 119 | 120 | Check how their plans look like: 121 | 122 | ```sh 123 | terramate run -- terraform plan 124 | ``` 125 | 126 | And apply them: 127 | 128 | ```sh 129 | terramate run -- terraform apply 130 | ``` 131 | 132 | For each Cloud Run service deployed, there will be an output with the URL to 133 | the deployed service, like this: 134 | 135 | ```sh 136 | url = "https://terramate-app1---lz.a.run.app" 137 | ``` 138 | 139 | You can check the outputs with: 140 | 141 | ```sh 142 | terramate run -- terraform output 143 | ``` 144 | 145 | Open the URL on the browser to check the running service. 146 | 147 | To avoid unnecessary charges to your account let's destroy all stacks: 148 | 149 | ```sh 150 | terramate run --reverse -- terraform destroy 151 | ``` 152 | 153 | The `--reverse` flag runs all stacks in reversed order, which is desirable 154 | when destroying resources. 155 | --------------------------------------------------------------------------------