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: