Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 21 additions & 44 deletions .github/RELEASE_NOTES_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,41 @@

### Highlights

- Added `--max-removals` and `--max-removal-percent` blast-radius ceilings across NQE sync, manifest sync, saved-plan apply, preflight, and webhook workflows.
- Added release installation, checksum, provenance verification, automation audit handling, and explicit External ID rollback guidance.
- Fixed the release checksum manifest so downloaded assets verify directly with `sha256sum -c sha256sums.txt`.
- Added reversible one-time External ID migration for existing AWS setups with `external-id --value` and `external-id --clear`.
- Added AWS GovCloud workflows for both regular Forward Organizations/NQE discovery and reviewed standalone-account manifests.
- Added `onboard-accounts` and `sync-accounts` for environments where AWS Organizations is unavailable by policy.
- Preserved `arn:aws-us-gov` IAM role partitions and rejected mixed or region-mismatched role ARNs.
- Blocked GovCloud removals without positive Organizations evidence; authoritative manifest removals require explicit review and `--allow-removals`.
- Added collector instance-profile onboarding payloads for self-managed GovCloud collectors.
- Hardened `apply-plan` so saved payloads cannot bypass current-state or GovCloud removal validation.
- Added a dedicated GovCloud operator guide with product-enhancement escalation criteria.
- Generated payload, manual, and audit files are now atomically replaced with owner-only `0600` permissions, including outputs that may contain static AWS credentials.
- Forward API reads, NQE queries, and full-state PATCH operations retry bounded transient `429`, `502`, `503`, and `504` responses. Non-idempotent create POSTs remain single-attempt.
- `awssync --version` now reports the release, source commit, and build date.
- CI now runs formatting, vet, tests, the race detector, and `govulncheck` with read-only repository permissions and commit-pinned actions.
- Release jobs use least-privilege permissions and continue to publish checksums and build-provenance attestations.
- The README now starts with the workflow decision diagram and routes detailed operator procedures to focused runbooks.
- Contribution guidance requires human attribution and excludes automation/tool identities from contributor metadata.

### Download and verify

Assets include platform binaries, tarballs, checksums, and release attestations:

- `awssync-linux-amd64`
- `awssync-linux-arm64`
- `awssync-darwin-amd64`
- `awssync-darwin-arm64`
- `awssync-linux-amd64.tar.gz`
- `awssync-linux-arm64.tar.gz`
- `awssync-darwin-amd64.tar.gz`
- `awssync-darwin-arm64.tar.gz`
- `sha256sums.txt`

### Quick usage
Assets include native Linux and macOS binaries for amd64 and arm64, tar archives, `sha256sums.txt`, and GitHub build-provenance attestations.

```bash
# Add a customer-defined External ID to an existing setup
./awssync external-id \
--network-id <NETWORK_ID> \
--setup-id <SETUP_ID> \
--value <CUSTOMER_VALUE> \
--output aws_external_id_payload.json
tar -xzf awssync-linux-amd64.tar.gz
sha256sum -c sha256sums.txt --ignore-missing
gh attestation verify awssync-linux-amd64 \
--repo forwardnetworks/aws-sync
./awssync-linux-amd64 --version
```

# Generate a GovCloud onboarding payload from a reviewed manifest
./awssync onboard-accounts \
--accounts-file govcloud-accounts.json \
--partition aws-us-gov \
--credential-mode instance-profile \
--setup-id <SETUP_ID> \
--role-name ForwardReadOnlyAccess \
--collect-region us-gov-west-1
### Start safely

# Dry-run an existing setup against an authoritative manifest
./awssync sync-accounts \
```bash
./awssync-linux-amd64 preflight \
--network-id <NETWORK_ID> \
--setup-id <SETUP_ID> \
--accounts-file govcloud-accounts.json \
--max-snapshot-age 24h \
--format human

# Verify the regular Organizations/NQE path before applying
./awssync preflight \
./awssync-linux-amd64 \
--network-id <NETWORK_ID> \
--setup-id <SETUP_ID> \
--max-snapshot-age 24h \
--output aws_sync_payload.json \
--format human
```

