Terraform para GCP: Guia Completo de Deploy
Publicado no blog Vivo de Código
Introdução
Gerenciar infraestrutura manualmente no console do Google Cloud é algo que funciona quando você tem dois ou três recursos. Quando o projeto cresce, quando a equipe cresce, quando você precisa replicar ambientes, essa abordagem se torna um pesadelo. É nesse cenário que o Terraform entra como peça fundamental.
O Terraform é uma ferramenta de Infrastructure as Code (IaC) criada pela HashiCorp que permite declarar, versionar e provisionar recursos de infraestrutura de forma previsível e repetível. Quando combinado com o Google Cloud Platform (GCP), ele oferece uma experiência poderosa para times que precisam de controle total sobre seus ambientes.
Neste guia, vou compartilhar o que aprendi em anos trabalhando com essa combinação em projetos de produção. Vamos desde o setup inicial até boas práticas avançadas, passando por exemplos reais que você pode adaptar ao seu contexto.
Por que usar Terraform com GCP
Vantagens da infraestrutura declarativa no Google Cloud
O GCP já oferece o Deployment Manager como ferramenta nativa de IaC. Então por que escolher o Terraform? Existem razões sólidas para isso.
Primeiro: o Terraform é agnóstico de provedor. Se sua empresa usa GCP para workloads de Machine Learning e AWS para o restante, você gerencia tudo com a mesma linguagem (HCL) e o mesmo fluxo de trabalho. Isso reduz a carga cognitiva do time de forma significativa.
Segundo: o ecossistema do Terraform é imenso. O provider oficial do Google Cloud é mantido em parceria direta entre HashiCorp e Google, o que garante suporte rápido a novos serviços. Além disso, a comunidade produz módulos prontos para cenários comuns como criação de clusters GKE, redes VPC e configurações de IAM.
Terceiro: o modelo de estado (state) do Terraform permite que você saiba exatamente o que está provisionado, o que mudou e o que será destruído antes de qualquer ação. Esse ciclo de plan e apply é essencial para ambientes de produção.
Casos de uso reais onde essa combinação brilha
Na prática, os cenários onde Terraform com GCP se destaca incluem: provisionamento de clusters GKE para times de desenvolvimento, criação de ambientes efêmeros para testes de integração, gerenciamento de políticas de IAM em organizações com múltiplos projetos, e configuração de pipelines de dados com BigQuery, Pub/Sub e Cloud Functions.
Um caso que vivenciei pessoalmente: uma fintech precisava criar ambientes completos (VPC, Cloud SQL, GKE, Cloud Run) para cada nova feature branch. Com Terraform e workspaces, automatizamos esse processo e reduzimos o tempo de provisionamento de dois dias para quinze minutos.
Setup inicial do Terraform com GCP
Instalando e configurando o ambiente
Antes de escrever qualquer código, você precisa do Terraform CLI e do Google Cloud SDK instalados. Vamos ao passo a passo.
Instale o Terraform (usando o gerenciador oficial da HashiCorp):
# No Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
# Verificando a instalação
terraform versionInstale e configure o Google Cloud SDK:
# Autenticação para uso local
gcloud auth application-default login
# Definindo o projeto padrão
gcloud config set project meu-projeto-idCriando a Service Account para o Terraform
Em ambientes de CI/CD, você nunca deve usar credenciais pessoais. Crie uma Service Account dedicada:
# Criando a service account
gcloud iam service-accounts create terraform-sa \
--display-name="Terraform Service Account" \
--project=meu-projeto-id
# Atribuindo permissões (ajuste conforme necessidade)
gcloud projects add-iam-policy-binding meu-projeto-id \
--member="serviceAccount:[email protected]" \
--role="roles/editor"
# Gerando a chave (para CI/CD)
gcloud iam service-accounts keys create terraform-key.json \
--iam-account=terraform-sa@meu-projeto-id.iam.gserviceaccount.comConfiguração do backend remoto com GCS
Armazenar o arquivo de estado localmente é um risco. Use o Google Cloud Storage como backend remoto:
# Criando o bucket para o state
gsutil mb -p meu-projeto-id -l us-central1 gs://meu-projeto-terraform-state
gsutil versioning set on gs://meu-projeto-terraform-stateExemplo prático: infraestrutura completa para uma aplicação web
Vamos criar uma infraestrutura real: uma aplicação Node.js rodando no Cloud Run, com um banco Cloud SQL PostgreSQL e uma VPC dedicada.
Estrutura de arquivos do projeto Terraform
infra/
main.tf
variables.tf
outputs.tf
modules/
network/
main.tf
variables.tf
outputs.tf
database/
main.tf
variables.tf
outputs.tf
cloudrun/
main.tf
variables.tf
outputs.tfArquivo principal de configuração
# main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
backend "gcs" {
bucket = "meu-projeto-terraform-state"
prefix = "terraform/state"
}
}
provider "google" {
project = var.project_id
region = var.region
}
module "network" {
source = "./modules/network"
project_id = var.project_id
region = var.region
}
module "database" {
source = "./modules/database"
project_id = var.project_id
region = var.region
vpc_id = module.network.vpc_id
private_ip_range = module.network.private_ip_range
db_password = var.db_password
}
module "cloudrun" {
source = "./modules/cloudrun"
project_id = var.project_id
region = var.region
image = var.container_image
db_connection_name = module.database.connection_name
db_password = var.db_password
vpc_connector_id = module.network.vpc_connector_id
}# variables.tf
variable "project_id" {
description = "ID do projeto GCP"
type = string
}
variable "region" {
description = "Região padrão para os recursos"
type = string
default = "us-central1"
}
variable "db_password" {
description = "Senha do banco de dados PostgreSQL"
type = string
sensitive = true
}
variable "container_image" {
description = "Imagem Docker para o Cloud Run"
type = string
}Módulo de rede VPC
# modules/network/main.tf
resource "google_compute_network" "main" {
name = "app-vpc"
project = var.project_id
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "main" {
name = "app-subnet"
project = var.project_id
region = var.region
network = google_compute_network.main.id
ip_cidr_range = "10.0.0.0/24"
}
resource "google_compute_global_address" "private_ip" {
name = "private-ip-range"
project = var.project_id
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = google_compute_network.main.id
}
resource "google_service_networking_connection" "private_vpc" {
network = google_compute_network.main.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.private_ip.name]
}
resource "google_vpc_access_connector" "connector" {
name = "vpc-connector"
project = var.project_id
region = var.region
network = google_compute_network.main.name
ip_cidr_range = "10.8.0.0/28"
}Módulo do Cloud SQL
# modules/database/main.tf
resource "google_sql_database_instance" "postgres" {
name = "app-db-instance"
project = var.project_id
region = var.region
database_version = "POSTGRES_15"
depends_on = [var.private_ip_range]
settings {
tier = "db-custom-2-4096"
availability_type = "REGIONAL"
ip_configuration {
ipv4_enabled = false
private_network = var.vpc_id
}
backup_configuration {
enabled = true
point_in_time_recovery_enabled = true
start_time = "03:00"
}
}
deletion_protection = true
}
resource "google_sql_database" "app" {
name = "appdb"
instance = google_sql_database_instance.postgres.name
project = var.project_id
}
resource "google_sql_user" "app" {
name = "appuser"
instance = google_sql_database_instance.postgres.name
project = var.project_id
password = var.db_password
}Executando o provisionamento
# Inicializando o Terraform
terraform init
# Visualizando o plano de execução
terraform plan -var="project_id=meu-projeto-id" \
-var="db_password=senha-segura-aqui" \
-var="container_image=gcr.io/meu-projeto-id/app:latest"
# Aplicando as mudanças
terraform apply -var="project_id=meu-projeto-id" \
-var="db_password=senha-segura-aqui" \
-var="container_image=gcr.io/meu-projeto-id/app:latest"Automatizando com CDKTF e TypeScript
Se você prefere escrever infraestrutura em TypeScript em vez de HCL, o CDK for Terraform (CDKTF) é a ferramenta ideal. Ele permite usar toda a tipagem e ferramentas do ecossistema TypeScript para definir recursos.
Exemplo 1: provisionando um bucket GCS com CDKTF
// main.ts
import { Construct } from "constructs";
import { App, TerraformStack, GcsBackend } from "cdktf";
import { GoogleProvider } from "@cdktf/provider-google/lib/provider";
import { StorageBucket } from "@cdktf/provider-google/lib/storage-bucket";
import { StorageBucketIamMember } from "@cdktf/provider-google/lib/storage-bucket-iam-member";
class StorageStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
const projectId = "meu-projeto-id";
const region = "us-central1";
new GoogleProvider(this, "google", {
project: projectId,
region: region,
});
new GcsBackend(this, {
bucket: "meu-projeto-terraform-state",
prefix: "cdktf/storage",
});
const dataBucket = new StorageBucket(this, "data-bucket", {
name: `${projectId}-data-lake`,
location: region,
forceDestroy: false,
uniformBucketLevelAccess: true,
versioning: { enabled: true },
lifecycleRule: [
{
condition: { age: 90 },
action: { type: "SetStorageClass", storageClass: "NEARLINE" },
},
{
condition: { age: 365 },
action: { type: "SetStorageClass", storageClass: "COLDLINE" },
},
],
});
new StorageBucketIamMember(this, "data-bucket-reader", {
bucket: dataBucket.name,
role: "roles/storage.objectViewer",
member: `serviceAccount:app-sa@${projectId}.iam.gserviceaccount.com`,
});
}
}
const app = new App();
new StorageStack(app, "storage");
app.synth();Exemplo 2: criando um cluster GKE com CDKTF
// gke-stack.ts
import { Construct } from "constructs";
import { App, TerraformStack, TerraformOutput } from "cdktf";
import { GoogleProvider } from "@cdktf/provider-google/lib/provider";
import { ContainerCluster } from "@cdktf/provider-google/lib/container-cluster";
import { ContainerNodePool } from "@cdktf/provider-google/lib/container-node-pool";
import { ComputeNetwork } from "@cdktf/provider-google/lib/compute-network";
import { ComputeSubnetwork } from "@cdktf/provider-google/lib/compute-subnetwork";
interface GkeStackConfig {
projectId: string;
region: string;
environment: string;
minNodes: number;
maxNodes: number;
machineType: string;
}
class GkeStack extends TerraformStack {
constructor(scope: Construct, id: string, config: GkeStackConfig) {
super(scope, id);
new GoogleProvider(this, "google", {
project: config.projectId,
region: config.region,
});
const network = new ComputeNetwork(this, "gke-network", {
name: `gke-vpc-${config.environment}`,
autoCreateSubnetworks: false,
project: config.projectId,
});
const subnet = new ComputeSubnetwork(this, "gke-subnet", {
name: `gke-subnet-${config.environment}`,
project: config.projectId,
region: config.region,
network: network.id,
ipCidrRange: "10.0.0.0/20",
secondaryIpRange: [
{ rangeName: "pods", ipCidrRange: "10.4.0.0/14" },
{ rangeName: "services", ipCidrRange: "10.8.0.0/20" },
],
});
const cluster = new ContainerCluster(this, "gke-cluster", {
name: `app-cluster-${config.environment}`,
project: config.projectId,
location: config.region,Guias Relacionados
Explore mais guias técnicos
Mais de 100 guias práticos de integração entre frameworks, ferramentas e plataformas.
Ver todos os guias