diff --git a/.checkov.yml b/.checkov.yml new file mode 100644 index 0000000..02f3a56 --- /dev/null +++ b/.checkov.yml @@ -0,0 +1,13 @@ +block-list-secret-scan: [] +compact: true +directory: + - . +download-external-modules: false +evaluate-variables: true +framework: + - all +output: + - cli +quiet: true +soft-fail: true +summary-position: top diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..03c5f79 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,19 @@ +# Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten. +"updates": +- "directory": "/" + "groups": + "github-actions": + "patterns": + - "*" + "package-ecosystem": "github-actions" + "schedule": + "interval": "daily" +- "directory": "/" + "groups": + "opentofu": + "patterns": + - "*" + "package-ecosystem": "opentofu" + "schedule": + "interval": "daily" +"version": 2 diff --git a/.github/workflows/dependabot-notify.yml b/.github/workflows/dependabot-notify.yml new file mode 100644 index 0000000..de27250 --- /dev/null +++ b/.github/workflows/dependabot-notify.yml @@ -0,0 +1,15 @@ +--- +# Managed by tfroot-github (gh-dependabot.tf); local edits are overwritten. +name: dependabot-notify + +on: + pull_request: + types: [opened, reopened] + +permissions: {} + +jobs: + notify: + if: github.actor == 'dependabot[bot]' + uses: makeitworkcloud/shared-workflows/.github/workflows/_dependabot-notify.yml@main + secrets: inherit diff --git a/.github/workflows/opentofu.yml b/.github/workflows/opentofu.yml new file mode 100644 index 0000000..098bcca --- /dev/null +++ b/.github/workflows/opentofu.yml @@ -0,0 +1,23 @@ +name: opentofu + +on: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: write + id-token: write + pull-requests: write + +jobs: + opentofu: + if: >- + github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository + uses: makeitworkcloud/shared-workflows/.github/workflows/opentofu.yml@main + secrets: + CHART_UPDATER_GITHUB_APP_PRIVATE_KEY: ${{ secrets.CHART_UPDATER_GITHUB_APP_PRIVATE_KEY }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca178e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +**/*.sw[po] + +# Do not commit OpenTofu state, lock files, or working directories. +**/.terraform.lock.hcl +**/.terraform + +**/.vscode +**/.DS_Store + +plan-output.txt + +# Generated canonical pre-commit configuration +/.pre-commit-config.yaml +/.pre-commit-config.yaml.tmp diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..925bb96 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,6 @@ +[extend] +useDefault = true + +[[allowlists]] +description = "Generated OpenTofu working data is ignored and never committed" +paths = ['''(^|/)\.terraform(?:/.*)?$'''] diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..99901db --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,3 @@ +--- +creation_rules: + - kms: arn:aws:kms:us-west-2:332355796717:key/0a45c0f6-71dc-4d54-ab33-9df4de1a9e91 diff --git a/.terraform-docs.yml b/.terraform-docs.yml new file mode 100644 index 0000000..c11fd54 --- /dev/null +++ b/.terraform-docs.yml @@ -0,0 +1,20 @@ +formatter: "markdown" + +output: + file: "README.md" + mode: replace + template: | + + {{ .Content }} + + +settings: + color: false + lockfile: false + +sort: + enabled: true + by: name + +recursive: + enabled: false diff --git a/.tflint.hcl b/.tflint.hcl new file mode 100644 index 0000000..062eb57 --- /dev/null +++ b/.tflint.hcl @@ -0,0 +1,12 @@ +plugin "terraform" { + enabled = true + preset = "recommended" +} + +rule "terraform_required_providers" { + enabled = false +} + +rule "terraform_required_version" { + enabled = false +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..47f6717 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ +# Agent Instructions + +OpenTofu root for Make IT Work Cloud Twilio control-plane infrastructure. + +This root owns only Twilio phone-number inventory and inbound messaging-webhook configuration. It must not own the OpenCode SMS bridge workload, Cloudflare workload route or DNS, number-to-agent mapping, approved-source allowlist, bridge credentials, or encryption material. Those runtime concerns belong to `kustomize-cluster`. + +This bootstrap contains no Twilio resources, provider configuration, backend credentials, encrypted secrets, state, or number identifiers. Use GitHub MCP and pull-request CI plans as validation authority. `main` is an environment-gated apply path; use scoped branches and pull requests, never direct pushes. Do not run OpenTofu, SOPS, state, import, or apply commands from this server. + +The shared workflow is owned by `shared-workflows`; the runner image and canonical pre-commit configuration are owned by `images/tfroot-runner`. Keep any future SOPS data encrypted and never expose credentials, decrypted values, state, private keys, or sensitive plans. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a8476b8 --- /dev/null +++ b/Makefile @@ -0,0 +1,50 @@ +SHELL := /bin/bash +TERRAFORM := $(shell which tofu) + +.PHONY: clean init plan apply test pre-commit-config pre-commit-check-deps pre-commit-install-hooks + +clean: + @find . -name .terraform -type d | xargs -r rm -rf + +# Bootstrap deliberately uses no remote backend until the encrypted backend +# contract and least-privilege CI access are established in a later PR. +init: clean + @${TERRAFORM} init -backend=false -upgrade -input=false + +plan: init + @${TERRAFORM} plan -refresh=false -input=false -lock=false -compact-warnings + +# There are intentionally no provider configurations or Twilio resources in +# this bootstrap, so the main-branch apply has no provider-side effect. +apply: init + @${TERRAFORM} apply -auto-approve -refresh=false -input=false -lock=false -compact-warnings + +test: pre-commit-config pre-commit-install-hooks + @pre-commit run -a + +pre-commit-config: + @curl --fail --silent --show-error --location \ + --output .pre-commit-config.yaml.tmp \ + https://raw.githubusercontent.com/makeitworkcloud/images/main/tfroot-runner/pre-commit-config.yaml + @if cmp -s .pre-commit-config.yaml.tmp .pre-commit-config.yaml; then \ + rm -f .pre-commit-config.yaml.tmp; \ + else \ + mv .pre-commit-config.yaml.tmp .pre-commit-config.yaml; \ + fi + +DEPS_PRE_COMMIT=$(shell which pre-commit || echo "pre-commit not found") +DEPS_TERRAFORM_DOCS=$(shell which terraform-docs || echo "terraform-docs not found") +DEPS_TFLINT=$(shell which tflint || echo "tflint not found") +DEPS_CHECKOV=$(shell which checkov || echo "checkov not found") +DEPS_JQ=$(shell which jq || echo "jq not found") +pre-commit-check-deps: + @echo "Checking for pre-commit and its dependencies:" + @echo " pre-commit: ${DEPS_PRE_COMMIT}" + @echo " terraform-docs: ${DEPS_TERRAFORM_DOCS}" + @echo " tflint: ${DEPS_TFLINT}" + @echo " checkov: ${DEPS_CHECKOV}" + @echo " jq: ${DEPS_JQ}" + @echo "" + +pre-commit-install-hooks: pre-commit-config pre-commit-check-deps + @pre-commit install --install-hooks --hook-type pre-commit --hook-type commit-msg diff --git a/README.md b/README.md index 3dcf5aa..bf81611 100644 --- a/README.md +++ b/README.md @@ -1 +1,28 @@ -# tfroot-twilio \ No newline at end of file + +## Requirements + +| Name | Version | +| ---- | ------- | +| [terraform](#requirement\_terraform) | > 1.3 | +| [twilio](#requirement\_twilio) | 0.27.1 | + +## Providers + +No providers. + +## Modules + +No modules. + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +No outputs. + diff --git a/docs/bootstrap.md b/docs/bootstrap.md new file mode 100644 index 0000000..eaf9785 --- /dev/null +++ b/docs/bootstrap.md @@ -0,0 +1,25 @@ +# Twilio root bootstrap + +## Scope + +This initial root validates the OpenTofu toolchain and provider installation only. It creates, imports, updates, or deletes **no** Twilio resources. In particular, it does not purchase or configure phone numbers, webhooks, credentials, messaging services, or runtime routing. + +The evaluated provider is `RJPearson94/twilio` `0.27.1`. Registry documentation confirms it supports Twilio phone-number resources and inbound `messaging` webhook fields, but it is community-maintained and requires existing account credentials. The bootstrap intentionally declares no provider configuration, so CI receives no Twilio credentials and performs no provider-side API call. + +## Ownership boundary + +The future root may own only Twilio phone-number inventory and inbound messaging-webhook configuration. `kustomize-cluster` remains the owner of the OpenCode bridge workload, workload `TunnelBinding`/DNS, fixed number-to-agent map, approved-source allowlist, runtime secrets, and state-encryption inputs. The root must never create a bridge-worker API key or duplicate a runtime owner. + +## Backend and credential contract + +The checked-in `.sops.yaml` identifies the approved encryption recipient but no encrypted secret file exists yet. A later, separately reviewed change must: + +1. establish least-privilege GitHub Actions OIDC access for this exact repository to decrypt SOPS material; +2. add an encrypted backend configuration for a dedicated Twilio state object; and +3. define an existing Twilio credential delivery path that cannot expose a token in source, CI logs, or OpenTofu state. + +Only after those prerequisites pass pull-request validation may a later root change add provider configuration or Twilio inventory. Any actual Twilio provisioning or webhook update still requires explicit owner confirmation before merge because `main` invokes the environment-gated apply path. + +## Central generated files + +`.github/dependabot.yml` and `.github/workflows/dependabot-notify.yml` are seeded here with the exact content generated by `tfroot-github`. They are not locally owned. This PR-based seed resolves the branch-protection bootstrap conflict; after merge, the central `github_repository_file` resources can adopt and continue managing them with `overwrite_on_create = true`. diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..adbd32e --- /dev/null +++ b/main.tf @@ -0,0 +1,3 @@ +# Bootstrap intentionally contains no Twilio resources, data sources, provider +# configuration, or secret material. The next root change must first establish +# the encrypted backend and credential-delivery contract described in docs/bootstrap.md. diff --git a/providers.tf b/providers.tf new file mode 100644 index 0000000..e580aec --- /dev/null +++ b/providers.tf @@ -0,0 +1,18 @@ +terraform { + required_version = "> 1.3" + + required_providers { + twilio = { + source = "RJPearson94/twilio" + version = "0.27.1" + } + } +} + +# The permanent S3 backend is intentionally omitted until its encrypted +# contract and least-privilege CI access are reviewed in a follow-up PR. +# The bootstrap Makefile initializes with -backend=false. + +# No provider block is declared during bootstrap. The candidate provider is +# installed and syntax-validated without receiving credentials or managing +# Twilio resources.