Skip to content

feat(aws): add xnoto.dev website publisher - #51

Merged
xnoto merged 4 commits into
mainfrom
feat/xnoto-dev-site-publisher
Sep 5, 2026
Merged

feat(aws): add xnoto.dev website publisher#51
xnoto merged 4 commits into
mainfrom
feat/xnoto-dev-site-publisher

Conversation

@xnoto

@xnoto xnoto commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add xnoto.dev to the managed public S3 website-bucket class
  • add a separate GitHub Actions OIDC publisher role trusted only by xnoto/xnoto.dev on main, using its ID-based subject claim
  • grant that role only bucket listing and asset publish actions for xnoto.dev

No linked issue.

Type of change

  • Bug fix
  • Feature / enhancement
  • Documentation
  • Infrastructure (OpenTofu root or module)
  • GitOps desired state (manifests, kustomize, charts, SOPS/KSOPS secrets)
  • Container image
  • CI / reusable workflow
  • Refactor / cleanup
  • Breaking change

Validation

  • Required pull-request checks pass — pending OpenTofu test and plan.
  • Generated or centrally distributed files were regenerated by their owning automation, not hand-edited — no generated files are included.

Impact and rollout

  • Producer: the existing aws_s3_bucket.web public-website resource class gains xnoto.dev; its consumer is the new portfolio site deployment workflow, which remains intentionally absent until this infrastructure is applied and separately approved.
  • The plan should add the xnoto.dev website bucket, its public-access configuration, anonymous-read policy, website configuration, the dedicated OIDC role, its narrowly scoped policy, and the non-sensitive role ARN output. It must not change existing buckets, SOPS state, CloudFront, or unrelated IAM roles.
  • The publisher trust is restricted to repo:xnoto@121333299/xnoto.dev@1358381234:ref:refs/heads/main; it cannot manage infrastructure, state, or CloudFront.
  • After passing plan review and explicit merge approval, the existing main workflow is intended to apply the desired state. Environment-gate attachment in the shared workflow is currently an unknown requiring reviewer confirmation. No site deployment is dispatched by this PR.
  • Rollback before apply is closing the PR. The bucket has prevent_destroy; after apply, removal of the source membership would require a deliberate follow-up plan and would not destroy the bucket automatically.

Safety and secrets

  • Contains no plaintext secrets, decrypted SOPS values, state files, kubeconfigs, tokens, or private endpoints.
  • No local OpenTofu init/plan/apply/destroy/import/state operations were run or claimed — plans come from pull-request checks.
  • Breaking or irreversible effects are described above with rollback notes.

AI assistance materially produced this change; reviewers should verify the plan is limited to the listed website resources and that the OIDC subject and S3 policy remain confined to xnoto.dev.

@xnoto
xnoto requested a review from a team as a code owner September 5, 2026 17:09
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

OpenTofu Test

OpenTofu test passed.

View the workflow run.

View run output
Terraform validate.......................................................Passed
Terraform validate with tflint...........................................Passed
Checkov..................................................................Passed
Terraform fmt............................................................Passed
Terraform docs...........................................................Passed
Detect hardcoded secrets.................................................Passed
check for case conflicts.................................................Passed
check for merge conflicts................................................Passed
check for broken symlinks............................(no files to check)Skipped
check vcs permalinks.....................................................Passed
detect destroyed symlinks................................................Passed
detect private key.......................................................Passed
fix end of files.........................................................Passed
mixed line ending........................................................Passed
trim trailing whitespace.................................................Passed
don't commit to branch..................................................Skipped
check for added large files..............................................Passed

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

OpenTofu Plan

OpenTofu plan passed.

View the workflow run.

View run output
OpenTofu will perform the following actions:

  # aws_iam_role.github_actions_xnoto_dev_site_deploy will be created
  + resource "aws_iam_role" "github_actions_xnoto_dev_site_deploy" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRoleWithWebIdentity"
                      + Condition = {
                          + StringEquals = {
[REDACTED: potentially sensitive plan output]
[REDACTED: potentially sensitive plan output]
                            }
                        }
                      + Effect    = "Allow"
                      + Principal = {
[REDACTED: potentially sensitive plan output]
                        }
                    },
                ]
              + 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                  = "github-actions-xnoto-dev-site-deploy"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags                  = {
          + "ManagedBy" = "Terraform"
          + "Purpose"   = "xnoto-dev-site-deployment"
        }
      + tags_all              = {
          + "ManagedBy" = "Terraform"
          + "Purpose"   = "xnoto-dev-site-deployment"
        }
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # aws_iam_role_policy.github_actions_xnoto_dev_site_deploy will be created
  + resource "aws_iam_role_policy" "github_actions_xnoto_dev_site_deploy" {
      + id          = (known after apply)
      + name        = "xnoto-dev-site-deployment"
      + name_prefix = (known after apply)
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action   = [
                          + "s3:ListBucket",
                        ]
                      + Effect   = "Allow"
                      + Resource = "arn:aws:s3:::xnoto.dev"
                      + Sid      = "ListSiteAssetBucket"
                    },
                  + {
                      + Action   = [
                          + "s3:AbortMultipartUpload",
                          + "s3:DeleteObject",
                          + "s3:GetObject",
                          + "s3:ListMultipartUploadParts",
                          + "s3:PutObject",
                        ]
                      + Effect   = "Allow"
                      + Resource = "arn:aws:s3:::xnoto.dev/*"
                      + Sid      = "PublishSiteAssets"
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + role        = (known after apply)
    }

  # aws_s3_bucket.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket" "web" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "xnoto.dev"
      + 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               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = "us-west-2"
      + request_payer               = (known after apply)
      + tags                        = {
          + "ManagedBy" = "Terraform"
        }
      + tags_all                    = {
          + "ManagedBy" = "Terraform"
        }
      + 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)
    }

  # aws_s3_bucket_policy.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket_policy" "web" {
      + bucket = "xnoto.dev"
      + id     = (known after apply)
      + policy = (known after apply)
      + region = "us-west-2"
    }

  # aws_s3_bucket_public_access_block.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket_public_access_block" "web" {
      + block_public_acls       = false
      + block_public_policy     = false
      + bucket                  = "xnoto.dev"
      + id                      = (known after apply)
      + ignore_public_acls      = false
      + region                  = "us-west-2"
      + restrict_public_buckets = false
    }

  # aws_s3_bucket_website_configuration.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket_website_configuration" "web" {
      + bucket           = "xnoto.dev"
      + id               = (known after apply)
      + region           = "us-west-2"
      + routing_rules    = (known after apply)
      + website_domain   = (known after apply)
      + website_endpoint = (known after apply)

      + index_document {
          + suffix = "index.html"
        }

      + routing_rule (known after apply)
    }

Plan: 6 to add, 0 to change, 0 to destroy.
OpenTofu will perform the following actions:

  # aws_iam_role.github_actions_xnoto_dev_site_deploy will be created
  + resource "aws_iam_role" "github_actions_xnoto_dev_site_deploy" {
      + arn                   = (known after apply)
      + assume_role_policy    = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "sts:AssumeRoleWithWebIdentity"
                      + Condition = {
                          + StringEquals = {
[REDACTED: potentially sensitive plan output]
[REDACTED: potentially sensitive plan output]
                            }
                        }
                      + Effect    = "Allow"
                      + Principal = {
[REDACTED: potentially sensitive plan output]
                        }
                    },
                ]
              + 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                  = "github-actions-xnoto-dev-site-deploy"
      + name_prefix           = (known after apply)
      + path                  = "/"
      + tags                  = {
          + "ManagedBy" = "Terraform"
          + "Purpose"   = "xnoto-dev-site-deployment"
        }
      + tags_all              = {
          + "ManagedBy" = "Terraform"
          + "Purpose"   = "xnoto-dev-site-deployment"
        }
      + unique_id             = (known after apply)

      + inline_policy (known after apply)
    }

  # aws_iam_role_policy.github_actions_xnoto_dev_site_deploy will be created
  + resource "aws_iam_role_policy" "github_actions_xnoto_dev_site_deploy" {
      + id          = (known after apply)
      + name        = "xnoto-dev-site-deployment"
      + name_prefix = (known after apply)
      + policy      = jsonencode(
            {
              + Statement = [
                  + {
                      + Action   = [
                          + "s3:ListBucket",
                        ]
                      + Effect   = "Allow"
                      + Resource = "arn:aws:s3:::xnoto.dev"
                      + Sid      = "ListSiteAssetBucket"
                    },
                  + {
                      + Action   = [
                          + "s3:AbortMultipartUpload",
                          + "s3:DeleteObject",
                          + "s3:GetObject",
                          + "s3:ListMultipartUploadParts",
                          + "s3:PutObject",
                        ]
                      + Effect   = "Allow"
                      + Resource = "arn:aws:s3:::xnoto.dev/*"
                      + Sid      = "PublishSiteAssets"
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + role        = (known after apply)
    }

  # aws_s3_bucket.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket" "web" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "xnoto.dev"
      + 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               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + object_lock_enabled         = (known after apply)
      + policy                      = (known after apply)
      + region                      = "us-west-2"
      + request_payer               = (known after apply)
      + tags                        = {
          + "ManagedBy" = "Terraform"
        }
      + tags_all                    = {
          + "ManagedBy" = "Terraform"
        }
      + 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)
    }

  # aws_s3_bucket_policy.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket_policy" "web" {
      + bucket = "xnoto.dev"
      + id     = (known after apply)
      + policy = (known after apply)
      + region = "us-west-2"
    }

  # aws_s3_bucket_public_access_block.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket_public_access_block" "web" {
      + block_public_acls       = false
      + block_public_policy     = false
      + bucket                  = "xnoto.dev"
      + id                      = (known after apply)
      + ignore_public_acls      = false
      + region                  = "us-west-2"
      + restrict_public_buckets = false
    }

  # aws_s3_bucket_website_configuration.web["xnoto.dev"] will be created
  + resource "aws_s3_bucket_website_configuration" "web" {
      + bucket           = "xnoto.dev"
      + id               = (known after apply)
      + region           = "us-west-2"
      + routing_rules    = (known after apply)
      + website_domain   = (known after apply)
      + website_endpoint = (known after apply)

      + index_document {
          + suffix = "index.html"
        }

      + routing_rule (known after apply)
    }

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

@xnoto
xnoto merged commit fd43861 into main Sep 5, 2026
3 checks passed
@xnoto
xnoto deleted the feat/xnoto-dev-site-publisher branch September 5, 2026 17:16
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