Skip to content

Commit

Permalink
GCP dev environment terraform configs (#610)
Browse files Browse the repository at this point in the history
* scaffold org and dev environment terraform directory configs

* save work setting up example organization

* remove org resources for now, we don't need to set up an entire organization for this example project. Doing so is outside scope and will easily take another 6 months to get quality terraform configs that set up an entire GCP organization

* update terraform dev environment docs

* save work

* add infra idea scope to vcs

* intial set up project and document the steps

* rename 'terraform' folder to 'gcp' to indicate all resources are Google Cloud centric

* scaffold network terraform module and add zone variable

* add gcp-apis module for enabling google services in our GCP project, initial network module

* set default network name to 'haztrak'

* scaffold k8 modules
  • Loading branch information
dpgraham4401 authored Oct 18, 2023
1 parent edd5372 commit cbabb54
Show file tree
Hide file tree
Showing 16 changed files with 258 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .idea/scopes/infra.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions infra/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
**/*-key.json

**/.terraform/*
*.tfstate
*.tfstate.*
crash.log
crash.*.log
*.tfvars
*.tfvars.json
override.tf
override.tf.json
*_override.tf
*_override.tf.json
42 changes: 42 additions & 0 deletions infra/gcp/dev/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions infra/gcp/dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Terraform configs for an example GCP Development Environment

This directory is an example set of terraform configs that allow us to provision the necessary resources for an example GCP deployment. It is not intended to be used as-is, but rather as an example. Setting up terraform configs for an entire GCP organization is outside the scope of this project.

## Prerequisites

1. [Terraform](https://www.terraform.io/downloads.html) installed
- See [GitHub Actions](/.github/workflows) workflow file for list of supported terraform versions
2. An account on Google Cloud (GCP) with the necessary IAM permissions for your project.
- ToDo: list necessary Permissions

## Setup

### Manual or pre-configured resources

While haztrak encourages users store all infrastructure as code that can be checked into VCS,
for purposes of this demonstration, a pre-configured project and access is necessary.

1. Create the project
- This could be bootstrapped from a separate terraform module/directory or manually created in the GCP console.
2. Ensure your account has sufficient permissions
- `roles/iam.serviceAccountCreater`
3. Enable the Google APIs that will allow us to manage our infrastructure with terraform.
- `gcloud config set project <project_id>`
- `gcloud services enable iamcredentials.googleapis.com cloudresourcemanager.googleapis.com storage.googleapis.com`
4. Create a [service account for terraform](https://cloud.google.com/iam/docs/service-accounts-create)
- `gcloud iam service-accounts create <sa_name> --display-name "Terraform Service Account"`
- The account will need the following permissions
- `roles/storage.objectAdmin`
- `gcloud projects add-iam-policy-binding <project_id> -dev-test-123 --member="serviceAccount:<sa_name>@haztrak-epa-dev-test-123.iam.gserviceaccount.com" --role=roles/storage.objectAdmin --role=roles/serviceusage.serviceUsageAdmin`
5. [Create a GCP bucket](https://cloud.google.com/storage/docs/creating-buckets#storage-create-bucket-cli) to hold Terraform remote state
- `gcloud storage buckets create gc://<bucket_name> --project <project_id>`

### Terraform Initialization and Apply

1. Modify the location of the remote state bucket in `backend.tf` to match the id of the bucket you created in the previous step.
2. Initialize terraform
- `terraform init`
3. Create a `terraform.tfvars` file with the following contents:
- `project_id = "<project_id>"`
4. Apply terraform
- `terraform apply`
18 changes: 18 additions & 0 deletions infra/gcp/dev/backend.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
terraform {
backend "gcs" {
bucket = "haztrak-terraform-remote-state-epa-test-123"
prefix = "terraform/dev"
}
required_providers {
google = {
source = "hashicorp/google"
version = "4.78.0"
}
}
}

provider "google" {
project = var.project
region = var.region
zone = var.zone
}
15 changes: 15 additions & 0 deletions infra/gcp/dev/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module "gcp_apis" {
source = "../modules/gcp-apis"

project = var.project
services = [
"compute.googleapis.com",
]
}

module "vpc" {
source = "../modules/network"
project = var.project
region = var.region
depends_on = [module.gcp_apis]
}
Empty file added infra/gcp/dev/outputs.tf
Empty file.
35 changes: 35 additions & 0 deletions infra/gcp/dev/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
variable "project" {
description = "The project ID to deploy to"
type = string

validation {
condition = length(var.project) > 0
error_message = "You must provide a project ID"
}

}

variable "region" {
description = "The default region to deploy to"
default = "us-east1"
type = string
validation {
condition = can(regex("^[a-z]{2,}-[a-z]*[1-9]{1}$", var.region))
error_message = "Invalid GCP region format. See 'gcloud compute regions list' for available options"
}
}

variable "environment" {
type = string
description = "The environment to deploy to"
default = "dev"
validation {
condition = contains(["dev", "prod", "test"], var.environment)
error_message = "Environment must be one of [dev, prod, test]"
}
}

variable "zone" {
type = string
description = "the default zone to use for the terraform GCP provider"
}
7 changes: 7 additions & 0 deletions infra/gcp/modules/gcp-apis/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Loop through each GCP API, passed as a string, and enable it
resource "google_project_service" "enabled_services" {
for_each = toset(var.services)

project = var.project
service = each.key
}
21 changes: 21 additions & 0 deletions infra/gcp/modules/gcp-apis/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
variable "services" {
description = "List of Google Cloud API names to enable."
type = list(string)
validation {
# condition = length([
# for service in var.services : service if can(regex("[a-z.]*", service)) == true
# ])
condition = can([for s in var.services : regex("^[a-zA-Z.]*googleapis.com$", s)])
error_message = "Service names must follow the format: 'serviceusage.googleapis.com'"
}
}

variable "project" {
description = "The Google Cloud project ID to enable services in."
type = string
validation {
error_message = "GCP project length must be greater than zero"
condition = length(var.project) > 0
}

}
Empty file added infra/gcp/modules/k8/main.tf
Empty file.
Empty file added infra/gcp/modules/k8/outputs.tf
Empty file.
Empty file.
14 changes: 14 additions & 0 deletions infra/gcp/modules/network/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
locals {
vpc_name = var.environment == "prod" ? var.vpc_name : "${var.vpc_name}-dev"
database_subnet_name = var.environment == "prod" ? "${var.project}-database-subnet-prod" : "${var.project}-database-subnet-dev"
}
module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 7.1"
project_id = var.project
network_name = local.vpc_name
routing_mode = "GLOBAL"
auto_create_subnetworks = false
subnets = var.subnets
}
7 changes: 7 additions & 0 deletions infra/gcp/modules/network/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "network" {
value = module.vpc.network
}

output "vpc" {
value = module.vpc.network_id
}
41 changes: 41 additions & 0 deletions infra/gcp/modules/network/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# network inputs

variable "vpc_name" {
type = string
description = "The name of the VPC"
default = "haztrak"
}

variable "project" {
description = "The GCP project to deploy to"
type = string
}

variable "region" {
description = "The region to deploy to"
type = string
}

variable "environment" {
description = "The environment to deploy to"
type = string
default = "dev"
validation {
condition = contains(["dev", "prod"], var.environment)
error_message = "Environment must be one of [dev, prod]"
}
}

# Note: A VPC is a global resource, subnets are regional.
variable "subnets" {
description = "Any subnets of the VPC."
type = list(object({
subnet_name = string
subnet_ip = string
subnet_region = string
subnet_private_access = optional(string)
subnet_private_ipv6_access = optional(string)
description = optional(string)
}))
default = []
}

0 comments on commit cbabb54

Please sign in to comment.