5-app-infra/modules/publish_artifacts/main.tf (188 lines of code) (raw):
/**
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
resource "google_project_service_identity" "artifact_registry_agent" {
provider = google-beta
project = var.project_id
service = "artifactregistry.googleapis.com"
}
resource "google_project_service_identity" "storage_agent" {
provider = google-beta
project = var.project_id
service = "storage.googleapis.com"
}
resource "google_kms_crypto_key_iam_member" "artifact-kms-key-binding" {
crypto_key_id = var.kms_crypto_key
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:${google_project_service_identity.artifact_registry_agent.email}"
}
resource "google_artifact_registry_repository" "repo" {
provider = google-beta
location = var.region
repository_id = local.name_var
description = var.description
format = var.format
cleanup_policy_dry_run = var.cleanup_policy_dry_run
project = data.google_project.project.project_id
#Customer Managed Encryption Keys
#Control ID: COM-CO-2.3
#NIST 800-53: SC-12 SC-13
#CRI Profile: PR.DS-1.1 PR.DS-1.2 PR.DS-2.1 PR.DS-2.2 PR.DS-5.1
kms_key_name = var.kms_crypto_key
#Cleanup policy
#Control ID: AR-CO-6.1
#NIST 800-53: SI-12
#CRI Profile: PR.IP-2.1 PR.IP-2.2 PR.IP-2.3
dynamic "cleanup_policies" {
for_each = var.cleanup_policies
content {
id = cleanup_policies.value.id
action = cleanup_policies.value.action
dynamic "condition" {
for_each = cleanup_policies.value.condition != null ? [cleanup_policies.value.condition] : []
content {
tag_state = condition.value[0].tag_state
tag_prefixes = condition.value[0].tag_prefixes
package_name_prefixes = condition.value[0].package_name_prefixes
older_than = condition.value[0].older_than
}
}
dynamic "most_recent_versions" {
for_each = cleanup_policies.value.most_recent_versions != null ? [cleanup_policies.value.most_recent_versions] : []
content {
package_name_prefixes = most_recent_versions.value[0].package_name_prefixes
keep_count = most_recent_versions.value[0].keep_count
}
}
}
}
depends_on = [
google_kms_crypto_key_iam_member.artifact-kms-key-binding,
]
}
resource "google_artifact_registry_repository_iam_member" "project" {
for_each = toset(local.trigger_sa_roles)
project = var.project_id
repository = google_artifact_registry_repository.repo.repository_id
location = var.region
role = each.key
member = google_service_account.trigger_sa.member
}
resource "google_service_account" "trigger_sa" {
account_id = var.docker_build_sa_id
display_name = "Docker Build Service Account"
project = var.project_id
}
resource "google_service_account_iam_member" "impersonate" {
service_account_id = google_service_account.trigger_sa.id
role = "roles/iam.serviceAccountUser"
member = local.current_member
}
resource "random_string" "suffix" {
length = 10
special = false
upper = false
}
// Add Service Agent for Storage
resource "google_kms_crypto_key_iam_member" "storage_agent" {
crypto_key_id = var.kms_crypto_key
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:service-${data.google_project.project.number}@gs-project-accounts.iam.gserviceaccount.com"
depends_on = [google_project_service_identity.storage_agent]
#member = "serviceAccount:${google_project_service_identity.storage.email}"
}
resource "google_storage_bucket" "cloud_build_logs" {
name = "artifacts-pipeline-logs-${random_string.suffix.result}"
storage_class = "REGIONAL"
project = var.project_id
location = var.region
uniform_bucket_level_access = true
encryption {
default_kms_key_name = var.kms_crypto_key
}
depends_on = [google_kms_crypto_key_iam_member.storage_agent]
}
resource "google_sourcerepo_repository_iam_member" "repo_reader" {
repository = data.google_sourcerepo_repository.artifacts_repo.id
role = "roles/source.reader"
member = google_service_account.trigger_sa.member
}
resource "google_storage_bucket_iam_member" "storage_admin" {
bucket = google_storage_bucket.cloud_build_logs.name
role = "roles/storage.admin"
member = google_service_account.trigger_sa.member
}
resource "google_cloudbuild_trigger" "docker_build" {
name = "docker-build"
project = var.project_id
location = var.region
service_account = google_service_account.trigger_sa.id
trigger_template {
branch_name = "^main$"
repo_name = var.name
invert_regex = false
}
build {
logs_bucket = google_storage_bucket.cloud_build_logs.name
timeout = "1800s"
step {
id = "unshallow"
name = "gcr.io/cloud-builders/git"
entrypoint = "/bin/bash"
args = [
"-c",
"git fetch --unshallow"
]
}
step {
id = "select-folder"
name = "gcr.io/cloud-builders/git"
entrypoint = "/bin/bash"
args = [
"-c",
<<-EOT
changed_files=$(git diff $${COMMIT_SHA}^1 --name-only -r)
changed_folders=$(echo "$changed_files" | awk -F/ '{print $2}' | sort | uniq )
for folder in $changed_folders; do
echo "Found docker folder: $folder"
echo $folder >> /workspace/docker_build
done
EOT
]
}
step {
id = "build-image"
wait_for = ["select-folder"]
name = "gcr.io/cloud-builders/docker"
entrypoint = "/bin/bash"
args = [
"-c",
<<-EOT
build_path="/workspace/docker_build"
while IFS= read -r line; do
docker build -t ${var.region}-docker.pkg.dev/$PROJECT_ID/c-publish-artifacts/$line images/$line
done < "$build_path"
EOT
]
}
step {
id = "push-image"
wait_for = ["select-folder", "build-image"]
name = "gcr.io/cloud-builders/docker"
entrypoint = "/bin/bash"
args = [
"-c",
<<-EOT
build_path="/workspace/docker_build"
while IFS= read -r line; do
docker push ${var.region}-docker.pkg.dev/$PROJECT_ID/c-publish-artifacts/$line
done < "$build_path"
EOT
]
}
}
depends_on = [google_service_account_iam_member.impersonate]
}