hashicorp / vscode-terraform

HashiCorp Terraform VSCode extension
https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform
Mozilla Public License 2.0
911 stars 180 forks source link

Secret_environment_variables in the service_config block of google_cloudfunctions2_function does not accept array #1775

Closed mtander closed 4 weeks ago

mtander commented 4 weeks ago

Extension Version

v2.30.2

VS Code Version

Version: 1.89.1 (Universal) Commit: dc96b837cf6bb4af9cd736aa3af08cf8279f7685 Date: 2024-05-07T05:14:24.611Z Electron: 28.2.8 ElectronBuildId: 27744544 Chromium: 120.0.6099.291 Node.js: 18.18.2 V8: 12.0.267.19-electron.0 OS: Darwin arm64 23.4.0

Operating System

macOS Sonoma Version 14.4.1

Terraform Version

Terraform v1.8.5 on darwin_arm64 + provider registry.terraform.io/hashicorp/archive v2.4.2 + provider registry.terraform.io/hashicorp/google v5.32.0 + provider registry.terraform.io/hashicorp/random v3.6.2

Steps to Reproduce

  1. View the following file main.tf in VSCode

  2. See highlighted error An attribute named secret_environment_variables is not expected here

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~>5.32.0"
    }
  }
}

locals {
  cf_zip_archive_name = "cf-scanprocessing-${data.archive_file.cf_source_zip.output_sha}.zip"
}

resource "random_id" "bucket_prefix" {
  byte_length = 8
}

resource "google_storage_bucket" "source_bucket" {
  name                        = "${random_id.bucket_prefix.hex}-gcf-source-bucket" # Every bucket name must be globally unique
  location                    = "us-east1"
  uniform_bucket_level_access = true
  project                     = var.project
  versioning {
    enabled = true
  }
}

data "archive_file" "cf_source_zip" {
  type        = "zip"
  output_path = "/tmp/function-source.zip"
  source_dir  = "."
  excludes    = ["./terraform.tfvars"]
}

resource "google_storage_bucket_object" "cf_source_zip" {
  name         = local.cf_zip_archive_name
  bucket       = google_storage_bucket.source_bucket.name
  source       = data.archive_file.cf_source_zip.output_path # Path to the zipped function source code
  content_type = "application/zip"
}

data "google_storage_project_service_account" "default" {
  project = var.project
}

resource "google_project_iam_member" "gcs_pubsub_publishing" {
  project = var.project
  role    = "roles/pubsub.publisher"
  member  = "serviceAccount:${data.google_storage_project_service_account.default.email_address}"
}

resource "google_service_account" "cloud_function_account" {
  account_id   = "cloudfunction-serviceaccount"
  display_name = "Google Cloud Function Service Account - used for both cloud fn and eventarc trigger in the test"
  project      = var.project
}

resource "google_secret_manager_secret_iam_binding" "binding" {
  project     = var.project
  secret_id   = "projects/${var.project}/secrets/DocumentOCRProjectId"
  role        = "roles/secretmanager.secretAccessor"
  members     = ["serviceAccount:${google_service_account.cloud_function_account.email}"]
  depends_on  = [google_service_account.cloud_function_account]
}

# Permissions on the service account used by the function and Eventarc trigger
resource "google_project_iam_member" "invoking" {
  project    = var.project
  for_each = toset([
    "roles/run.invoker",
    "roles/storage.objectViewer",
    "roles/documentai.apiUser"
#    "secretmanager.secretAccessor"
  ])
  role       = each.key
  member     = "serviceAccount:${google_service_account.cloud_function_account.email}"
  depends_on = [google_project_iam_member.gcs_pubsub_publishing, google_service_account.cloud_function_account]
}

resource "google_project_iam_member" "event_receiving" {
  project    = var.project
  role       = "roles/eventarc.eventReceiver"
  member     = "serviceAccount:${google_service_account.cloud_function_account.email}"
  depends_on = [google_project_iam_member.invoking, google_service_account.cloud_function_account]
}

resource "google_project_iam_member" "artifactregistry_reader" {
  project    = var.project
  role       = "roles/artifactregistry.reader"
  member     = "serviceAccount:${google_service_account.cloud_function_account.email}"
  depends_on = [google_project_iam_member.event_receiving, google_service_account.cloud_function_account]
}

resource "google_cloudfunctions2_function" "default" {
  depends_on = [
    google_project_iam_member.event_receiving,
    google_project_iam_member.artifactregistry_reader,
    google_secret_manager_secret_iam_binding.binding
  ]
  name        = "function-v2-scanprocessing"
  location    = "us-east1"
  description = "This gcp function is triggered upon a file being synced to the relevant GCP storage bucket.  Once triggered, it kicks off processing the scanned file."
  project     = var.project

  build_config {
    runtime     = "nodejs20"
    entry_point = "scanProcessing" # Set the entry point to the name of the exported function
    source {
      storage_source {
        bucket = google_storage_bucket.source_bucket.name
        object = google_storage_bucket_object.cf_source_zip.name
      }
    }
  }

  service_config {
    max_instance_count = 3
    min_instance_count = 1
    available_memory   = "256M"
    timeout_seconds    = 60

    # This works, but doesn't allow for multiple secret_environment_variables
    # secret_environment_variables {
    #   key         = "DocumentOCRProjectId"
    #   project_id  = var.project
    #   secret      = "projects/${var.project}/secrets/DocumentOCRProjectId"
    #   version     = "latest"
    # }

    # This doesn't work, but is what we want to do
    secret_environment_variables = [
      {
        key         = "DocumentOCRProjectId"
        project_id = var.project
        secret     = "projects/${var.project}/secrets/DocumentOCRProjectId"
        version    = "latest"
      },
      {
        key        = "DocumentOCRSplitterProcessorId""
        project_id = var.project
        secret     = "projects/${var.project}/secrets/DocumentOCRSplitterProcessorId""
        version    = "latest"
      }
    ]

    # leaving this commented out in case we ever want/need to input environment variables here.
    # environment_variables = {
    #     EXAMPLE = "iAmAnEnvVar"
    # }

    ingress_settings               = "ALLOW_INTERNAL_ONLY"
    all_traffic_on_latest_revision = true
    service_account_email          = google_service_account.cloud_function_account.email
  }

  event_trigger {
    trigger_region        = "us-east1" # The trigger must be in the same location as the bucket
    event_type            = "google.cloud.storage.object.v1.finalized"
    retry_policy          = "RETRY_POLICY_RETRY"
    service_account_email = google_service_account.cloud_function_account.email
    event_filters {
      attribute = "bucket"
      value     = "${var.scan_transfer_bucket_name}-${var.env}"
    }
  }
}

Expected Behavior

We should be able to pass in multiple blocks in the secret_environment_variables section without getting a syntax error from the extension. Additionally, the documentation should include an example of using more than one secret_environment_variable

Actual Behavior

Upon running terraform plan, I get the following message. I am not sure how to address this issue.

│ Error: Unsupported argument │ │ on main.tf line 131, in resource "google_cloudfunctions2_function" "default": │ 131: secret_environment_variables = [ │ │ An argument named "secret_environment_variables" is not expected here. Did you mean to define a block of type "secret_environment_variables"?

Terraform Configuration

No response

Project Structure

No response

Gist

No response

Anything Else?

No response

Workarounds

No response

References

No response

Help Wanted

Community Note

mtander commented 4 weeks ago

I figured out the syntax with some help. I'll leave this for posterity and close the bug.

(within service_config section of resource google_cloudfunctions2_function)

  service_config {
    max_instance_count = 3
    min_instance_count = 1
    available_memory   = "256M"
    timeout_seconds    = 60
    secret_environment_variables {
        key         = "DocumentOCRProjectId"
        project_id = var.project
        secret     = "projects/${var.project}/secrets/DocumentOCRProjectId"
        version    = "latest"
    }

    secret_environment_variables {
        key        = "DocumentOCRSplitterProcessorId"
        project_id = var.project
        secret     = "projects/${var.project}/secrets/DocumentOCRSplitterProcessorId"
        version    = "latest"
      }

    ingress_settings               = "ALLOW_INTERNAL_ONLY"
    all_traffic_on_latest_revision = true
    service_account_email          = google_service_account.cloud_function_account.email
  }