Documentation

Install with Helm

From nothing to a live subnet inventory in four steps. The chart comes from charts.hypersurgery.dev and the operator only reads your AWS accounts.

Before you start

You need:

The operator never writes to AWS. The inventory uses three Describe calls. Automatic subnet management is a separate, opt-in layer that is not part of this release.

Quick start

1

Add the chart repository

helm repo add hypersurgery https://charts.hypersurgery.dev
helm repo update hypersurgery
helm search repo hypersurgery/aws-subnet-operator
2

Give the operator AWS credentials

With EKS Pod Identity, associate your role with the service account that the release creates:

# after step 3, the service account is
# aws-subnet-operator-system/subnet-operator-aws-subnet-operator
aws eks create-pod-identity-association \
  --cluster-name my-cluster \
  --namespace aws-subnet-operator-system \
  --service-account subnet-operator-aws-subnet-operator \
  --role-arn arn:aws:iam::111111111111:role/aws-subnet-operator

With IRSA, annotate the service account instead (step 3 shows where). For other accounts, deploy the read-only role from deploy/iam/spoke-readonly-role.cfn.yaml with a CloudFormation StackSet.

3

Install the chart

The image lives in a registry that needs a login, so create a pull secret first:

kubectl create namespace aws-subnet-operator-system
kubectl create secret docker-registry registry -n aws-subnet-operator-system \
  --docker-server=registry.hypersurgery.dev \
  --docker-username=<user> --docker-password=<token>
helm install subnet-operator hypersurgery/aws-subnet-operator \
  --namespace aws-subnet-operator-system \
  --set imagePullSecrets[0].name=registry \
  --set aws.region=eu-central-1 \
  --set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::111111111111:role/aws-subnet-operator

The chart installs the CRDs, RBAC, the manager and a metrics service. Drop the serviceAccount.annotations line if you use Pod Identity.

4

Tell it what to discover

kubectl apply -f - <<'EOF'
apiVersion: aws.hypersurgery/v1alpha1
kind: NetworkScope
metadata:
  name: organization
spec:
  accounts:
    - id: "111111111111"              # the operator's own account
    - id: "222222222222"
      roleARN: arn:aws:iam::222222222222:role/aws-subnet-operator-readonly
  regions: [eu-central-1, eu-west-1]
  vpcTagSelector:
    hs/managed: "true"
  requiredSubnetTags: [hs/owner, hs/env, hs/tier]
  resyncInterval: 10m
EOF

Leave vpcTagSelector out to take every VPC in the listed regions.

Verify

kubectl get networkscopes
NAME           VPCS   SUBNETS   READY   LAST SYNC
organization   42     318       True    12s

kubectl get subnets -o wide
kubectl get vpcs -o wide
kubectl get subnets -l aws.hypersurgery/account=222222222222

If READY stays False, the scope status names the account and region that failed:

kubectl get networkscope organization -o jsonpath='{.status.targets}' | jq

Change events (optional)

Without events, changes appear at the next resync. With them, a changed account and region is re-read about ten seconds after the API call.

  1. Make sure CloudTrail records management events in every account, for example with an organization trail.
  2. Deploy deploy/events/hub-events.cfn.yaml in the operator's account, and deploy/events/spoke-events.cfn.yaml to every other account and region with a StackSet.
  3. Point the release at the queue:
helm upgrade subnet-operator hypersurgery/aws-subnet-operator \
  --namespace aws-subnet-operator-system --reuse-values \
  --set events.queueUrl=https://sqs.eu-central-1.amazonaws.com/111111111111/aws-subnet-operator-events

Dashboard and alerts

With kube-prometheus-stack in the cluster:

helm upgrade subnet-operator hypersurgery/aws-subnet-operator \
  --namespace aws-subnet-operator-system --reuse-values \
  --set metrics.serviceMonitor.enabled=true \
  --set metrics.serviceMonitor.labels.release=kube-prometheus-stack \
  --set prometheusRule.enabled=true \
  --set prometheusRule.labels.release=kube-prometheus-stack \
  --set grafanaDashboard.enabled=true

