This repository contains a complete, production-ready setup for deploying a simple Node.js "Hello World" API to Google Cloud Run. The entire infrastructure is managed using Terraform, and the CI/CD pipeline is automated with GitHub Actions.
The project emphasizes best practices, including Infrastructure as Code (IaC), automated testing, security scanning, and secure authentication using Workload Identity Federation.
The workflow is designed to be fully automated upon a push to the main branch:
- Local Development: A developer makes changes and commits. Husky git hooks run linting and tests locally to catch errors early.
- Git Push: Code is pushed to the GitHub repository.
- GitHub Actions CI:
- A workflow is triggered.
- The
lint-and-testjob runs, ensuring code quality and correctness.
- GitHub Actions CD:
- The
build-scan-and-pushjob builds a Docker container. - It securely authenticates to Google Cloud using Workload Identity Federation (no static keys).
- The container image is scanned for
HIGHandCRITICALvulnerabilities using Trivy. - If the scan passes, the image is pushed to Google Artifact Registry.
- The
deploy-to-cloud-runjob deploys the new container image to Google Cloud Run.
- The
graph TD
A[Developer commits code] -- git push --> B{GitHub Repository}
B -- triggers --> C[GitHub Actions Workflow]
C --> D[Lint & Test]
D -- on success --> E[Build, Scan & Push]
subgraph E [ ]
E1[Authenticate to GCP via WIF]
E2[Build Docker Image]
E3[Scan with Trivy]
E4[Push to Artifact Registry]
end
E -- on success --> F[Deploy to Cloud Run]
F --> G[Google Cloud Run Service]
style G fill:#4CAF50,stroke:#333,stroke-width:2px,color:#fff
- Infrastructure as Code: All Google Cloud resources are defined and managed by Terraform.
- Automated CI/CD: Fully automated build, test, scan, and deploy pipeline using GitHub Actions.
- Secure Authentication: Uses Workload Identity Federation to grant GitHub Actions temporary, keyless access to GCP.
- Container Security: Multi-stage, non-root Docker builds and vulnerability scanning with Trivy.
- Developer Experience: Pre-commit and pre-push hooks using Husky to maintain code quality locally.
- Principle of Least Privilege: Separate service accounts for CI/CD operations and for the Cloud Run service at runtime.
Follow these steps to deploy the infrastructure and the application.
- A Google Cloud Platform (GCP) project with billing enabled.
- A GitHub account and a repository for this project.
- Google Cloud SDK (
gcloud) installed and authenticated. - Terraform CLI (
>=1.0) installed.
-
Clone the repository:
git clone https://github.com/athallabf/gcp-terraform cd gcp-terraform -
Configure Terraform variables: Navigate to the
terraformdirectory. Create aterraform.tfvarsfile by copying the example:cd terraform cp terraform.tfvars.example terraform.tfvarsEdit
terraform.tfvarswith your specific values:# terraform/terraform.tfvars gcp_project_id = "your-gcp-project-id" gcp_region = "us-central1" github_org = "your-github-username-or-org" github_repo = "name-of-your-github-repo"
-
Initialize and apply Terraform:
terraform init terraform plan terraform apply --auto-approve
Terraform will create all the necessary GCP resources. Note the outputs at the end, as you will need them for the next step.
The GitHub Actions workflow needs credentials to interact with your GCP project. These are provided by the resources you just created.
- In your GitHub repository, go to Settings > Secrets and variables > Actions.
- Create the following three Repository secrets using the outputs from the
terraform applycommand:
| Secret Name | Value from Terraform Output | Example Value |
|---|---|---|
GCP_PROJECT_ID |
Your GCP Project ID | my-gcp-project-12345 |
GCP_SERVICE_ACCOUNT |
service_account_email |
hello-world-api-ci-cd@my-gcp-project-12345.iam.gserviceaccount.com |
GCP_WORKLOAD_IDENTITY_PROVIDER |
workload_identity_provider |
projects/123456789/locations/global/workloadIdentityPools/hello-world-api-gh-pool/providers/github-provider |
-
Commit and push your changes to the
mainbranch. If you've just set up the repository, a simple change will do:git add . git commit -m "Initial commit and infrastructure setup" git push origin main
-
Go to the Actions tab in your GitHub repository. You will see the workflow running. Once it completes, your application will be deployed and accessible at the URL provided in the Cloud Run service details.
Security is managed by adhering to the principle of least privilege.
| Service Account | Roles Granted | Purpose |
|---|---|---|
| CI/CD Service Account | roles/run.admin |
To deploy and manage the Cloud Run service. |
(...-ci-cd@...) |
roles/artifactregistry.writer |
To push Docker images to the Artifact Registry repository. |
roles/iam.serviceAccountUser |
To allow the service account to be impersonated and to attach the runtime SA to the Cloud Run service. | |
roles/iam.workloadIdentityUser (via WIF) |
Allows identities from the GitHub WIF pool (i.e., your GitHub Actions runner) to impersonate this service account. | |
| Cloud Run Runtime SA | None by default | The identity of the application itself at runtime. It has no permissions by default but can be granted specific access (e.g., to a database) without affecting the CI/CD pipeline's permissions. |
(...-runtime@...) |
Public Access: The Cloud Run service is made publicly accessible by granting the roles/run.invoker role to allUsers.
- Workload Identity Federation vs. Service Account Keys: We use WIF to avoid static, long-lived service account keys. Keys are a major security risk if leaked. WIF provides short-lived, automatically-managed credentials to the GitHub Actions runner, scoped only to a specific repository and branch.
- Terraform for IaC: Using Terraform ensures that our infrastructure is version-controlled, repeatable, and easy to audit. It prevents manual configuration drift and simplifies environment replication.
- Google Cloud Run: Chosen for its serverless nature. It automatically scales (even to zero), is cost-effective for services with variable traffic, and abstracts away all infrastructure management.
- Multi-Stage Dockerfile: The
Dockerfileuses abuildstage to install dependencies and a final, cleanrunstage. This results in a smaller, more secure production image that doesn't contain build tools or source code. The container also runs as a non-root user for enhanced security. - Automated Vulnerability Scanning: Integrating Trivy into the CI/CD pipeline ("shifting left") allows us to catch critical vulnerabilities before they are ever deployed to production, failing the build automatically.
This setup is designed to be highly cost-effective, especially for low-traffic applications.
- Google Cloud Run: Has a generous free tier that includes 2 million requests and 360,000 GB-seconds of compute per month. For a simple API like this, costs will likely be $0/month.
- Google Artifact Registry: Also has a free tier of 0.5 GB of storage per month. The Docker image is small, so storage costs will be negligible, likely $0/month.
- GitHub Actions: Free for public repositories.
Conclusion: For a hobby project or a low-traffic production service, the monthly cost for this entire setup is expected to be $0 or very close to it. Costs will only scale with significant traffic to the Cloud Run service.