Skip to content

[WIP] static site support#55

Draft
jnf wants to merge 5 commits intomainfrom
jnf/static-site-support
Draft

[WIP] static site support#55
jnf wants to merge 5 commits intomainfrom
jnf/static-site-support

Conversation

@jnf
Copy link
Copy Markdown
Contributor

@jnf jnf commented Apr 27, 2026

No description provided.

jnf and others added 3 commits April 27, 2026 14:58
Exposes the top-level `type` field from an app spec (defaults to
"container" when absent). Static app specs declare `type: static`; the
output lets config layers inspect the type without re-parsing the raw spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fork of the docs module for hosting arbitrary static files behind Okta
SSO. Keeps the same infrastructure (S3 + CloudFront + Lambda@Edge OIDC)
but removes MkDocs-specific assumptions:

- No docs.enabled filter — all apps passed to the module are hosted
- All prefixes are protected (OIDC required for every app)
- project renamed to cfa-static-apps
- subdomain default changed from "docs" to "apps"
- Fixes the cfa-docummentation typo in the KMS alias from the original

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tofu/configs/static/ instantiates the static-app module following the
same pattern as configs/docs/: a locals.tf discovers app specs from
apps/*.yaml, appspec modules parse them, and the static module wires
everything together.

.github/workflows/deploy-static.yml is a reusable workflow_call target.
Apps call it by passing AWS_STATIC_ROLE_ARN (secret) and
STATIC_BUCKET + STATIC_PREFIX (vars). CLOUDFRONT_DISTRIBUTION_ID is
optional — if set, the workflow invalidates the prefix after sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TFLint flagged four issues in configs/static — all from three files
that were present in configs/docs but not carried over:

- versions.tf: required_version constraint (terraform_required_version)
- providers.tf: AWS provider with default_tags, which also consumes the
  program and project variables (terraform_unused_declarations)
- outputs.tf: exposes deploy_roles, endpoint_url, prefixes from the
  static module (terraform_standard_module_structure)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Using aws_lambda_function.oidc.logging_config[0].log_group as a
for_each key fails at plan time because it's an apply-time attribute.
Lambda always creates the log group at /aws/lambda/<function_name>, so
we can construct the path from local.prefix instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jnf jnf temporarily deployed to development April 27, 2026 22:43 — with GitHub Actions Inactive
@github-actions
Copy link
Copy Markdown

Plan output for static config


OpenTofu used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
 <= read (data resources)

OpenTofu will perform the following actions:

  # module.static.data.archive_file.oidc will be read during apply
  # (depends on a resource or a module with changes pending)
 <= data "archive_file" "oidc" {
      + id                  = (known after apply)
      + output_base64sha256 = (known after apply)
      + output_base64sha512 = (known after apply)
      + output_md5          = (known after apply)
      + output_path         = "../../modules/static-app/dist/oidc-function.zip"
      + output_sha          = (known after apply)
      + output_sha256       = (known after apply)
      + output_sha512       = (known after apply)
      + output_size         = (known after apply)
      + source_dir          = "../../modules/static-app/dist/oidc"
      + type                = "zip"
    }

  # module.static.aws_acm_certificate.endpoint will be created
  + resource "aws_acm_certificate" "endpoint" {
      + arn                       = (known after apply)
      + domain_name               = "apps.dev.services.cfa.codes"
      + domain_validation_options = [
          + {
              + domain_name           = "apps.dev.services.cfa.codes"
              + resource_record_name  = (known after apply)
              + resource_record_type  = (known after apply)
              + resource_record_value = (known after apply)
            },
        ]
      + id                        = (known after apply)
      + key_algorithm             = (known after apply)
      + not_after                 = (known after apply)
      + not_before                = (known after apply)
      + pending_renewal           = (known after apply)
      + region                    = "us-east-1"
      + renewal_eligibility       = (known after apply)
      + renewal_summary           = (known after apply)
      + status                    = (known after apply)
      + subject_alternative_names = [
          + "apps.dev.services.cfa.codes",
        ]
      + tags                      = (known after apply)
      + tags_all                  = (known after apply)
      + type                      = (known after apply)
      + validation_emails         = (known after apply)
      + validation_method         = "DNS"

      + options (known after apply)
    }

  # module.static.aws_acm_certificate_validation.endpoint will be created
  + resource "aws_acm_certificate_validation" "endpoint" {
      + certificate_arn         = (known after apply)
      + id                      = (known after apply)
      + region                  = "us-east-1"
      + validation_record_fqdns = (known after apply)
    }

  # module.static.aws_cloudfront_distribution.endpoint will be created
  + resource "aws_cloudfront_distribution" "endpoint" {
      + aliases                         = [
          + "apps.dev.services.cfa.codes",
        ]
      + arn                             = (known after apply)
      + caller_reference                = (known after apply)
      + comment                         = "Serve static apps from S3."
      + continuous_deployment_policy_id = (known after apply)
      + default_root_object             = "index.html"
      + domain_name                     = (known after apply)
      + enabled                         = true
      + etag                            = (known after apply)
      + hosted_zone_id                  = (known after apply)
      + http_version                    = "http2"
      + id                              = (known after apply)
      + in_progress_validation_batches  = (known after apply)
      + is_ipv6_enabled                 = true
      + last_modified_time              = (known after apply)
      + logging_v1_enabled              = (known after apply)
      + price_class                     = "PriceClass_100"
      + retain_on_delete                = false
      + staging                         = false
      + status                          = (known after apply)
      + tags                            = (known after apply)
      + tags_all                        = (known after apply)
      + trusted_key_groups              = (known after apply)
      + trusted_signers                 = (known after apply)
      + wait_for_deployment             = true

      + default_cache_behavior {
          + allowed_methods        = [
              + "GET",
              + "HEAD",
            ]
          + cache_policy_id        = "658327ea-f89d-4fab-a63d-7e88639e58f6"
          + cached_methods         = [
              + "GET",
              + "HEAD",
            ]
          + compress               = true
          + default_ttl            = 0
          + max_ttl                = 0
          + min_ttl                = 0
          + target_origin_id       = "cfa-static-apps-development"
          + trusted_key_groups     = (known after apply)
          + trusted_signers        = (known after apply)
          + viewer_protocol_policy = "redirect-to-https"

          + grpc_config (known after apply)

          + lambda_function_association {
              + event_type   = "viewer-request"
              + include_body = false
              + lambda_arn   = (known after apply)
            }
        }

      + logging_config {
          + bucket          = "shared-services-development-logs.s3.amazonaws.com"
          + include_cookies = false
          + prefix          = "cloudfront/apps.dev.services.cfa.codes"
        }

      + origin {
          + connection_attempts         = 3
          + connection_timeout          = 10
          + domain_name                 = (known after apply)
          + origin_access_control_id    = (known after apply)
          + origin_id                   = "cfa-static-apps-development"
          + response_completion_timeout = (known after apply)
        }

      + restrictions {
          + geo_restriction {
              + locations        = (known after apply)
              + restriction_type = "none"
            }
        }

      + viewer_certificate {
          + acm_certificate_arn      = (known after apply)
          + minimum_protocol_version = "TLSv1.2_2021"
          + ssl_support_method       = "sni-only"
        }
    }

  # module.static.aws_cloudfront_function.endpoint_rewrite will be created
  + resource "aws_cloudfront_function" "endpoint_rewrite" {
      + arn             = (known after apply)
      + code            = <<-EOT
            function handler(event) {
              const request = event.request;
              const uri = request.uri;
            
              // If the request is being made to a directory (e.g. / or /docs), we want to
              // append "index.html" so that S3 serves the proper object. If the path
              // doesn't contain a file extension, we assume it's a directory as well.
              if (uri.endsWith('/')) {
                request.uri += 'index.html';
              }
              else if (!uri.includes('.')) {
                request.uri += '/index.html'
              }
            
              return request;
            }
        EOT
      + comment         = "Rewrite requests to direct to the index.html file in the S3 bucket."
      + etag            = (known after apply)
      + id              = (known after apply)
      + live_stage_etag = (known after apply)
      + name            = "cfa-static-apps-development-rewrite"
      + publish         = true
      + runtime         = "cloudfront-js-2.0"
      + status          = (known after apply)
    }

  # module.static.aws_cloudfront_origin_access_control.endpoint will be created
  + resource "aws_cloudfront_origin_access_control" "endpoint" {
      + arn                               = (known after apply)
      + description                       = "Authorize CloudFront to serve content from the static apps S3 bucket."
      + etag                              = (known after apply)
      + id                                = (known after apply)
      + name                              = "cfa-static-apps-development-endpoint"
      + origin_access_control_origin_type = "s3"
      + signing_behavior                  = "always"
      + signing_protocol                  = "sigv4"
    }

  # module.static.aws_cloudwatch_log_subscription_filter.datadog["/aws/lambda/cfa-static-apps-development-oidc"] will be created
  + resource "aws_cloudwatch_log_subscription_filter" "datadog" {
      + apply_on_transformed_logs = (known after apply)
      + destination_arn           = "arn:aws:lambda:us-east-1:816069131564:function:DatadogIntegration-ForwarderStack-W7KLT9-Forwarder-VufEAiofNX9f"
      + distribution              = "ByLogStream"
      + id                        = (known after apply)
      + log_group_name            = "/aws/lambda/cfa-static-apps-development-oidc"
      + name                      = "datadog"
      + region                    = "us-east-1"
      + role_arn                  = (known after apply)
    }

  # module.static.aws_iam_openid_connect_provider.github will be created
  + resource "aws_iam_openid_connect_provider" "github" {
      + arn             = (known after apply)
      + client_id_list  = [
          + "sts.amazonaws.com",
        ]
      + id              = (known after apply)
      + tags            = (known after apply)
      + tags_all        = (known after apply)
      + thumbprint_list = (known after apply)
      + url             = "https://token.actions.githubusercontent.com"
    }

  # module.static.aws_iam_policy.oidc_function will be created
  + resource "aws_iam_policy" "oidc_function" {
      + arn              = (known after apply)
      + attachment_count = (known after apply)
      + description      = "Permissions for the OIDC authentication Lambda function."
      + id               = (known after apply)
      + name             = "cfa-static-apps-development-oidc-function"
      + name_prefix      = (known after apply)
      + path             = "/"
      + policy           = (known after apply)
      + policy_id        = (known after apply)
      + tags             = (known after apply)
      + tags_all         = (known after apply)
    }

  # module.static.aws_iam_role.oidc_function will be created
  + resource "aws_iam_role" "oidc_function" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Effect    = "Allow"
                      + Principal = {
                          + Service = [
                              + "lambda.amazonaws.com",
                              + "edgelambda.amazonaws.com",
                            ]
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "cfa-static-apps-development-oidc-function"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags                  = (known after apply)
      + tags_all              = (known after apply)
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # module.static.aws_iam_role_policy_attachment.oidc_function will be created
  + resource "aws_iam_role_policy_attachment" "oidc_function" {
      + id         = (known after apply)
      + policy_arn = (known after apply)
      + role       = "cfa-static-apps-development-oidc-function"
    }

  # module.static.aws_kms_alias.static will be created
  + resource "aws_kms_alias" "static" {
      + arn            = (known after apply)
      + id             = (known after apply)
      + name           = "alias/cfa-static-apps/development"
      + name_prefix    = (known after apply)
      + region         = "us-east-1"
      + target_key_arn = (known after apply)
      + target_key_id  = (known after apply)
    }

  # module.static.aws_kms_key.static will be created
  + resource "aws_kms_key" "static" {
      + arn                                = (known after apply)
      + bypass_policy_lockout_safety_check = false
      + customer_master_key_spec           = "SYMMETRIC_DEFAULT"
      + description                        = "Encryption key for static app hosting"
      + enable_key_rotation                = true
      + id                                 = (known after apply)
      + is_enabled                         = true
      + key_id                             = (known after apply)
      + key_usage                          = "ENCRYPT_DECRYPT"
      + multi_region                       = (known after apply)
      + policy                             = (known after apply)
      + region                             = "us-east-1"
      + rotation_period_in_days            = (known after apply)
      + tags                               = (known after apply)
      + tags_all                           = (known after apply)
    }

  # module.static.aws_lambda_function.oidc will be created
  + resource "aws_lambda_function" "oidc" {
      + architectures                  = (known after apply)
      + arn                            = (known after apply)
      + code_sha256                    = (known after apply)
      + filename                       = "../../modules/static-app/dist/oidc-function.zip"
      + function_name                  = "cfa-static-apps-development-oidc"
      + handler                        = "index.handler"
      + id                             = (known after apply)
      + invoke_arn                     = (known after apply)
      + last_modified                  = (known after apply)
      + memory_size                    = 128
      + package_type                   = "Zip"
      + publish                        = true
      + qualified_arn                  = (known after apply)
      + qualified_invoke_arn           = (known after apply)
      + region                         = "us-east-1"
      + reserved_concurrent_executions = -1
      + response_streaming_invoke_arn  = (known after apply)
      + role                           = (known after apply)
      + runtime                        = "nodejs22.x"
      + signing_job_arn                = (known after apply)
      + signing_profile_version_arn    = (known after apply)
      + skip_destroy                   = false
      + source_code_hash               = (known after apply)
      + source_code_size               = (known after apply)
      + tags                           = (known after apply)
      + tags_all                       = (known after apply)
      + timeout                        = 3
      + version                        = (known after apply)

      + ephemeral_storage (known after apply)

      + logging_config (known after apply)

      + tracing_config (known after apply)
    }

  # module.static.aws_route53_record.endpoint["A"] will be created
  + resource "aws_route53_record" "endpoint" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "apps.dev.services.cfa.codes"
      + type            = "A"
      + zone_id         = "Z088564232NGJRO0LYHKL"

      + alias {
          + evaluate_target_health = false
          + name                   = (known after apply)
          + zone_id                = (known after apply)
        }
    }

  # module.static.aws_route53_record.endpoint["AAAA"] will be created
  + resource "aws_route53_record" "endpoint" {
      + allow_overwrite = (known after apply)
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = "apps.dev.services.cfa.codes"
      + type            = "AAAA"
      + zone_id         = "Z088564232NGJRO0LYHKL"

      + alias {
          + evaluate_target_health = false
          + name                   = (known after apply)
          + zone_id                = (known after apply)
        }
    }

  # module.static.aws_route53_record.endpoint_validation["apps.dev.services.cfa.codes"] will be created
  + resource "aws_route53_record" "endpoint_validation" {
      + allow_overwrite = true
      + fqdn            = (known after apply)
      + id              = (known after apply)
      + name            = (known after apply)
      + records         = (known after apply)
      + ttl             = 60
      + type            = (known after apply)
      + zone_id         = "Z088564232NGJRO0LYHKL"
    }

  # module.static.aws_s3_object.index will be created
  + resource "aws_s3_object" "index" {
      + acl                    = (known after apply)
      + arn                    = (known after apply)
      + bucket                 = "apps.dev.services.cfa.codes"
      + bucket_key_enabled     = (known after apply)
      + checksum_crc32         = (known after apply)
      + checksum_crc32c        = (known after apply)
      + checksum_crc64nvme     = (known after apply)
      + checksum_sha1          = (known after apply)
      + checksum_sha256        = (known after apply)
      + content_base64         = "PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8aGVhZD4KICA8c3R5bGU+CiAgICBsaSB7IHBhZGRpbmctbGVmdDogMC41ZW07IH0KCiAgICBsaS5wdWJsaWM6Om1hcmtlciB7CiAgICAgIGNvbnRlbnQ6IHVybCgiZGF0YTppbWFnZS9zdmcreG1sO2NoYXJzZXQ9VVRGLTgsPHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIGhlaWdodD0nMjQnIHdpZHRoPScyNCc+PHBhdGggc3Ryb2tlLWxpbmVjYXA9J3JvdW5kJyBzdHJva2UtbGluZWpvaW49J3JvdW5kJyBkPSdNMTMuNSAxMC41VjYuNzVhNC41IDQuNSAwIDEgMSA5IDB2My43NU0zLjc1IDIxLjc1aDEwLjVhMi4yNSAyLjI1IDAgMCAwIDIuMjUtMi4yNXYtNi43NWEyLjI1IDIuMjUgMCAwIDAtMi4yNS0yLjI1SDMuNzVhMi4yNSAyLjI1IDAgMCAwLTIuMjUgMi4yNXY2Ljc1YTIuMjUgMi4yNSAwIDAgMCAyLjI1IDIuMjVaJyBmaWxsPSdub25lJyBzdHJva2U9J2dyZWVuJyBzdHJva2Utd2lkdGg9JzMnIC8+PC9zdmc+IikKICAgIH0KCiAgICBsaS5wcml2YXRlOjptYXJrZXIgewogICAgICBjb250ZW50OiB1cmwoImRhdGE6aW1hZ2Uvc3ZnK3htbDtjaGFyc2V0PVVURi04LDxzdmcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyBoZWlnaHQ9JzI0JyB3aWR0aD0nMjQnPjxwYXRoIHN0cm9rZS1saW5lY2FwPSdyb3VuZCcgc3Ryb2tlLWxpbmVqb2luPSdyb3VuZCcgZD0nTTE2LjUgMTAuNVY2Ljc1YTQuNSA0LjUgMCAxIDAtOSAwdjMuNzVtLS43NSAxMS4yNWgxMC41YTIuMjUgMi4yNSAwIDAgMCAyLjI1LTIuMjV2LTYuNzVhMi4yNSAyLjI1IDAgMCAwLTIuMjUtMi4yNUg2Ljc1YTIuMjUgMi4yNSAwIDAgMC0yLjI1IDIuMjV2Ni43NWEyLjI1IDIuMjUgMCAwIDAgMi4yNSAyLjI1WicgZmlsbD0nbm9uZScgc3Ryb2tlPSdyZWQnIHN0cm9rZS13aWR0aD0nMycgLz48L3N2Zz4iKQogICAgfQogIDwvc3R5bGU+CiAgPHRpdGxlPkNvZGUgZm9yIEFtZXJpY2EgRW5naW5lZXJpbmcgRG9jdW1lbnRhdGlvbiAoRGV2ZWxvcG1lbnQpPC90aXRsZT4KPC9oZWFkPgo8Ym9keT4KICA8aDE+Q29kZSBmb3IgQW1lcmljYSBFbmdpbmVlcmluZyBEb2N1bWVudGF0aW9uIChEZXZlbG9wbWVudCk8L2gxPgoKICA8dWw+CiAgICAgIDwvdWw+CjwvYm9keT4KPC9odG1sPgo="
      + content_type           = "text/html"
      + etag                   = (known after apply)
      + force_destroy          = true
      + id                     = (known after apply)
      + key                    = "index.html"
      + kms_key_id             = (known after apply)
      + region                 = "us-east-1"
      + server_side_encryption = (known after apply)
      + storage_class          = (known after apply)
      + tags                   = (known after apply)
      + tags_all               = (known after apply)
      + version_id             = (known after apply)
    }

  # module.static.aws_s3_object.robots will be created
  + resource "aws_s3_object" "robots" {
      + acl                    = (known after apply)
      + arn                    = (known after apply)
      + bucket                 = "apps.dev.services.cfa.codes"
      + bucket_key_enabled     = (known after apply)
      + checksum_crc32         = (known after apply)
      + checksum_crc32c        = (known after apply)
      + checksum_crc64nvme     = (known after apply)
      + checksum_sha1          = (known after apply)
      + checksum_sha256        = (known after apply)
      + content_type           = (known after apply)
      + etag                   = (known after apply)
      + force_destroy          = true
      + id                     = (known after apply)
      + key                    = "robots.txt"
      + kms_key_id             = (known after apply)
      + region                 = "us-east-1"
      + server_side_encryption = (known after apply)
      + source                 = "../../modules/static-app/files/robots.txt"
      + storage_class          = (known after apply)
      + tags                   = (known after apply)
      + tags_all               = (known after apply)
      + version_id             = (known after apply)
    }

  # module.static.aws_servicecatalogappregistry_application.static will be created
  + resource "aws_servicecatalogappregistry_application" "static" {
      + application_tag = (known after apply)
      + arn             = (known after apply)
      + description     = "Static app hosting for Code for America."
      + id              = (known after apply)
      + name            = "cfa-static-apps-development"
      + region          = "us-east-1"
      + tags            = {
          + "application" = "cfa-static-apps-development"
          + "environment" = "development"
          + "program"     = "engineering"
          + "project"     = "cfa-static-apps"
        }
      + tags_all        = {
          + "application" = "cfa-static-apps-development"
          + "environment" = "development"
          + "program"     = "engineering"
          + "project"     = "cfa-static-apps"
        }
    }

  # module.static.local_file.lambda_js will be created
  + resource "local_file" "lambda_js" {
      + content              = (known after apply)
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "../../modules/static-app/dist/oidc/index.js"
      + id                   = (known after apply)
    }

  # module.static.local_file.pkg_json will be created
  + resource "local_file" "pkg_json" {
      + content              = jsonencode(
            {
              + dependencies = {
                  + "@aws-sdk/client-secrets-manager" = "^3.556"
                  + jose                              = "^5.2"
                }
              + description  = "Lambda@Edge function for OIDC authentication"
              + main         = "index.js"
              + name         = "cfa-static-apps-development-oidc"
              + type         = "module"
              + version      = "1.0.0"
            }
        )
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "../../modules/static-app/dist/oidc/package.json"
      + id                   = (known after apply)
    }

  # module.static.null_resource.npm_install will be created
  + resource "null_resource" "npm_install" {
      + id       = (known after apply)
      + triggers = {
          + "lambda_hash"       = (known after apply)
          + "modules_exist"     = "false"
          + "package_json_hash" = "356edafce620ef2c07d9720adf085ad82de123a3bed6d6a58934d80b15dcdc7d"
        }
    }

  # module.static.module.bucket.data.aws_iam_policy_document.combined will be read during apply
  # (config refers to values not yet known)
 <= data "aws_iam_policy_document" "combined" {
      + id                      = (known after apply)
      + json                    = (known after apply)
      + minified_json           = (known after apply)
      + source_policy_documents = (known after apply)
    }

  # module.static.module.bucket.data.aws_iam_policy_document.default will be read during apply
  # (config refers to values not yet known)
 <= data "aws_iam_policy_document" "default" {
      + id            = (known after apply)
      + json          = (known after apply)
      + minified_json = (known after apply)

      + statement {
          + actions   = [
              + "s3:GetBucketLocation",
            ]
          + effect    = "Allow"
          + resources = [
              + (known after apply),
            ]
          + sid       = "DefaultS3BucketPolicyStatement"

          + principals {
              + identifiers = [
                  + "arn:aws:iam::816069131564:root",
                ]
              + type        = "AWS"
            }
        }
    }

  # module.static.module.bucket.aws_s3_bucket.main will be created
  + resource "aws_s3_bucket" "main" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "apps.dev.services.cfa.codes"
      + bucket_domain_name          = (known after apply)
      + bucket_namespace            = (known after apply)
      + bucket_prefix               = (known after apply)
      + bucket_region               = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = true
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = "us-east-1"
      + request_payer               = (known after apply)
      + tags                        = (known after apply)
      + tags_all                    = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + cors_rule (known after apply)

      + grant (known after apply)

      + lifecycle_rule (known after apply)

      + logging (known after apply)

      + object_lock_configuration (known after apply)

      + replication_configuration (known after apply)

      + server_side_encryption_configuration (known after apply)

      + versioning (known after apply)

      + website (known after apply)
    }

  # module.static.module.bucket.aws_s3_bucket_lifecycle_configuration.main[0] will be created
  + resource "aws_s3_bucket_lifecycle_configuration" "main" {
      + bucket                                 = (known after apply)
      + expected_bucket_owner                  = (known after apply)
      + id                                     = (known after apply)
      + region                                 = "us-east-1"
      + transition_default_minimum_object_size = "all_storage_classes_128K"

      + rule {
          + id     = "static-site"
          + status = "Enabled"

          + abort_incomplete_multipart_upload {
              + days_after_initiation = 7
            }

          + filter {
            }

          + noncurrent_version_expiration {
              + noncurrent_days = 30
            }
        }
    }

  # module.static.module.bucket.aws_s3_bucket_logging.main[0] will be created
  + resource "aws_s3_bucket_logging" "main" {
      + bucket        = (known after apply)
      + id            = (known after apply)
      + region        = "us-east-1"
      + target_bucket = "shared-services-development-logs"
      + target_prefix = "/AWSLogs/816069131564/s3accesslogs/apps.dev.services.cfa.codes"
    }

  # module.static.module.bucket.aws_s3_bucket_ownership_controls.main will be created
  + resource "aws_s3_bucket_ownership_controls" "main" {
      + bucket = "apps.dev.services.cfa.codes"
      + id     = (known after apply)
      + region = "us-east-1"

      + rule {
          + object_ownership = "ObjectWriter"
        }
    }

  # module.static.module.bucket.aws_s3_bucket_policy.main will be created
  + resource "aws_s3_bucket_policy" "main" {
      + bucket = "apps.dev.services.cfa.codes"
      + id     = (known after apply)
      + policy = (known after apply)
      + region = "us-east-1"
    }

  # module.static.module.bucket.aws_s3_bucket_public_access_block.main will be created
  + resource "aws_s3_bucket_public_access_block" "main" {
      + block_public_acls       = true
      + block_public_policy     = true
      + bucket                  = "apps.dev.services.cfa.codes"
      + id                      = (known after apply)
      + ignore_public_acls      = true
      + region                  = "us-east-1"
      + restrict_public_buckets = true
    }

  # module.static.module.bucket.aws_s3_bucket_server_side_encryption_configuration.main will be created
  + resource "aws_s3_bucket_server_side_encryption_configuration" "main" {
      + bucket = "apps.dev.services.cfa.codes"
      + id     = (known after apply)
      + region = "us-east-1"

      + rule {
          + blocked_encryption_types = (known after apply)
          + bucket_key_enabled       = true

          + apply_server_side_encryption_by_default {
              + kms_master_key_id = (known after apply)
              + sse_algorithm     = "aws:kms"
            }
        }
    }

  # module.static.module.bucket.aws_s3_bucket_versioning.main will be created
  + resource "aws_s3_bucket_versioning" "main" {
      + bucket = "apps.dev.services.cfa.codes"
      + id     = (known after apply)
      + region = "us-east-1"

      + versioning_configuration {
          + mfa_delete = (known after apply)
          + status     = "Enabled"
        }
    }

  # module.static.module.doppler.data.aws_region.current will be read during apply
  # (depends on a resource or a module with changes pending)
 <= data "aws_region" "current" {
      + description = (known after apply)
      + endpoint    = (known after apply)
      + id          = (known after apply)
      + name        = (known after apply)
      + region      = (known after apply)
    }

  # module.static.module.doppler.aws_iam_policy.this will be created
  + resource "aws_iam_policy" "this" {
      + arn              = (known after apply)
      + attachment_count = (known after apply)
      + description      = "Policy to allow Doppler to access secrets in AWS Secrets Manager."
      + id               = (known after apply)
      + name             = "cfa-static-apps-development-doppler"
      + name_prefix      = (known after apply)
      + path             = "/"
      + policy           = (known after apply)
      + policy_id        = (known after apply)
      + tags             = (known after apply)
      + tags_all         = (known after apply)
    }

  # module.static.module.doppler.aws_iam_role.this will be created
  + resource "aws_iam_role" "this" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRole"
                      + Condition = {
                          + StringEquals = {
                              + "sts:ExternalId" = "08430c37e2a2889dc220"
                            }
                        }
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = "arn:aws:iam::299900769157:root"
                        }
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + create_date           = (known after apply)
      + force_detach_policies = false
      + id                    = (known after apply)
      + managed_policy_arns   = (known after apply)
      + max_session_duration  = 3600
      + name                  = "cfa-static-apps-development-doppler"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags                  = (known after apply)
      + tags_all              = (known after apply)
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # module.static.module.doppler.aws_iam_role_policy_attachments_exclusive.this will be created
  + resource "aws_iam_role_policy_attachments_exclusive" "this" {
      + policy_arns = [
          + (known after apply),
        ]
      + role_name   = "cfa-static-apps-development-doppler"
    }

  # module.static.module.secrets.aws_kms_alias.secrets will be created
  + resource "aws_kms_alias" "secrets" {
      + arn            = (known after apply)
      + id             = (known after apply)
      + name           = "alias/cfa-static-apps/development/secrets"
      + name_prefix    = (known after apply)
      + region         = "us-east-1"
      + target_key_arn = (known after apply)
      + target_key_id  = (known after apply)
    }

  # module.static.module.secrets.aws_kms_key.secrets will be created
  + resource "aws_kms_key" "secrets" {
      + arn                                = (known after apply)
      + bypass_policy_lockout_safety_check = false
      + customer_master_key_spec           = "SYMMETRIC_DEFAULT"
      + deletion_window_in_days            = 30
      + description                        = "Secrets encryption key for cfa-static-apps development"
      + enable_key_rotation                = true
      + id                                 = (known after apply)
      + is_enabled                         = true
      + key_id                             = (known after apply)
      + key_usage                          = "ENCRYPT_DECRYPT"
      + multi_region                       = (known after apply)
      + policy                             = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "kms:*"
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = "arn:aws:iam::816069131564:root"
                        }
                      + Resource  = "*"
                      + Sid       = "Enable IAM User Permissions"
                    },
                  + {
                      + Action    = [
                          + "kms:Encrypt",
                          + "kms:Decrypt",
                          + "kms:ReEncrypt*",
                          + "kms:CreateGrant",
                          + "kms:DescribeKey",
                        ]
                      + Condition = {
                          + StringEquals = {
                              + "kms:CallerAccount" = "816069131564"
                              + "kms:ViaService"    = "secretsmanager.us-east-1.amazonaws.com"
                            }
                        }
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = [
                              + "*",
                            ]
                        }
                      + Resource  = "*"
                      + Sid       = "Allow access through AWS Secrets Manager"
                    },
                  + {
                      + Action    = "kms:GenerateDataKey*"
                      + Condition = {
                          + StringEquals = {
                              + "kms:CallerAccount" = "816069131564"
                            }
                          + StringLike   = {
                              + "kms:ViaService" = "secretsmanager.us-east-1.amazonaws.com"
                            }
                        }
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = [
                              + "*",
                            ]
                        }
                      + Resource  = "*"
                      + Sid       = "Allow access through AWS Secrets Manager"
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + region                             = "us-east-1"
      + rotation_period_in_days            = (known after apply)
      + tags                               = (known after apply)
      + tags_all                           = (known after apply)
    }

  # module.static.module.doppler.module.sync["this"].doppler_integration_aws_secrets_manager.this will be created
  + resource "doppler_integration_aws_secrets_manager" "this" {
      + assume_role_arn = (known after apply)
      + id              = (known after apply)
      + name            = "cfa-static-apps-development-asm"
    }

  # module.static.module.doppler.module.sync["this"].doppler_secrets_sync_aws_secrets_manager.this will be created
  + resource "doppler_secrets_sync_aws_secrets_manager" "this" {
      + config          = "dev"
      + delete_behavior = "leave_in_target"
      + id              = (known after apply)
      + integration     = (known after apply)
      + path            = "cfa-static-apps/development"
      + path_behavior   = "none"
      + project         = "cfa-static-apps"
      + region          = (known after apply)
      + sync_strategy   = "multi-secret"
      + update_metadata = false
    }

  # module.static.module.secrets.module.secrets_manager["OIDC_SETTINGS"].aws_secretsmanager_secret.this[0] will be created
  + resource "aws_secretsmanager_secret" "this" {
      + arn                            = (known after apply)
      + description                    = "OIDC credentials for static app hosting"
      + force_overwrite_replica_secret = false
      + id                             = (known after apply)
      + kms_key_id                     = (known after apply)
      + name                           = "cfa-static-apps/development/OIDC_SETTINGS"
      + name_prefix                    = (known after apply)
      + policy                         = (known after apply)
      + recovery_window_in_days        = 30
      + region                         = "us-east-1"
      + tags                           = (known after apply)
      + tags_all                       = (known after apply)

      + replica (known after apply)
    }

  # module.static.module.secrets.module.secrets_manager["OIDC_SETTINGS"].aws_secretsmanager_secret_version.ignore_changes[0] will be created
  + resource "aws_secretsmanager_secret_version" "ignore_changes" {
      + arn                  = (known after apply)
      + has_secret_string_wo = (known after apply)
      + id                   = (known after apply)
      + region               = "us-east-1"
      + secret_id            = (known after apply)
      + secret_string        = (sensitive value)
      + secret_string_wo     = (write-only attribute)
      + version_id           = (known after apply)
      + version_stages       = (known after apply)
    }

Plan: 39 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + deploy_roles = {}
  + endpoint_url = (known after apply)
  + prefixes     = []

Warning: Deprecated attribute

  on .terraform/modules/static.bucket/locals.tf line 4, in locals:
   4:   region          = data.aws_region.current.name

The attribute "name" is deprecated. Refer to the provider documentation for
details.

(and 7 more similar warnings elsewhere)

Warning: Object attribute is ignored

  on ../../modules/static-app/main.tf line 11, in module "secrets":
   9:     OIDC_SETTINGS = {
  10:       description = "OIDC credentials for static app hosting"
  11:       type        = "json"
  12:       start_value = jsonencode({
  13:         client_id     = "abc",
  14:         client_secret = "123",
  15:       })
  16:     }

The object type for input variable "secrets" nested value ["OIDC_SETTINGS"]
does not include an attribute named "type", so this definition is unused.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    tofu apply "tfplan"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant