diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md
index 16c4912a8..7e5577e9a 100644
--- a/fast/stages/1-resman/README.md
+++ b/fast/stages/1-resman/README.md
@@ -329,8 +329,8 @@ terraform apply
| [custom_roles](variables-fast.tf#L54) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap |
| [factories_config](variables.tf#L20) | Configuration for the resource factories or external data. | object({…}) | | {} | |
| [fast_addon](variables-addons.tf#L17) | FAST addons configurations for stages 2. Keys are used as short names for the add-on resources. | map(object({…})) | | {} | |
-| [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | map(object({…})) | | {} | |
-| [fast_stage_3](variables-stages.tf#L117) | FAST stages 3 configurations. | map(object({…})) | | {} | |
+| [fast_stage_2](variables-stages.tf#L17) | FAST stages 2 configurations. | map(object({…})) | | {} | |
+| [fast_stage_3](variables-stages.tf#L117) | FAST stages 3 configurations. | map(object({…})) | | {} | |
| [groups](variables-fast.tf#L93) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap |
| [locations](variables-fast.tf#L109) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap |
| [org_policy_tags](variables-fast.tf#L153) | Organization policy tags. | object({…}) | | {} | 0-bootstrap |
diff --git a/fast/stages/1-resman/data/stage-2/project-factory.yaml b/fast/stages/1-resman/data/stage-2/project-factory.yaml
index 889321b36..cc3fd9e89 100644
--- a/fast/stages/1-resman/data/stage-2/project-factory.yaml
+++ b/fast/stages/1-resman/data/stage-2/project-factory.yaml
@@ -42,6 +42,6 @@ organization_config:
# name: my-org/my-repository
# type: github
# workflows_config:
-# tf_var_files:
+# extra_files:
# - 2-networking.auto.tfvars.json
# - 2-security.auto.tfvars.json
\ No newline at end of file
diff --git a/fast/stages/1-resman/outputs-cicd.tf b/fast/stages/1-resman/outputs-cicd.tf
index 7a8609b0d..e43d78e87 100644
--- a/fast/stages/1-resman/outputs-cicd.tf
+++ b/fast/stages/1-resman/outputs-cicd.tf
@@ -54,7 +54,7 @@ locals {
"2-security.auto.tfvars.json"
]
),
- v.workflows_config.tf_var_files
+ v.workflows_config.extra_files
)
}
)
diff --git a/fast/stages/1-resman/schemas/fast-stage2.schema.json b/fast/stages/1-resman/schemas/fast-stage2.schema.json
index afe9689c4..d9daf9545 100644
--- a/fast/stages/1-resman/schemas/fast-stage2.schema.json
+++ b/fast/stages/1-resman/schemas/fast-stage2.schema.json
@@ -45,7 +45,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
- "tf_var_files": {
+ "extra_files": {
"type": "array",
"items": {
"type": "string"
diff --git a/fast/stages/1-resman/schemas/fast-stage2.schema.md b/fast/stages/1-resman/schemas/fast-stage2.schema.md
index a555f7d7f..82d85d2cd 100644
--- a/fast/stages/1-resman/schemas/fast-stage2.schema.md
+++ b/fast/stages/1-resman/schemas/fast-stage2.schema.md
@@ -18,7 +18,7 @@
*default: github*, *enum: ['github', 'gitlab']*
- **workflows_config**: *object*
*additional properties: false*
- - **tf_var_files**: *array*
+ - **extra_files**: *array*
- items: *string*
- **folder_config**: *object*
*additional properties: false*
diff --git a/fast/stages/1-resman/schemas/fast-stage3.schema.json b/fast/stages/1-resman/schemas/fast-stage3.schema.json
index f446c9770..5b2bf0808 100644
--- a/fast/stages/1-resman/schemas/fast-stage3.schema.json
+++ b/fast/stages/1-resman/schemas/fast-stage3.schema.json
@@ -56,7 +56,7 @@
"type": "object",
"additionalProperties": false,
"properties": {
- "tf_var_files": {
+ "extra_files": {
"type": "array",
"items": {
"type": "string"
diff --git a/fast/stages/1-resman/schemas/fast-stage3.schema.md b/fast/stages/1-resman/schemas/fast-stage3.schema.md
index 2ee6c5c79..295fd3d25 100644
--- a/fast/stages/1-resman/schemas/fast-stage3.schema.md
+++ b/fast/stages/1-resman/schemas/fast-stage3.schema.md
@@ -20,7 +20,7 @@
*default: github*, *enum: ['github', 'gitlab']*
- **workflows_config**: *object*
*additional properties: false*
- - **tf_var_files**: *array*
+ - **extra_files**: *array*
- items: *string*
- **folder_config**: *object*
*additional properties: false*
diff --git a/fast/stages/1-resman/stage-2.tf b/fast/stages/1-resman/stage-2.tf
index bda5f96c6..ed1e395d7 100644
--- a/fast/stages/1-resman/stage-2.tf
+++ b/fast/stages/1-resman/stage-2.tf
@@ -44,7 +44,7 @@ locals {
type = try(v.cicd_config.repository.type, "github")
})
workflows_config = {
- tf_var_files = try(v.cicd_config.workflows_config.tf_var_files, [])
+ extra_files = try(v.cicd_config.workflows_config.extra_files, [])
}
}
folder_config = lookup(v, "folder_config", null) == null ? null : {
diff --git a/fast/stages/1-resman/stage-3.tf b/fast/stages/1-resman/stage-3.tf
index 58b6d1117..3a5af762d 100644
--- a/fast/stages/1-resman/stage-3.tf
+++ b/fast/stages/1-resman/stage-3.tf
@@ -42,6 +42,9 @@ locals {
branch = try(v.cicd_config.repository.branch, null)
type = try(v.cicd_config.repository.type, "github")
})
+ workflows_config = {
+ extra_files = try(v.cicd_config.workflows_config.extra_files, [])
+ }
}
folder_config = lookup(v, "folder_config", null) == null ? null : {
name = v.folder_config.name
diff --git a/fast/stages/1-resman/variables-stages.tf b/fast/stages/1-resman/variables-stages.tf
index 8cde5b565..74e04ae27 100644
--- a/fast/stages/1-resman/variables-stages.tf
+++ b/fast/stages/1-resman/variables-stages.tf
@@ -26,7 +26,7 @@ variable "fast_stage_2" {
type = optional(string, "github")
})
workflows_config = optional(object({
- tf_var_files = optional(list(string), [])
+ extra_files = optional(list(string), [])
}), {})
}))
folder_config = optional(object({
@@ -128,7 +128,7 @@ variable "fast_stage_3" {
type = optional(string, "github")
})
workflows_config = optional(object({
- tf_var_files = optional(list(string), [])
+ extra_files = optional(list(string), [])
}), {})
}))
folder_config = optional(object({
diff --git a/fast/stages/2-networking-a-simple/schemas/subnet.schema.md b/fast/stages/2-networking-a-simple/schemas/subnet.schema.md
index 66286ef8e..1d735e38e 100644
--- a/fast/stages/2-networking-a-simple/schemas/subnet.schema.md
+++ b/fast/stages/2-networking-a-simple/schemas/subnet.schema.md
@@ -23,6 +23,8 @@
- **ipv6**: *object*
*additional properties: false*
- **access_type**: *string*
+ - **ipv6_only**: *boolean*
+- **ip_collection**: *string*
- **name**: *string*
- ⁺**region**: *string*
- **psc**: *boolean*
diff --git a/fast/stages/2-networking-b-nva/schemas/subnet.schema.md b/fast/stages/2-networking-b-nva/schemas/subnet.schema.md
index 66286ef8e..1d735e38e 100644
--- a/fast/stages/2-networking-b-nva/schemas/subnet.schema.md
+++ b/fast/stages/2-networking-b-nva/schemas/subnet.schema.md
@@ -23,6 +23,8 @@
- **ipv6**: *object*
*additional properties: false*
- **access_type**: *string*
+ - **ipv6_only**: *boolean*
+- **ip_collection**: *string*
- **name**: *string*
- ⁺**region**: *string*
- **psc**: *boolean*
diff --git a/fast/stages/2-networking-c-separate-envs/schemas/subnet.schema.md b/fast/stages/2-networking-c-separate-envs/schemas/subnet.schema.md
index 66286ef8e..1d735e38e 100644
--- a/fast/stages/2-networking-c-separate-envs/schemas/subnet.schema.md
+++ b/fast/stages/2-networking-c-separate-envs/schemas/subnet.schema.md
@@ -23,6 +23,8 @@
- **ipv6**: *object*
*additional properties: false*
- **access_type**: *string*
+ - **ipv6_only**: *boolean*
+- **ip_collection**: *string*
- **name**: *string*
- ⁺**region**: *string*
- **psc**: *boolean*
diff --git a/fast/stages/2-project-factory/schemas/project.schema.md b/fast/stages/2-project-factory/schemas/project.schema.md
index cdd081ef4..0be198251 100644
--- a/fast/stages/2-project-factory/schemas/project.schema.md
+++ b/fast/stages/2-project-factory/schemas/project.schema.md
@@ -136,6 +136,7 @@
- **iam**: *reference([iam](#refs-iam))*
- **iam_bindings**: *reference([iam_bindings](#refs-iam_bindings))*
- **iam_bindings_additive**: *reference([iam_bindings_additive](#refs-iam_bindings_additive))*
+ - **force_destroy**: *boolean*
- **labels**: *object*
*additional properties: String*
- **location**: *string*
diff --git a/modules/cloud-deploy/versions.tf b/modules/cloud-deploy/versions.tf
index 0ebb55c97..45fd47935 100644
--- a/modules/cloud-deploy/versions.tf
+++ b/modules/cloud-deploy/versions.tf
@@ -19,11 +19,11 @@ terraform {
required_providers {
google = {
source = "hashicorp/google"
- version = ">= 6.33.0, < 7.0.0" # tftest
+ version = ">= 6.40.0, < 7.0.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
- version = ">= 6.33.0, < 7.0.0" # tftest
+ version = ">= 6.40.0, < 7.0.0" # tftest
}
}
provider_meta "google" {
diff --git a/modules/cloud-deploy/versions.tofu b/modules/cloud-deploy/versions.tofu
index 5d4839e8d..26a249225 100644
--- a/modules/cloud-deploy/versions.tofu
+++ b/modules/cloud-deploy/versions.tofu
@@ -19,11 +19,11 @@ terraform {
required_providers {
google = {
source = "hashicorp/google"
- version = ">= 6.33.0, < 7.0.0" # tftest
+ version = ">= 6.40.0, < 7.0.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
- version = ">= 6.33.0, < 7.0.0" # tftest
+ version = ">= 6.40.0, < 7.0.0" # tftest
}
}
provider_meta "google" {
diff --git a/tests/fast/stages/s1_resman/simple.tfvars b/tests/fast/stages/s1_resman/simple.tfvars
index b318ff675..c18133120 100644
--- a/tests/fast/stages/s1_resman/simple.tfvars
+++ b/tests/fast/stages/s1_resman/simple.tfvars
@@ -15,6 +15,11 @@ fast_stage_2 = {
name = "cloud-foundation-fabric/1-resman"
branch = "main"
}
+ workflows_config = {
+ extra_files = [
+ "99-user.auto.tfvars.json"
+ ]
+ }
}
organization_config = {
iam_bindings_additive = {
diff --git a/tests/fast/stages/s1_resman/simple.yaml b/tests/fast/stages/s1_resman/simple.yaml
index 1bf3b876d..5e0f4b4b1 100644
--- a/tests/fast/stages/s1_resman/simple.yaml
+++ b/tests/fast/stages/s1_resman/simple.yaml
@@ -31,6 +31,109 @@ counts:
modules: 45
resources: 271
+values:
+ google_storage_bucket_object.workflows["2-project-factory"]:
+ bucket: fast2-prod-iac-core-outputs
+ content: "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License,\
+ \ Version 2.0 (the \"License\");\n# you may not use this file except in compliance\
+ \ with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n\
+ #\n# Unless required by applicable law or agreed to in writing, software\n#\
+ \ distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT\
+ \ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the\
+ \ License for the specific language governing permissions and\n# limitations\
+ \ under the License.\n\nname: \"FAST project-factory stage\"\n\non:\n pull_request:\n\
+ \ branches:\n - main\n types:\n - closed\n - opened\n \
+ \ - synchronize\n\nenv:\n FAST_SERVICE_ACCOUNT: fast2-prod-resman-pf-1@fast2-prod-automation.iam.gserviceaccount.com\n\
+ \ FAST_SERVICE_ACCOUNT_PLAN: fast2-prod-resman-pf-1r@fast2-prod-automation.iam.gserviceaccount.com\n\
+ \ FAST_WIF_PROVIDER: projects/1234567890/locations/global/workloadIdentityPools/ldj-bootstrap/providers/ldj-bootstrap-github-ludomagno\n\
+ \ SSH_AUTH_SOCK: /tmp/ssh_agent.sock\n TF_PROVIDERS_FILE: 2-project-factory-providers.tf\n\
+ \ TF_PROVIDERS_FILE_PLAN: 2-project-factory-r-providers.tf\n TF_VERSION: 1.11.4\n\
+ \njobs:\n fast-pr:\n # Skip PRs which are closed without being merged.\n\
+ \ if: >-\n github.event.action == 'closed' && \n github.event.pull_request.merged\
+ \ == true ||\n github.event.action == 'opened' ||\n github.event.action\
+ \ == 'synchronize'\n permissions:\n contents: read\n id-token:\
+ \ write\n issues: write\n pull-requests: write\n runs-on: ubuntu-latest\n\
+ \ steps:\n - id: checkout\n name: Checkout repository\n \
+ \ uses: actions/checkout@v4\n\n # set up SSH key authentication to the\
+ \ modules repository\n\n - id: ssh-config\n name: Configure SSH\
+ \ authentication\n run: |\n ssh-agent -a \"$SSH_AUTH_SOCK\"\
+ \ > /dev/null\n ssh-add - <<< \"${{ secrets.CICD_MODULES_KEY }}\"\n\
+ \n # set up step variables for plan / apply\n\n - id: vars-plan\n\
+ \ if: github.event.pull_request.merged != true && success()\n \
+ \ name: Set up plan variables\n run: |\n echo \"plan_opts=-lock=false\"\
+ \ >> \"$GITHUB_ENV\"\n echo \"provider_file=${{env.TF_PROVIDERS_FILE_PLAN}}\"\
+ \ >> \"$GITHUB_ENV\"\n echo \"service_account=${{env.FAST_SERVICE_ACCOUNT_PLAN}}\"\
+ \ >> \"$GITHUB_ENV\"\n\n - id: vars-apply\n if: github.event.pull_request.merged\
+ \ == true && success()\n name: Set up apply variables\n run: |\n\
+ \ echo \"provider_file=${{env.TF_PROVIDERS_FILE}}\" >> \"$GITHUB_ENV\"\
+ \n echo \"service_account=${{env.FAST_SERVICE_ACCOUNT}}\" >> \"$GITHUB_ENV\"\
+ \n\n # set up authentication via Workload identity Federation and gcloud\n\
+ \n - id: gcp-auth\n name: Authenticate to Google Cloud\n \
+ \ uses: google-github-actions/auth@v2\n with:\n workload_identity_provider:\
+ \ ${{env.FAST_WIF_PROVIDER}}\n service_account: ${{env.service_account}}\n\
+ \ access_token_lifetime: 900s\n\n - id: gcp-sdk\n name:\
+ \ Set up Cloud SDK\n uses: google-github-actions/setup-gcloud@v2\n \
+ \ with:\n install_components: alpha\n\n # copy provider file\n\
+ \n - id: tf-config-provider\n name: Copy Terraform provider file\n\
+ \ run: |\n gcloud storage cp -r \\\n \"gs://fast2-prod-iac-core-outputs/providers/${{env.provider_file}}\"\
+ \ ./\n gcloud storage cp -r \\\n \"gs://fast2-prod-iac-core-outputs/tfvars/0-bootstrap.auto.tfvars.json\"\
+ \ ./\n gcloud storage cp -r \\\n \"gs://fast2-prod-iac-core-outputs/tfvars/1-resman.auto.tfvars.json\"\
+ \ ./\n gcloud storage cp -r \\\n \"gs://fast2-prod-iac-core-outputs/tfvars/0-globals.auto.tfvars.json\"\
+ \ ./\n gcloud storage cp -r \\\n \"gs://fast2-prod-iac-core-outputs/tfvars/99-user.auto.tfvars.json\"\
+ \ ./\n\n - id: tf-setup\n name: Set up Terraform\n uses:\
+ \ hashicorp/setup-terraform@v3\n with:\n terraform_version:\
+ \ ${{env.TF_VERSION}}\n\n # run Terraform init/validate/plan\n\n -\
+ \ id: tf-init\n name: Terraform init\n continue-on-error: true\n\
+ \ run: |\n terraform init -no-color\n\n - id: tf-validate\n\
+ \ continue-on-error: true\n name: Terraform validate\n \
+ \ run: terraform validate -no-color\n\n - id: tf-plan\n name: Terraform\
+ \ plan\n continue-on-error: true\n run: |\n terraform\
+ \ plan -input=false -out ../plan.out -no-color ${{env.plan_opts}}\n\n -\
+ \ id: tf-apply\n if: github.event.pull_request.merged == true && success()\n\
+ \ name: Terraform apply\n continue-on-error: true\n run:\
+ \ |\n terraform apply -input=false -auto-approve -no-color ../plan.out\n\
+ \n # PR comment with Terraform result from previous steps\n # length\
+ \ is checked and trimmed for length so as to stay within the limit\n\n \
+ \ - id: pr-comment\n name: Post comment to Pull Request\n continue-on-error:\
+ \ true\n uses: actions/github-script@v7\n if: github.event_name\
+ \ == 'pull_request'\n env:\n PLAN: ${{steps.tf-plan.outputs.stdout}}\\\
+ n${{steps.tf-plan.outputs.stderr}}\n with:\n script: |\n \
+ \ const output = `### Terraform Initialization \\`${{steps.tf-init.outcome}}\\\
+ `\n\n ### Terraform Validation \\`${{steps.tf-validate.outcome}}\\\
+ `\n\n Validation Output
\n\n \
+ \ \\`\\`\\`\\n\n ${{steps.tf-validate.outputs.stdout}}\n \
+ \ \\`\\`\\`\n\n \n\n ### Terraform Plan\
+ \ \\`${{steps.tf-plan.outcome}}\\`\n\n Show Plan
\n\
+ \n \\`\\`\\`\\n\n ${process.env.PLAN.split('\\n').filter(l\
+ \ => l.match(/^([A-Z\\s].*|)$$/)).join('\\n')}\n \\`\\`\\`\n\n \
+ \ \n\n ### Terraform Apply \\`${{steps.tf-apply.outcome}}\\\
+ `\n\n *Pusher: @${{github.actor}}, Action: \\`${{github.event_name}}\\\
+ `, Working Directory: \\`${{env.tf_actions_working_dir}}\\`, Workflow: \\`${{github.workflow}}\\\
+ `*`;\n\n github.rest.issues.createComment({\n issue_number:\
+ \ context.issue.number,\n owner: context.repo.owner,\n \
+ \ repo: context.repo.repo,\n body: output\n })\n\
+ \n - id: pr-short-comment\n name: Post comment to Pull Request (abbreviated)\n\
+ \ uses: actions/github-script@v7\n if: github.event_name == 'pull_request'\
+ \ && steps.pr-comment.outcome != 'success'\n with:\n script:\
+ \ |\n const output = `### Terraform Initialization \\`${{steps.tf-init.outcome}}\\\
+ `\n\n ### Terraform Validation \\`${{steps.tf-validate.outcome}}\\\
+ `\n\n ### Terraform Plan \\`${{steps.tf-plan.outcome}}\\`\n\n \
+ \ Plan output is in the action log.\n\n ### Terraform Apply\
+ \ \\`${{steps.tf-apply.outcome}}\\`\n\n *Pusher: @${{github.actor}},\
+ \ Action: \\`${{github.event_name}}\\`, Working Directory: \\`${{env.tf_actions_working_dir}}\\\
+ `, Workflow: \\`${{github.workflow}}\\`*`;\n\n github.rest.issues.createComment({\n\
+ \ issue_number: context.issue.number,\n owner: context.repo.owner,\n\
+ \ repo: context.repo.repo,\n body: output\n \
+ \ })\n\n # exit on error from previous steps\n\n - id: check-init\n\
+ \ name: Check init failure\n if: steps.tf-init.outcome != 'success'\n\
+ \ run: exit 1\n\n - id: check-validate\n name: Check validate\
+ \ failure\n if: steps.tf-validate.outcome != 'success'\n run:\
+ \ exit 1\n\n - id: check-plan\n name: Check plan failure\n \
+ \ if: steps.tf-plan.outcome != 'success'\n run: exit 1\n\n - id:\
+ \ check-apply\n name: Check apply failure\n if: github.event.pull_request.merged\
+ \ == true && steps.tf-apply.outcome != 'success'\n run: exit 1\n"
+ name: workflows/2-project-factory-workflow.yaml
+
outputs:
cicd_repositories:
project-factory:
diff --git a/tests/fixtures.py b/tests/fixtures.py
index 7e9eace58..c82649a1b 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -193,6 +193,7 @@ def plan_validator(module_path, inventory_paths, basedir, tf_var_files=None,
# - include a descriptive error message to the assert
# print(yaml.dump({'values': summary.values}))
# print("", yaml.dump({'counts': summary.counts}))
+ # print(yaml.dump({'outputs': summary.values}))
if 'values' in inventory:
try: