Skip to content

Fix deploy and make it general - #124

Merged
humbeatbox merged 2 commits into
mainfrom
gary
Aug 1, 2026
Merged

Fix deploy and make it general#124
humbeatbox merged 2 commits into
mainfrom
gary

Conversation

@humbeatbox

@humbeatbox humbeatbox commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added configurable deployment settings for AWS profiles, domains, repositories, state storage, and project names.
    • Added example configuration files for Terraform variables and backend setup.
    • Enabled deployment workflows to use repository-configured host and health-check values.
  • Documentation

    • Updated deployment guides with instructions for configuring and deploying personal forks.
    • Clarified setup requirements, state storage, and configurable infrastructure values.
  • Chores

    • Updated infrastructure policies and ignored generated Terraform configuration files.

…deploys

Remove personal defaults (domain, AWS profile, state bucket, GitHub repo
fork URL) from Terraform variables so other deployers can fork and apply
with their own values via terraform.tfvars, without editing .tf files.

- Add project_name variable driving Lightsail resource naming
- Convert infra/main/backend.tf to a partial S3 backend config
  (bucket/key/region now supplied via backend.hcl, gitignored)
- Widen bootstrap IAM policy Resource to "*" so the JSON works unedited
  for any deployer's own AWS account
- Parameterize CI deploy host/health-check URL via GitHub Actions
  repository variables (SSH_HOST, API_HEALTH_URL)
- Add terraform.tfvars.example / backend.hcl.example templates

Verified zero-diff against the real production state: terraform plan
output is identical before and after this change (bootstrap: "No
changes"; main: same pre-existing drift, unrelated to this refactor).
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@humbeatbox, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3314a512-d76b-41c8-920b-47e1060a5b34

📥 Commits

Reviewing files that changed from the base of the PR and between e9c0196 and 188f1d4.

📒 Files selected for processing (1)
  • infra/main/README.md
📝 Walkthrough

Walkthrough

The PR generalizes bootstrap and main Terraform configuration for fork deployments. It externalizes backend settings, parameterizes resource names and deployment inputs, broadens bootstrap S3 policy resources, and configures CI to read deployment endpoints from repository variables.

Changes

Fork deployment infrastructure

Layer / File(s) Summary
Bootstrap state and IAM configuration
infra/bootstrap/...
Bootstrap variables and instructions now require configured AWS and state-bucket values. Bootstrap and deploy policies use wildcard S3 resources.
Main Terraform configuration
infra/main/..., .gitignore
The main deployment accepts deployer-specific variables, loads backend settings from ignored backend.hcl, and derives Lightsail names from project_name.
CI deployment endpoints
.github/workflows/ci.yml, infra/main/README.md
The deployment workflow reads SSH_HOST and API_HEALTH_URL from repository variables. Documentation reflects these settings.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • 86unj/Foundit#120: Introduced the infrastructure and CI files refined by this PR.
  • 86unj/Foundit#121: Changed related deployment configuration in infra/main/README.md, infra/main/main.tf, and infra/main/variables.tf.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the deployment fixes and generalization of the infrastructure configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gary

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@humbeatbox
humbeatbox merged commit f37c777 into main Aug 1, 2026
12 of 13 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
.github/workflows/ci.yml (1)

151-151: 🔒 Security & Privacy | 🔵 Trivial

Protect the configurable SSH target.

vars.SSH_HOST now controls where appleboy/ssh-action@v1 executes sudo /opt/foundit/redeploy.sh. A changed value can send deployment commands to the wrong machine.

Store the target and expected host fingerprint in a protected deployment environment. Confirm strict host-key verification for the selected action version before enabling fork deployments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 151, Update the deployment job using
appleboy/ssh-action@v1 to source the SSH target and expected host fingerprint
from a protected deployment environment rather than an unrestricted
vars.SSH_HOST value. Configure strict host-key verification with the action’s
supported fingerprint option, and ensure fork-triggered deployments remain
disabled until that protected configuration is enforced.
infra/main/backend.tf (1)

10-11: 🩺 Stability & Availability | 🔵 Trivial

Enable state locking for the S3 backend.

Neither this backend block nor infra/main/backend.hcl.example enables use_lockfile. HashiCorp documents this option as disabled by default. Concurrent local and CI applies can update the same state without coordination. Enable locking for the declared Terraform version, grant exact .tflock permissions, and verify that the bootstrap bucket has versioning and encryption enabled. (developer.hashicorp.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/main/backend.tf` around lines 10 - 11, Update the S3 backend
configuration and backend.hcl.example to enable use_lockfile for the declared
Terraform version. Grant the backend role exact read/write/delete permissions
for the .tflock object, and verify the bootstrap bucket has versioning and
encryption enabled.

Source: MCP tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Line 162: Update the workflow step containing the curl health check to pass
API_HEALTH_URL through the step’s env configuration, then reference the quoted
shell variable in the curl command instead of interpolating the GitHub
expression in the script. Preserve the existing curl options and conditional
behavior.

In `@infra/bootstrap/policies/foundit-bootstrap-policy.json`:
- Line 8: Scope the S3 statement in
infra/bootstrap/policies/foundit-bootstrap-policy.json at line 8 to the intended
state-bucket ARN instead of a global resource. Review
infra/bootstrap/policies/foundit-deploy-policy.json lines 8-14 for the
corresponding S3 scope and align it if it grants the same access; update
infra/bootstrap/README.md at lines 15-21, 33-35, and 56-58 so its permissions
and “exactly needed” claims accurately describe the resulting policy.

In `@infra/main/backend.tf`:
- Around line 1-3: Update the deploy policy referenced by FounditDeployPolicy so
its S3 permissions no longer use wildcard resources: scope s3:ListBucket to the
bootstrap state bucket ARN, and scope s3:GetObject, s3:PutObject, and
s3:DeleteObject to the configured Terraform state-key and lock-file ARNs,
retaining delete access only for the lock object.

In `@infra/main/README.md`:
- Around line 42-46: Update the README instructions for
github_deploy_key_private to show a parseable HCL heredoc value in
terraform.tfvars, including the private key content between heredoc delimiters.
Keep the existing read-only deploy-key guidance and warning against using a
personal SSH key.

In `@infra/main/variables.tf`:
- Around line 14-18: Update the project_name variable validation to reject
values that produce invalid Lightsail resource names when used with the existing
instance, key pair, and static IP suffixes, while preserving the current
default. Extend the variable description to state that project_name must be
unique within the shared AWS account and region.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 151: Update the deployment job using appleboy/ssh-action@v1 to source the
SSH target and expected host fingerprint from a protected deployment environment
rather than an unrestricted vars.SSH_HOST value. Configure strict host-key
verification with the action’s supported fingerprint option, and ensure
fork-triggered deployments remain disabled until that protected configuration is
enforced.

In `@infra/main/backend.tf`:
- Around line 10-11: Update the S3 backend configuration and backend.hcl.example
to enable use_lockfile for the declared Terraform version. Grant the backend
role exact read/write/delete permissions for the .tflock object, and verify the
bootstrap bucket has versioning and encryption enabled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 786480c6-67aa-4a69-af37-54cbd315c669

📥 Commits

Reviewing files that changed from the base of the PR and between 13d39c4 and e9c0196.

📒 Files selected for processing (13)
  • .github/workflows/ci.yml
  • .gitignore
  • infra/bootstrap/README.md
  • infra/bootstrap/policies/foundit-bootstrap-policy.json
  • infra/bootstrap/policies/foundit-deploy-policy.json
  • infra/bootstrap/terraform.tfvars.example
  • infra/bootstrap/variables.tf
  • infra/main/README.md
  • infra/main/backend.hcl.example
  • infra/main/backend.tf
  • infra/main/main.tf
  • infra/main/terraform.tfvars.example
  • infra/main/variables.tf

Comment thread .github/workflows/ci.yml
run: |
for attempt in 1 2 3 4 5 6; do
if curl -fsS --max-time 10 https://foundit-api.garychang1214.com/api/health; then
if curl -fsS --max-time 10 "${{ vars.API_HEALTH_URL }}"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '130,175p' .github/workflows/ci.yml || true

echo
echo "== security context references =="
rg -n "permissions:|id-token:|write|API_HEALTH_URL|vars\.|contents:" .github/workflows/ci.yml .github/workflows || true

echo
echo "== deterministic bash probe for interpolation vs env variable =="
python3 - <<'PY'
import subprocess

bad = 'API_HEALTH_URL="http://example.com"; x="x"; if curl -fsS --max-time 10 "${API_HEALTH_URL}"; then echo first; echo second; fi ;; >/tmp/bad_interpolation.log 2>&1 ; echo "bad_interpolation: $?"; cat /tmp/bad_interpolation.log'

# The intended pattern cannot be directly simulated in a shell one-liner,
# so parse it:
good = 'API_HEALTH_URL="http://example.com"; curl -fsS --max-time 10 -- "$API_HEALTH_URL" ;; >/tmp/good_env.log 2>&1 ; echo "good_env: $?"; cat /tmp/good_env.log'

for name, cmd in [("imagine_interpolated_bad", bad), ("intended", good)]:
    try:
        out = subprocess.run(["bash", "-c", "set +e; " + cmd], text=True, capture_output=True, timeout=10)
        print(f">{name} exit={out.returncode}")
        print(out.stdout.strip())
        if out.stderr.strip():
            print(out.stderr.strip())
    except Exception as e:
        print(f">{name} exception={e}")
PY

Repository: 86unj/Foundit

Length of output: 2683


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== deterministic bash command injection probe =="
python3 - <<'PY'
import subprocess

payload = '"; echo INJECTED; echo SUCCESS;; # '

cases = {
    "curl_with_curly_var_substitution": 'API_HEALTH_URL="http://example.com"; if bash -c "curl -fsS --max-time 10 \'"${API_HEALTH_URL}"\'"; then echo first; echo second; fi ;;',
    "curl_with_quoted_envvar": 'API_HEALTH_URL="http://example.com"; curl -fsS --max-time 10 -- "$API_HEALTH_URL" ;;',
    "bad_escaping_newline_payload": 'API_HEALTH_URL="http://example.com"; if curl -fsS --max-time 10 "${API_HEALTH_URL}' + payload + '"; then echo first; echo second; fi ;;',
}

for name, cmd in cases.items():
    print(f"\n== {name} ==")
    # Use file descriptor passing to avoid shell quoting in the probe construction.
    fd = 3
    out = subprocess.run(["bash", "-c", cmd], text=True, capture_output=True, timeout=10)
    print(f"exit={out.returncode}")
    print("stdout:", repr(out.stdout))
    print("stderr:", repr(out.stderr))
PY

echo
echo "== workflow top-level permissions =="
sed -n '1,40p' .github/workflows/ci.yml || true

Repository: 86unj/Foundit

Length of output: 1849


🌐 Web query:

GitHub Actions run context shell context template variables inject run script before execution documentation

💡 Result:

In GitHub Actions, interpolating context variables directly into a 'run' step using the '${{ }}' syntax is a critical security risk known as shell injection [1][2][3]. When you use the '${{ }}' expression syntax inside a 'run' block, the GitHub Actions template engine evaluates the expression and performs a literal text substitution before the script is passed to the runner's shell [1][3][4]. If the context variable contains untrusted input—such as an issue title, pull request description, or branch name—an attacker can craft malicious input that breaks out of the intended command context to execute arbitrary code on the runner [1][2][4]. To safely handle untrusted input or context variables in shell scripts, you must use intermediate environment variables [5][2][4]. Recommended Pattern: 1. Define an environment variable within the 'env' block of the specific step [5][2][4]. 2. Assign the context expression to that environment variable [5][4]. 3. Reference the environment variable in your 'run' script using standard shell syntax (e.g., '$VAR_NAME') [5][2][4]. Example of the safe pattern: - name: Print PR title safely env: # Assign the untrusted context to an environment variable PR_TITLE: ${{ github.event.pull_request.title }} run: | # Reference as a shell variable; the shell treats this as data, not code echo "$PR_TITLE" By moving the value into an environment variable, the runner receives the content as data rather than executable script, which mitigates the risk of shell metacharacter interpretation [2][3][4]. This approach applies to 'run' commands and also when passing inputs to 'actions/github-script' or similar tools [6][4][7]. While self-hosted runners allow for custom pre-job or setup scripts on the host machine to enforce security policies globally [8], defining step-level environment variables remains the standard and most effective practice for preventing injection vulnerabilities within workflow YAML files [2][4].

Citations:


🌐 Web query:

Appleboy ssh-action permissions needed secrets LIGHTSAIL_SSH_KEY

💡 Result:

To use AWS Lightsail SSH keys with appleboy/ssh-action, you do not need special file system permissions, but you must correctly configure the SSH key content within GitHub Secrets and ensure your Lightsail instance is properly prepared [1][2][3]. Key Requirements and Configuration: 1. GitHub Secrets Setup: Store your private SSH key in a GitHub Repository Secret (e.g., named LIGHTSAIL_SSH_KEY) [3][4]. The content should be the raw private key text [1][5]. Ensure there are no leading or trailing spaces in the secret value, as these can cause authentication failures [6]. 2. Lightsail Configuration: The Lightsail instance must have the corresponding public key authorized [7]. - If you are using the default key downloaded from AWS Lightsail, ensure it is correctly formatted [3]. - If you are generating a new key pair, it is recommended to use ed25519 for better compatibility and security [2][5]. - Ensure the public key is added to the ~/.ssh/authorized_keys file on the remote Lightsail instance [2][7]. - Standard file permissions on the target server for SSH are: -.ssh directory: 700 [8][5] - authorized_keys file: 640 or 600 [8][5] 3. Action Configuration: In your GitHub Actions workflow YAML file, map the secret to the key input [1][4]: - name: Deploy to Lightsail uses: appleboy/ssh-action@v1 with: host: ${{ secrets.LIGHTSAIL_IP }} username: ${{ secrets.LIGHTSAIL_USER }} key: ${{ secrets.LIGHTSAIL_SSH_KEY }} port: 22 Troubleshooting Tips: - If authentication fails, verify that you are using the correct static IP address for your Lightsail instance rather than a hostname [9]. - Test the SSH connection from your local machine using the same private key before attempting to use it in GitHub Actions to confirm the key pair is valid [7]. - If the connection times out, ensure the instance's firewall allows traffic on port 22 from the GitHub Actions runner IP range (or generally, if public access is allowed) [9].

Citations:


🌐 Web query:

appleboy ssh-action v1 key input private key SSH private key

💡 Result:

To use an SSH private key with the appleboy/ssh-action (v1), you provide the raw content of your private key directly to the key input parameter [1][2]. ### Implementation Steps 1. Prepare your private key: Copy the entire contents of your private key file (e.g., ~/.ssh/id_rsa), ensuring you include the header and footer lines, such as -----BEGIN OPENSSH PRIVATE KEY----- and -----END OPENSSH PRIVATE KEY----- [2][3]. 2. Store as a GitHub Secret: Go to your repository settings on GitHub, navigate to Secrets and variables > Actions, and add your private key as a new repository secret (e.g., named SSH_PRIVATE_KEY) [1][4]. 3. Configure your workflow: Use the key parameter in your workflow YAML file to reference the secret [1][2]. Example configuration: - name: Execute remote SSH commands using SSH key uses: appleboy/ssh-action@v1 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} port: ${{ secrets.PORT }} script: whoami ### Important Considerations * Passphrase: If your private key is protected by a passphrase, you must also provide the passphrase input parameter [2][5]. - name: Execute with SSH key and passphrase uses: appleboy/ssh-action@v1 with: key: ${{ secrets.SSH_PRIVATE_KEY }} passphrase: ${{ secrets.PASSPHRASE }} #... other parameters * Security: Never commit your private key directly to your repository [2][3]. Always use GitHub Secrets to keep the key encrypted and secure [1][4]. * Troubleshooting: If you encounter authentication failures, ensure that the entire key content, including the start and end markers, is copied correctly without extra quotes or leading/trailing whitespace in the GitHub Secret value [4].

Citations:


Do not interpolate API_HEALTH_URL into the deploy runner.

GitHub substitutes ${{ vars.API_HEALTH_URL }} into the run script before Bash parses it, so quotes or newlines in the repository variable can break command syntax and inject code. Use the suggested env pattern and quote the shell variable.

🧰 Tools
🪛 zizmor (1.28.0)

[info] 162-162: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 162, Update the workflow step containing
the curl health check to pass API_HEALTH_URL through the step’s env
configuration, then reference the quoted shell variable in the curl command
instead of interpolating the GitHub expression in the script. Preserve the
existing curl options and conditional behavior.

Source: Linters/SAST tools

"arn:aws:s3:::foundit-tfstate-688948287774",
"arn:aws:s3:::foundit-tfstate-688948287774/*"
]
"Resource": "*"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

for policy in \
  infra/bootstrap/policies/foundit-bootstrap-policy.json \
  infra/bootstrap/policies/foundit-deploy-policy.json
do
  jq -e '
    def actions:
      .Action | if type == "array" then . else [.] end;

    all(.Statement[];
      if ((actions | any(startswith("s3:"))) and .Resource == "*") then
        (actions | all(. == "s3:CreateBucket"))
      else
        true
      end
    )
  ' "$policy" >/dev/null
done

Repository: 86unj/Foundit

Length of output: 151


🏁 Script executed:

#!/usr/bin/env/bash
set -euo pipefail

echo "Files:"
git ls-files 'infra/bootstrap/policies/*.json' 'infra/bootstrap/README.md' || true

echo
echo "Bootstrap policy:"
cat -n infra/bootstrap/policies/foundit-bootstrap-policy.json

echo
echo "Deploy policy:"
cat -n infra/bootstrap/policies/foundit-deploy-policy.json

echo
echo "README relevant lines:"
sed -n '1,70p' infra/bootstrap/README.md | cat -n

echo
echo "Broad S3 resource/statements probe:"
python3 - <<'PY'
import json, pathlib
for p in pathlib.Path('infra/bootstrap/policies').glob('*.json'):
    data=json.loads(p.read_text())
    print(f'--- {p} ---')
    for i,st in enumerate(data.get('Statement') or [], 1):
        action=st.get('Action')
        resource=st.get('Resource')
        print(f'Statement {i}: Effect={st.get("Effect")}, Action={action!r}, Resource={resource!r}')
PY

Repository: 86unj/Foundit

Length of output: 6782


Scope the bootstrap S3 policy to the state bucket and update the README.

infra/bootstrap/policies/foundit-bootstrap-policy.json grants s3:* on *, and the README says this gives the deploy user exactly the S3 permissions needed to create the state bucket. Scope the S3 statement to the intended bucket ARN, or clearly separate the unavoidable wildcard actions from the “exactly needed” claim.

📍 Affects 3 files
  • infra/bootstrap/policies/foundit-bootstrap-policy.json#L8-L8 (this comment)
  • infra/bootstrap/policies/foundit-deploy-policy.json#L8-L14
  • infra/bootstrap/README.md#L15-L21
  • infra/bootstrap/README.md#L33-L35
  • infra/bootstrap/README.md#L56-L58
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/bootstrap/policies/foundit-bootstrap-policy.json` at line 8, Scope the
S3 statement in infra/bootstrap/policies/foundit-bootstrap-policy.json at line 8
to the intended state-bucket ARN instead of a global resource. Review
infra/bootstrap/policies/foundit-deploy-policy.json lines 8-14 for the
corresponding S3 scope and align it if it grants the same access; update
infra/bootstrap/README.md at lines 15-21, 33-35, and 56-58 so its permissions
and “exactly needed” claims accurately describe the resulting policy.

Comment thread infra/main/backend.tf
Comment on lines +1 to +3
# Remote state lives in the bucket created by ../bootstrap. The deploy IAM
# user (via FounditGroup -> FounditDeployPolicy) already has read/write on
# every object in this bucket, so no IAM change is needed for this state key.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Tracked files matching relevant names:\n'
git ls-files | rg '(^|/)(backend\.hcl|backend\.tf|foundit-deploy-policy\.json)$|policies/' || true

printf '\n--- infra/main/backend.tf ---\n'
if [ -f infra/main/backend.tf ]; then
  cat -n infra/main/backend.tf
fi

printf '\n--- infra/bootstrap/policies/foundit-deploy-policy.json ---\n'
if [ -f infra/bootstrap/policies/foundit-deploy-policy.json ]; then
  cat -n infra/bootstrap/policies/foundit-deploy-policy.json
fi

printf '\n--- Search for foundit deploy policy and deploy user/policy references ---\n'
rg -n "FounditDeployPolicy|foundit-deploy-policy|s3:|Resource":.*\*|Backend|terraform backend|backend" . || true

printf '\n--- Policy bucket objects extraction ---\n'
python3 - <<'PY'
import json, pathlib, re
p=pathlib.Path("infra/bootstrap/policies/foundit-deploy-policy.json")
if p.exists():
    data=json.loads(p.read_text())
    policy=data.get("Policy") if isinstance(data, dict) and "Policy" in data else data
    print(json.dumps(policy, indent=2, sort_keys=True))
PY

Repository: 86unj/Foundit

Length of output: 1921


🌐 Web query:

HashiCorp Terraform S3 backend required permissions bucket actions object actions developer.hashicorp.com

💡 Result:

The AWS IAM permissions required for the Terraform S3 backend depend on your specific configuration, such as whether you use workspaces or the lockfile feature [1][2]. When not using workspaces (or using only the default workspace), the minimum required permissions are [1][2]: - s3:ListBucket: Must be granted on the bucket (e.g., arn:aws:s3:::mybucket) to list the path where the state is stored [1][2]. - s3:GetObject: Must be granted on the state file path (e.g., arn:aws:s3:::mybucket/path/to/my/key) [1][2]. - s3:PutObject: Must be granted on the state file path [1][2]. Note that s3:DeleteObject is generally not required for the state file itself because Terraform does not delete the state file [1][2]. However, if the use_lockfile configuration option is enabled, s3:DeleteObject is required to manage the lock file (e.g., arn:aws:s3:::mybucket/path/to/my/key.tflock) [1][2][3]. When using use_lockfile, s3:GetObject and s3:PutObject permissions are also required for the lock file [1][2]. When using workspaces, Terraform requires additional permissions to perform operations on workspace-specific state files [1][2]: - s3:ListBucket: Must be able to list the paths for both the default workspace and other workspaces [1][2]. - s3:GetObject, s3:PutObject, s3:DeleteObject: Must be granted on both the default state path and the workspace-specific paths (e.g., arn:aws:s3:::mybucket/<workspace_key_prefix>/*/path/to/my/key) [1][2]. If you are using DynamoDB for state locking, separate IAM permissions are required for the DynamoDB table, typically including actions such as dynamodb:GetItem, dynamodb:PutItem, and dynamodb:DeleteItem [4]. If your S3 bucket uses server-side encryption with KMS, you must also grant the necessary kms:Decrypt and kms:GenerateDataKey permissions to the IAM principal [5].

Citations:


Scope S3 permissions to the configured state bucket.

The deploy policy grants s3:ListBucket, s3:GetObject, s3:PutObject, and s3:DeleteObject with "Resource": "*", which gives the deploy identity access to all S3 buckets and objects it can use. The Terraform S3 backend only requires bucket-specific permissions for s3:ListBucket, object-specific permissions for the state key, and s3:DeleteObject only for the lock file when locking is enabled. Scope these actions to the bootstrap state bucket ARN for list access, and to the configured state and lock key ARNs for object access.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/main/backend.tf` around lines 1 - 3, Update the deploy policy
referenced by FounditDeployPolicy so its S3 permissions no longer use wildcard
resources: scope s3:ListBucket to the bootstrap state bucket ARN, and scope
s3:GetObject, s3:PutObject, and s3:DeleteObject to the configured Terraform
state-key and lock-file ARNs, retaining delete access only for the lock object.

Source: MCP tools

Comment thread infra/main/README.md
Comment on lines +42 to +46
Add `foundit-deploy-key.pub` to your fork under **Settings → Deploy keys
Add deploy key** (leave "Allow write access" unchecked — read-only).
Put the contents of `foundit-deploy-key` (the private half) into
`terraform.tfvars` as `github_deploy_key_private`. Don't reuse your
personal SSH key for this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
rg -n -C 3 'github_deploy_key_private|<<-' infra/main/README.md

Repository: 86unj/Foundit

Length of output: 568


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== tfvars examples / key variable references =="
rg -n -C 2 'github_deploy_key_private|terraform\.tfvars|decrypt|private|private_key' infra -g '!*~*' || true

echo
echo "== relevant infra files =="
find infra -maxdepth 3 -type f | sed 's#^\./##' | sort | head -200

echo
echo "== README surrounding lines =="
sed -n '35,55p' infra/main/README.md

Repository: 86unj/Foundit

Length of output: 10474


🌐 Web query:

Terraform HCL heredoc strings multiline with triple-hyphen and end label

💡 Result:

In Terraform HCL, heredoc strings provide a way to express multiline strings [1][2]. They are defined by an opening marker followed by a user-chosen identifier, and they are terminated when that same identifier appears alone on a line [1][3]. The standard heredoc is introduced by the << sequence [1][4]. In this mode, every character, including leading spaces, is treated as a literal part of the string [1][4]. The indented heredoc is introduced by the <<- sequence [1][4]. This variant is designed to improve code readability by allowing the heredoc block to be indented within the HCL code [1][2]. When using this syntax, Terraform analyzes the content lines to determine the smallest number of leading spaces and automatically strips that many spaces from the beginning of every line in the string [1][5]. Key rules for termination and syntax: - Delimiter: You choose an identifier (e.g., EOT, EOF) which must be used as both the opening reference and the closing line [1][4]. - Closing line: The identifier must appear alone on its own line [1][4]. For the <<- variant, the closing identifier may have arbitrary leading spaces (indentation) [3]. - Trailing spaces: The closing line must not contain trailing spaces after the identifier, or the heredoc will fail to terminate [6]. - Indentation stripping: Only the <<- syntax performs indentation stripping [1][5]. Standard << does not [1][4]. It is important to note that while template interpolations (e.g., ${var}) and directives (e.g., %{for...}) work within heredoc strings, complex interactions between indentation stripping and template directives can sometimes result in unexpected whitespace in the final output [7][5].

Citations:


🌐 Web query:

Terraform HCL strings escape newline \n inside double quotes

💡 Result:

In Terraform HCL, you can represent a newline character inside a double-quoted string by using the escape sequence \n [1][2]. Within double-quoted strings, the backslash character () initiates an escape sequence [1][3]. The following escape sequences are supported for quoted strings [1][4]: \n: Newline [1][2] \r: Carriage return [1][2] \t: Tab [1][2] ": Literal double quote [1][2] \: Literal backslash [1][2] \uNNNN: Unicode character (4 hex digits) [1][2] \UNNNNNNNN: Unicode character (8 hex digits) [1][2] Alternatively, Terraform supports a "heredoc" syntax (e.g., <<EOT... EOT), which allows you to define multi-line strings without needing to escape newlines with backslashes [1][5]. In heredoc strings, backslash sequences are interpreted literally rather than as escape sequences [1][4].

Citations:


Show a parseable HCL value for github_deploy_key_private.

The README tells users to paste a multiline private key directly into terraform.tfvars, but a quoted HCL string requires escaped newlines. Use a heredoc example instead:

Use an HCL heredoc
-   Put the contents of `foundit-deploy-key` (the private half) into `terraform.tfvars` as `github_deploy_key_private`.
+   Put the contents of `foundit-deploy-key` into `terraform.tfvars` with an HCL heredoc:
+
+   github_deploy_key_private = <<-KEY
+   <contents of foundit-deploy-key>
+   KEY
🧰 Tools
🪛 LanguageTool

[uncategorized] ~45-~45: The official name of this software platform is spelled with a capital “H”.
Context: ...ate half) into terraform.tfvars as github_deploy_key_private. Don't reuse your ...

(GITHUB)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/main/README.md` around lines 42 - 46, Update the README instructions
for github_deploy_key_private to show a parseable HCL heredoc value in
terraform.tfvars, including the private key content between heredoc delimiters.
Keep the existing read-only deploy-key guidance and warning against using a
personal SSH key.

Comment thread infra/main/variables.tf
Comment on lines +14 to +18
variable "project_name" {
description = "Short name prefixed onto Lightsail resource names, e.g. \"<project_name>-app\"."
type = string
default = "foundit"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '^infra/main/(variables\.tf|main\.tf)$' || true

echo "== variables.tf =="
if [ -f infra/main/variables.tf ]; then
  cat -n infra/main/variables.tf
fi

echo "== main.tf outline/sections =="
if [ -f infra/main/main.tf ]; then
  wc -l infra/main/main.tf
  cat -n infra/main/main.tf
fi

echo "== project_name usages =="
rg -n '\bproject_name\b|aws_lightsail|StaticIp|PublicKey' infra/main || true

Repository: 86unj/Foundit

Length of output: 11966


🌐 Web query:

AWS Lightsail instance name valid characters length key pair name valid characters name static IP name valid characters length

💡 Result:

In Amazon Lightsail, resources such as instances, key pairs, and static IPs generally follow consistent naming requirements. For Amazon Lightsail resource names (including instances and static IPs), the following constraints apply [1][2]: Length: Must be between 2 and 255 characters [1][2]. Characters: Can include alphanumeric characters, numbers, periods (.), dashes (-), and underscores (_) [1][2]. Formatting: Must start and end with an alphanumeric character or number [1][2]. Uniqueness: Must be unique within each AWS Region in your Lightsail account [1][2]. Technical API specifications for these resources often cite the pattern \w[\w\-]*\w [3][4][5], which aligns with these requirements (where \w typically encompasses alphanumeric characters and underscores, often including dashes depending on the implementation context). While some technical documentation may show slight variations in pattern notation, the explicit guidance provided in the AWS user documentation confirms the 2-255 character limit and the specific allowed characters listed above [1][2]. Key pair names are also typically subject to these naming conventions, as they are managed as named resources within the Lightsail environment [4][6]. Top results: [1][2][3][4]

Citations:


Validate project_name before interpolating it into Lightsail names.

project_name is interpolated into instance, key pair, and static IP names without checks. Add validation so Terraform rejects names that would violate Lightsail naming rules after concatenation. Also document that each project_name must be unique in the shared AWS account and region.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/main/variables.tf` around lines 14 - 18, Update the project_name
variable validation to reject values that produce invalid Lightsail resource
names when used with the existing instance, key pair, and static IP suffixes,
while preserving the current default. Extend the variable description to state
that project_name must be unique within the shared AWS account and region.

Source: MCP tools

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