diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..b53e01e --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,16 @@ +{ + "permissions": { + "allow": [ + "Bash(ssh:*)", + "Bash(git checkout:*)", + "Bash(git add:*)", + "Bash(git commit:*)", + "Bash(GIT_SSH_COMMAND=\"ssh -i ~/.ssh/id_rsa -o IdentitiesOnly=yes\" git push:*)", + "WebFetch(domain:raw.githubusercontent.com)", + "WebFetch(domain:github.com)", + "WebSearch", + "Bash(gh pr:*)", + "Bash(curl:*)" + ] + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..ea567f6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug Report +about: Report a bug or unexpected behavior in this module +title: '[BUG] ' +labels: bug +assignees: '' +--- + +## Describe the Bug +A clear and concise description of what the bug is. + +## Steps to Reproduce +1. Module configuration used (sanitize any secrets) +2. Command run (e.g. `terraform apply`) +3. See error + +## Expected Behavior +What you expected to happen. + +## Actual Behavior +What actually happened. Include the full error message/stack trace if applicable. + +## Environment +- Terraform / OpenTofu version: +- Provider version (`digitalocean/digitalocean`): +- Module version / git ref: +- OS: + +## Additional Context +Any other context, logs, or screenshots that might help diagnose the issue. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..6812cbc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: CloudDrove Community + url: https://clouddrove.com + about: For general questions and community support. + - name: DigitalOcean Terraform Provider Docs + url: https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs + about: Official DigitalOcean Terraform provider documentation. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..d1495bd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: Feature Request +about: Suggest a new feature or enhancement for this module +title: '[FEATURE] ' +labels: enhancement +assignees: '' +--- + +## Summary +A clear and concise description of the feature you're requesting. + +## Problem / Motivation +Describe the problem this feature would solve or the use case it enables. + +## Proposed Solution +Describe the solution you'd like. Include example Terraform/OpenTofu configuration if possible. + +## Alternatives Considered +List any alternative approaches or workarounds you've already tried. + +## Additional Context +Any other context, links to upstream provider docs, or related issues. diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 4fb94b4..74dd5c2 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -1,15 +1,15 @@ name: Auto merge on: pull_request: -permissions: +permissions: contents: write checks: read pull-requests: write jobs: auto-merge: - uses: clouddrove/github-shared-workflows/.github/workflows/auto_merge.yml@521761c1a5cf68d919980a2ed84b755182aeabfe # pinned to latest + uses: clouddrove/github-shared-workflows/.github/workflows/auto_merge.yml@88efd7724e007c8f721a219498be29e0c9ad471b # pinned to latest with: azure_cloud: true tfchecks_azure: '["tf-lint / tflint"]' secrets: - GITHUB: ${{ secrets.GITHUB }} \ No newline at end of file + GITHUB: ${{ secrets.GITHUB }} diff --git a/.github/workflows/checkov.yml b/.github/workflows/checkov.yml index 552ab00..39fa4b1 100644 --- a/.github/workflows/checkov.yml +++ b/.github/workflows/checkov.yml @@ -1,14 +1,14 @@ name: Security Scan -on: - pull_request: +on: + pull_request: branches: [master] types: [opened, synchronize] -jobs: +jobs: checkov: uses: clouddrove/github-shared-workflows/.github/workflows/checkov.yml@f6cad30db3bfad5fc248a08ab65ac82eb8dbcb82 # pinned to latest with: directory: '.' continue_on_error: 'true' - skip_check: 'CKV_TF_1' \ No newline at end of file + skip_check: 'CKV_TF_1' diff --git a/.github/workflows/gitleaks-pr-scan.yml b/.github/workflows/gitleaks-pr-scan.yml index bc391d3..44de1a7 100644 --- a/.github/workflows/gitleaks-pr-scan.yml +++ b/.github/workflows/gitleaks-pr-scan.yml @@ -9,5 +9,5 @@ permissions: jobs: gitleaks: - uses: clouddrove/github-shared-workflows/.github/workflows/gitleaks-pr-scan.yml@521761c1a5cf68d919980a2ed84b755182aeabfe + uses: clouddrove/github-shared-workflows/.github/workflows/gitleaks-pr-scan.yml@62909fc10e105bba5cba4ff73b03c020b97789d4 # pinned to latest secrets: inherit diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml index 1b2d3cd..517a5f6 100644 --- a/.github/workflows/pr_checks.yml +++ b/.github/workflows/pr_checks.yml @@ -1,6 +1,7 @@ -name: 'PR Validation' +name: PR Validation + on: - pull_request_target: + pull_request_target: types: - opened - edited @@ -8,9 +9,9 @@ on: - reopened jobs: - pre-validation: + pr-validation: if: github.actor != 'dependabot[bot]' uses: clouddrove/github-shared-workflows/.github/workflows/pr_checks.yml@f6cad30db3bfad5fc248a08ab65ac82eb8dbcb82 # pinned to latest secrets: inherit with: - subjectPattern: '^.+$' \ No newline at end of file + subjectPattern: '^.+$' diff --git a/.github/workflows/stale_pr.yml b/.github/workflows/stale_pr.yml index a9469f2..8377025 100644 --- a/.github/workflows/stale_pr.yml +++ b/.github/workflows/stale_pr.yml @@ -1,14 +1,14 @@ -name: 'Mark or close stale issues and PRs' +name: Mark or close stale issues and PRs -on: +on: schedule: - - cron: '0 0 * * 5' # Runs every Friday midnight + - cron: '0 0 * * 5' # Runs every Friday midnight workflow_dispatch: jobs: call-shared-stale: uses: clouddrove/github-shared-workflows/.github/workflows/stale_pr.yml@f6cad30db3bfad5fc248a08ab65ac82eb8dbcb82 # pinned to latest - with: + with: days-before-issue-stale: 30 days-before-pr-stale: 30 days-before-issue-close: 10 diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index 974826c..82a4fe2 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -4,10 +4,10 @@ on: pull_request: types: [closed] -jobs: +jobs: release: uses: clouddrove/github-shared-workflows/.github/workflows/tag-release.yml@f6cad30db3bfad5fc248a08ab65ac82eb8dbcb82 # pinned to latest with: target_branch: master secrets: - GITHUB: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tf-checks.yml b/.github/workflows/tf-checks.yml index acf7cfb..234557c 100644 --- a/.github/workflows/tf-checks.yml +++ b/.github/workflows/tf-checks.yml @@ -8,10 +8,11 @@ jobs: complete-example: uses: clouddrove/github-shared-workflows/.github/workflows/stf-checks.yml@521761c1a5cf68d919980a2ed84b755182aeabfe # pinned to latest with: + provider: digitalocean working_directory: './_examples/complete/' -# Seperate Job for TFlint workflow call +# Separate job for TFlint workflow call tf-lint: uses: clouddrove/github-shared-workflows/.github/workflows/tf-lint.yml@f6cad30db3bfad5fc248a08ab65ac82eb8dbcb82 # pinned to latest - secrets: + secrets: GITHUB: ${{ secrets.GITHUB }} diff --git a/.github/workflows/tflint.yml b/.github/workflows/tflint.yml index 5ba7301..76d5e16 100644 --- a/.github/workflows/tflint.yml +++ b/.github/workflows/tflint.yml @@ -8,6 +8,6 @@ on: jobs: tf-lint: - uses: clouddrove/github-shared-workflows/.github/workflows/tf-lint.yml@c615ea7ef3e5beba98a335bf9acce8e67e03c755 # pinned to latest + uses: clouddrove/github-shared-workflows/.github/workflows/tf-lint.yml@master secrets: GITHUB: ${{ secrets.GITHUB }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2fc1df8..0a62a95 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,14 @@ repos: + - repo: https://github.com/gruntwork-io/pre-commit - rev: v0.1.12 # Get the latest from: https://github.com/gruntwork-io/pre-commit/releases + rev: v0.1.23 # Get the latest from: https://github.com/gruntwork-io/pre-commit/releases hooks: - id: terraform-fmt - id: shellcheck - id: tflint - - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 # Use the ref you want to point at + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 # Use the ref you want to point at hooks: - id: end-of-file-fixer - id: trailing-whitespace @@ -17,4 +18,4 @@ repos: - id: check-merge-conflict - id: debug-statements - id: check-yaml - - id: check-added-large-files \ No newline at end of file + - id: check-added-large-files diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..efe9cf7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,42 @@ +# Security Policy + +## Supported Versions + +We actively maintain the latest major release of each module. Security fixes are applied to the latest release only. + +| Version | Supported | +|---------|-----------| +| Latest | Yes | +| Older | No | + +## Reporting a Vulnerability + +**Please do not report security vulnerabilities through public GitHub issues.** + +To report a security issue, email us at **support@clouddrove.com** with the subject line: +`[SECURITY] terraform-do-modules/` + +Include as much of the following as possible: + +- Type of issue (e.g. exposed secret, privilege escalation, insecure default) +- Affected module and version/git ref +- Steps to reproduce or proof-of-concept +- Potential impact assessment + +We will acknowledge receipt within **48 hours** and aim to provide a fix or mitigation within **7 days** for critical issues. + +## Scope + +This policy covers: +- Terraform module code in this repository +- GitHub Actions workflow configurations +- Example configurations under `_examples/` + +This policy does **not** cover: +- The DigitalOcean provider itself (report to [HashiCorp](https://www.hashicorp.com/security) or [DigitalOcean](https://www.digitalocean.com/security)) +- Issues in third-party dependencies (report upstream) + +## Preferred Languages + +We accept reports in **English**. + diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..0f81c67 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,78 @@ +# Architecture: terraform-digitalocean-vpc + +## Overview + +A Virtual Private Cloud (VPC) is the network foundation for all DigitalOcean resources. It provides a logically isolated network environment in which droplets, Kubernetes clusters, databases, and load balancers communicate using private IP addresses, without traversing the public internet. This module manages a single `digitalocean_vpc` resource and applies consistent naming via the shared labels module. + +## VPC as Network Foundation + +Every workload in DigitalOcean that requires private networking must be placed inside a VPC. The VPC is created first and its `id` output is passed as `vpc_uuid` to downstream modules. This dependency chain ensures resources are always colocated on the same private network fabric. + +Typical dependency order: + +1. VPC (this module) +2. Droplet / Kubernetes / Database modules (consume `vpc.id`) +3. Load Balancer module (attached to the VPC-resident resources) + +## CIDR Planning (RFC 1918) + +DigitalOcean VPCs accept any CIDR block that satisfies all of the following: + +- Falls within RFC 1918 private address space: `10.0.0.0/8`, `172.16.0.0/12`, or `192.168.0.0/16` +- Is no larger than `/16` (65 536 addresses) +- Is no smaller than `/24` (256 addresses) + +Recommended sizing guidance: + +| Use case | CIDR example | Usable hosts | +|--------------------|-------------------|--------------| +| Small environment | 10.x.x.0/24 | 254 | +| Medium environment | 10.x.0.0/22 | 1 022 | +| Large environment | 10.x.0.0/16 | 65 534 | + +Choose non-overlapping ranges across all VPCs in the same account and across any remote networks that may be peered or connected via VPN in the future. + +## Regional Scope + +A DigitalOcean VPC is scoped to a single region (e.g., `blr1`, `nyc3`, `fra1`, `sgp1`). All resources attached to a VPC must reside in the same region. Cross-region private connectivity is not natively supported; inter-region traffic must traverse the public internet or a private VPN endpoint. + +This module exposes the `region` variable (default `blr1`) so that the VPC region matches the region used by downstream modules. + +## Integration with Other Modules + +| Consumer module | Input variable | Source output | +|----------------------------------|-----------------|-----------------| +| terraform-digitalocean-droplet | `vpc_uuid` | `module.vpc.id` | +| terraform-digitalocean-kubernetes| `vpc_uuid` | `module.vpc.id` | +| terraform-digitalocean-database | `vpc_uuid` | `module.vpc.id` | +| terraform-digitalocean-lb | `vpc_uuid` | `module.vpc.id` | + +Example wiring: + +```hcl +module "vpc" { + source = "terraform-do-modules/vpc/digitalocean" + version = "1.0.0" + name = "app" + environment = "prod" + region = "blr1" + ip_range = "10.10.0.0/16" +} + +module "cluster" { + source = "terraform-do-modules/kubernetes/digitalocean" + version = "1.0.0" + vpc_uuid = module.vpc.id + # ... +} +``` + +## Operational Checklist + +- Do not use overlapping CIDR ranges across VPCs in the same account or with on-premises networks. +- DigitalOcean does not support native VPC peering; plan CIDR ranges as if each VPC is a standalone island. +- Each account has a default VPC per region; avoid using it for workloads — the `default` output identifies whether the VPC is the region default. +- Pin the module version in production to prevent unintended VPC replacement on version upgrades. +- VPC deletion is blocked if resources are still attached; decommission all droplets, clusters, and databases first. +- Set `enabled = false` to skip resource creation without removing the module call from the configuration. +- Record the VPC `id` and `urn` outputs in remote state so they are referenceable by other root modules without hard-coding. diff --git a/docs/io.md b/docs/io.md new file mode 100644 index 0000000..b057ba4 --- /dev/null +++ b/docs/io.md @@ -0,0 +1,23 @@ +# Inputs and Outputs: terraform-digitalocean-vpc + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|----------| +| `name` | Name (e.g. `app` or `cluster`). | `string` | `""` | no | +| `environment` | Environment (e.g. `prod`, `dev`, `staging`). | `string` | `""` | no | +| `label_order` | Label order, e.g. `name`. | `list(any)` | `["name", "environment"]` | no | +| `managedby` | ManagedBy, eg `terraform-do-modules` or `hello@clouddrove.com`. | `string` | `"terraform-do-modules"` | no | +| `enabled` | A boolean flag to enable/disable vpc. | `bool` | `true` | no | +| `region` | The region to create VPC, like `blr1`. | `string` | `"blr1"` | no | +| `description` | A free-form text field up to a limit of 255 characters to describe the VPC. | `string` | `"VPC"` | no | +| `ip_range` | The range of IP addresses for the VPC in CIDR notation. Network ranges cannot overlap with other networks in the same account and must be in range of private addresses as defined in RFC1918. It may not be larger than /16 or smaller than /24. | `string` | `""` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| `id` | The unique identifier for the VPC. | +| `urn` | The uniform resource name (URN) for the VPC. | +| `default` | A boolean indicating whether or not the VPC is the default one for the region. | +| `created_at` | The date and time of when the VPC was created. |