The dashboard ConfigMap is labelled grafana_dashboard: "1", which the Grafana sidecar imports on its own. You get headline counts, utilization by environment, free addresses per account, the fullest subnets, tag compliance, target health and the age of the last full sync — the same panels as the live demo.

The alerts are: a subnet at 85% or more, a subnet with no free addresses, an account that cannot be discovered, overlapping VPC CIDRs, and an inventory that has not fully resynced for an hour.

Google Sheet mirror (optional)

For people who still want a table. The operator writes the sheet and never reads it back.

# share the spreadsheet with the service account address first
kubectl create secret generic google-sheets -n aws-subnet-operator-system \
  --from-file=credentials.json=./service-account.json

kubectl apply -f - <<'EOF'
apiVersion: aws.hypersurgery/v1alpha1
kind: SheetExport
metadata:
  name: organization
spec:
  scopeRef: organization
  spreadsheetID: 1AbCdEfGhIjKlMnOpQrStUvWxYz0123456789
  credentialsSecretRef:
    name: google-sheets
    namespace: aws-subnet-operator-system
  refreshInterval: 5m
EOF

Only service account keys are accepted: other credential configurations can make the Google client run a local command, which a Secret should never be able to do.

Common values

ValueDefaultWhat it does
image.repositoryghcr.io/example/aws-subnet-operatorManager image; set it to your registry.
aws.regionemptyRegion for the operator's own calls.
discovery.concurrency4Account/region pairs discovered in parallel.
events.queueUrlemptySQS queue with EC2 change events.
metrics.securetrueHTTPS with authn/authz on the metrics endpoint.
metrics.serviceMonitor.enabledfalseServiceMonitor for the Prometheus operator.
prometheusRule.enabledfalseAlerting rules.
grafanaDashboard.enabledfalseDashboard ConfigMap for the Grafana sidecar.
networkScope.createfalseCreate a scope with the release instead of by hand.
resources500m / 256MiContainer resources.

Everything else: helm show values hypersurgery/aws-subnet-operator.

Upgrade and uninstall

helm repo update hypersurgery
helm upgrade subnet-operator hypersurgery/aws-subnet-operator \
  --namespace aws-subnet-operator-system --reuse-values
CRDs are not upgraded by Helm. When a release changes them, apply them yourself: helm pull hypersurgery/aws-subnet-operator --untar && kubectl apply -f aws-subnet-operator/crds/
helm uninstall subnet-operator -n aws-subnet-operator-system

Uninstalling removes the operator and, with it, the mirrored VPC and Subnet objects. Nothing happens to your AWS networks: they were never touched.

The dashboard app

hypersurgery.dev/dashboard/ is the same set of panels as an installable web app: open it in Chrome or Edge and use the install icon in the address bar, or the Install app button in its header; on iOS and macOS Safari use Share → Add to Home Screen or Add to Dock. It keeps working offline and carries four themes (terminal, cyberpunk, midnight and daylight).

The app shows demo data, so it is a way to see the shape of the inventory before you install anything. Your own numbers live in the Grafana dashboard the chart ships, and in kubectl get subnets.

Troubleshooting

The scope says Ready=False

Read status.targets: each account and region carries its own error. AccessDenied usually means the spoke role's trust policy does not list the operator's role, or the externalID does not match. The operator also refuses a roleARN whose account does not match the declared account ID, so a typo shows up here rather than as someone else's networks.

No subnets appear

The tag selector matches VPCs, not subnets: a VPC without hs/managed=true is skipped together with everything inside it. Check with aws ec2 describe-vpcs --filters Name=tag:hs/managed,Values=true.

Utilization looks stale

Free-address counts change with every ENI and are refreshed by the full resync, not by events. Lower resyncInterval if you need them fresher.

Metrics are not scraped

With metrics.secure=true Prometheus needs a token and must accept the self-signed serving certificate; the chart's ServiceMonitor sets both. Check the labels your Prometheus selects ServiceMonitors on.