See `docs/govcloud-workflow.md` for the complete GovCloud Organizations and standalone-account procedures.
See the README workflow diagram, `docs/aws-account-sync-procedure.md`, and `docs/govcloud-workflow.md` before enabling apply automation or account removals.
26 changes: 20 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,34 @@ on:
branches:
- main

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: go.mod
cache: true
- name: Check formatting
run: test -z "$(gofmt -l ./cmd ./internal)"
run: make fmt-check
- name: Vet
run: go vet ./...
run: make vet
- name: Test
run: go test ./...
run: make test
- name: Race detector
run: make race
- name: Vulnerability scan
run: make vuln
- name: Build
run: go build -o bin/awssync ./cmd/awssync
run: make build
58 changes: 40 additions & 18 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
name: release
permissions:
contents: write
id-token: write
attestations: write

on:
workflow_dispatch:
push:
tags:
- "v*"

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: go.mod
cache: true
- name: Check formatting
run: test -z "$(gofmt -l ./cmd ./internal)"
run: make fmt-check
- name: Vet
run: go vet ./...
run: make vet
- name: Test
run: go test ./...
run: make test
- name: Race detector
run: make race
- name: Vulnerability scan
run: make vuln

build:
runs-on: ubuntu-latest
timeout-minutes: 15
needs: test
strategy:
matrix:
Expand All @@ -41,8 +48,10 @@ jobs:
- goos: darwin
goarch: arm64
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version-file: go.mod
cache: true
Expand All @@ -51,22 +60,35 @@ jobs:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
CGO_ENABLED: "0"
VERSION: ${{ github.ref_name }}
COMMIT: ${{ github.sha }}
run: |
mkdir -p dist
go build -trimpath -o "dist/awssync-${GOOS}-${GOARCH}" ./cmd/awssync
- uses: actions/upload-artifact@v7
BUILD_DATE="$(git show -s --format=%cI "$GITHUB_SHA")"
make build \
BINARY="dist/awssync-${GOOS}-${GOARCH}" \
VERSION="${VERSION}" \
COMMIT="${COMMIT}" \
BUILD_DATE="${BUILD_DATE}"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: awssync-${{ matrix.goos }}-${{ matrix.goarch }}
path: dist/awssync-${{ matrix.goos }}-${{ matrix.goarch }}

publish:
runs-on: ubuntu-latest
timeout-minutes: 15
needs: build
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Download all build artifacts
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
path: dist
merge-multiple: true
Expand Down Expand Up @@ -103,7 +125,7 @@ jobs:
sha256sum -c sha256sums.txt
)
- name: Generate build provenance
uses: actions/attest-build-provenance@v4
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4
with:
subject-path: |
dist/awssync-linux-amd64
Expand All @@ -118,7 +140,7 @@ jobs:
run: |
sed "s|{{VERSION}}|${{ github.ref_name }}|g" .github/RELEASE_NOTES_TEMPLATE.md > dist/release-notes.md
- name: Publish GitHub release
uses: softprops/action-gh-release@v3
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
Expand Down
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Contributing

## Changes

Open a focused pull request with tests and documentation for operator-visible behavior. Run the same checks used by CI before publishing:

```bash
make ci
```

Keep destructive behavior opt-in, preserve dry-run output, and add regression tests for safety checks. Never include customer credentials, tenant data, generated payloads, or private communications in commits, issues, test fixtures, or workflow logs.

## Attribution

Commits and pull requests must identify the human authors responsible for the change. Automation and generative tools are tools, not contributors: do not add tool identities through `Author`, `Co-authored-by`, contributor lists, acknowledgements, or similar attribution metadata.

Use a verified human email address for commits. Maintainers may ask for attribution metadata to be corrected before merge.
20 changes: 17 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
BINARY := bin/awssync
VERSION ?= dev
COMMIT ?= $(shell git rev-parse --short=12 HEAD 2>/dev/null || printf unknown)
BUILD_DATE ?= unknown
LDFLAGS := -X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(BUILD_DATE)

.PHONY: build test fmt vet ci
.PHONY: build test race fmt fmt-check vet vuln ci

build:
go build -o $(BINARY) ./cmd/awssync
mkdir -p $(dir $(BINARY))
go build -trimpath -buildvcs=false -ldflags "$(LDFLAGS)" -o $(BINARY) ./cmd/awssync

test:
go test ./...

race:
go test -race ./...

fmt:
gofmt -w ./cmd ./internal

fmt-check:
test -z "$$(gofmt -l ./cmd ./internal)"

vet:
go vet ./...

ci: fmt vet test build
vuln:
go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 ./...

ci: fmt-check vet test race vuln build
Loading