Creating a GCP Project with Terraform

Like most jobs today, mine requires me to automate as much of it as possible. One of the things that seemed like an easy goal was to automate the creation of a GCP Project using an automation tool. Today we are looking at doing this, using Terraform.

Project Layout

When creating this I laid out the files in easy to use sections. I also made sure to use the depends_on line a lot so I could ensue that everything was working in the order I wanted

Project Files

Below, I will break down each file and what it is used for, as well as the code inside of it.

project.tf

In this file, I look for a few variables that help me create the project, including the name, what folder it should live in, and a simple label to be applied to it. I also set up Google as the provider since I will be using GCP. To make life easy, I set up a service account ahead of time that it will create projects and modify IAM throughout my environment. I then took the JSON key from it and I insert the path so the script knows what to use.

variable "project_namer" {
type = string
description = "The name of the project"
}

variable "project_folder" {
type = string
description = "The folder ID the project will live in"
}

variable "label1" {
type = string
description = "A label that we can use in the project"
}
//**********
//Terraform Provider Info
//***********

// Setup Google as provider for this project
// credentials is a file that has the key for the terraform service // account

provider "google" {
credentials = "FULL PATH TO CREDENTIALS"
region = "us-east1"
zone = "us-east1-b"
}

// Setup project, billing account, and labels
resource "google_project" "project" {
name = var.project_name
project_id = var.project_name
billing_account = "INSERT BILLING ACCOUNT # HERE"
folder_id = var.project_folder
labels = {
label1 = var.label1
    }
}

services.tf

In this file I lay out all the APIs I need turned on. I have shortened this list, but you can use it to get a guide on what it should look like

//****************************************
// Services and APIs
//****************************************

resource "google_project_service" "compute_api" {
project = google_project.project.project_id
service = "compute.googleapis.com"

disable_dependent_services = true
}

resource "google_project_service" "deploymentmanager_api" {
project = google_project.project.project_id
service = "deploymentmanager.googleapis.com"

disable_dependent_services = true
}

iam.tf

This file is used to set all of the IAM permissions in the project. It can get quite large if you have a lot of sets you need to make, and I am sure there are better ways to write it, but this is currently what is working for us. I have also tried shortened this list.

You will notice these code blocks all have a line depends_on. This forces terraform to wait until the code block in that line has finished running. This is important to have since it helps make sure accounts have been created or APIs have been enabled before terraform tries to run this.

//****************************************
// IAM Permissions
//****************************************

resource "google_project_iam_member" "owner" {
depends_on = [google_service_account.service_account]
project = google_project.project.project_id
role = "roles/owner"
member = "serviceAccount:service_account@serviceaccount.com"
}

resource "google_project_iam_member" "owner2" {
depends_on = [google_service_account.service_account]
project = google_project.project.project_id
role = "roles/owner"
member = "user:email@email.com"
}

network.tf

Here we setup a basic VPC network with a NAT Gateway so there is no need for public IPs.

//****************************************
// Networking
//****************************************
resource "google_compute_network" "my_vpc_network" {
depends_on = [google_project_service.compute_api]
name = join("", [google_project.project.project_id, "-vpc"])
project = google_project.project.project_id
auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "my_network" {
depends_on = [google_compute_network.my_vpc_network]
name = "project-subnet"
project = google_project.project.project_id
ip_cidr_range = "10.142.0.0/20"
region = "us-east1"
network = join("", [google_project.project.project_id, "-vpc"])
private_ip_google_access = true
}

resource "google_compute_router" "default" {
depends_on = [google_compute_subnetwork.default]
name = join("", [google_project.project.project_id, "-cr"])
project = google_project.project.project_id
network = join("", [google_project.project.project_id, "-vpc"])
bgp {
asn = 64512
advertise_mode = "DEFAULT"
}
}

resource "google_compute_router_nat" "my_nat_gateway" {
depends_on = [google_compute_router.default]
name = join("", [google_project.project.project_id, "-natgwy"])
project = google_project.project.project_id
router = join("", [google_project.project.project_id, "-cr"])
region = "us-east1"
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}

output "project_id" {
value = google_project.project.project_id
}

storage.tf

In this block we create a simple storage bucket for project data. Note that we have a line depends_on and we make sure the storage API has been enabled. If it is not, the terraform script could possibly fail.

//****************************************
// Storage Buckets
//****************************************
resource "google_storage_bucket" "my_storage_bucket" {
depends_on = [google_project_service.storage-component_api]
project = google_project.project.project_id
name = join("", [google_project.project.project_id, "-project-data"])
location = "us-east1"
}

serviceaccounts.tf

The following code makes a simple service account inside the project that we can use.

//****************************************
// Service Accounts
//****************************************

// Normal service account used for everything inside of the project
resource "google_service_account" "my_service_account" {
project = google_project.project.project_id
account_id = join("", ["svc-", google_project.project.project_id])
display_name = join("", ["svc-", google_project.project.project_id])
}

terraform.tfvars

You can also create a terraform.tfvars file and save all your values in that file so that Terraform will be able to pick them up while running.

project_name=PROJECT_NAME
project_folder=PROJECT_FOLDER
label1=LABEL1_DATA

Running the terraform script

Running the script is pretty easy. The first step is making sure you have terraform installed by going to their website. Once you have this installed and all the scripts are in the same directory, you can run some simple commands.

To plan the terraform changes, you can run the following command and terraform will print out everything it wants to do.

terraform plan

To apply the terraform changes, you can run the following command and terraform will print out everything it wants to do, and then do it

terraform apply

Hope this helps.

#StayAwesome