{"id":47636264,"url":"https://github.com/fabidick22/flux2-ecr-webhook","last_synced_at":"2026-04-14T21:01:36.926Z","repository":{"id":161316836,"uuid":"634763227","full_name":"fabidick22/flux2-ecr-webhook","owner":"fabidick22","description":"K8s controller that automates Flux webhook triggers on container registry push events — deploy changes in seconds :rocket:.","archived":false,"fork":false,"pushed_at":"2026-03-26T15:09:11.000Z","size":169,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-02T23:45:41.579Z","etag":null,"topics":["aws-ecr","ecr","flux2","flux2-gitops-toolkit","gitops","k8s-cluster","webhook"],"latest_commit_sha":null,"homepage":"https://github.com/fluxcd/flux2/discussions/2925","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fabidick22.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-05-01T05:51:18.000Z","updated_at":"2026-03-31T21:04:46.000Z","dependencies_parsed_at":"2026-04-02T11:01:52.521Z","dependency_job_id":null,"html_url":"https://github.com/fabidick22/flux2-ecr-webhook","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/fabidick22/flux2-ecr-webhook","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabidick22%2Fflux2-ecr-webhook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabidick22%2Fflux2-ecr-webhook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabidick22%2Fflux2-ecr-webhook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabidick22%2Fflux2-ecr-webhook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabidick22","download_url":"https://codeload.github.com/fabidick22/flux2-ecr-webhook/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabidick22%2Fflux2-ecr-webhook/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31815080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T18:05:02.291Z","status":"ssl_error","status_checked_at":"2026-04-14T18:05:01.765Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws-ecr","ecr","flux2","flux2-gitops-toolkit","gitops","k8s-cluster","webhook"],"created_at":"2026-04-02T00:08:45.166Z","updated_at":"2026-04-14T21:01:36.921Z","avatar_url":"https://github.com/fabidick22.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flux2-ecr-webhook\n\nAutomates calling Flux webhooks ([Receivers](https://fluxcd.io/flux/components/notification/receiver/)) when container registry push events occur.\n\n\u003e Cloud-agnostic design ([CloudProvider interface](controller/internal/cloud/provider.go)). Currently implemented and tested on **AWS** (ECR + EventBridge + SQS + Lambda).\n\n## How it works\n\n```mermaid\ngraph LR\n  subgraph Kubernetes Cluster\n    C[Controller] --\u003e|watches| IR[ImageRepository]\n    C --\u003e|discovers| IP[ImagePolicy]\n    C --\u003e|discovers| R[Receiver]\n  end\n  C --\u003e|sync mapping| SS[Secret Store]\n  REG[Container Registry] --\u003e|push event| EV[Cloud Events]\n  EV --\u003e Q[Queue] --\u003e FN[Serverless Function]\n  FN --\u003e|reads mapping| SS\n  FN --\u003e|POST webhook| R\n```\n\n\u003e **AWS:** ECR → EventBridge → SQS → Lambda → SecretsManager\n\n1. The controller watches all `ImageRepository` resources in the cluster\n2. For each one, it cross-references `Receiver` and `ImagePolicy` resources\n3. It builds the `repo_mapping` structure automatically (webhook URLs, tokens, tag regex)\n4. The mapping is persisted to the cloud secret store for the serverless function to consume\n5. On a registry push event, the function reads the mapping and calls the matching Flux webhooks\n\n## Install\n\n```bash\nhelm install flux2-ecr-webhook ./helm/flux2-ecr-webhook \\\n  --namespace flux-system \\\n  --set flux.webhookBaseURL=https://flux.example.com \\\n  --set aws.region=us-east-1 \\\n  --set aws.irsaRoleArn=arn:aws:iam::123456789012:role/my-role\n```\n\n## Configuration\n\n| Value | Description | Default |\n|-------|-------------|---------|\n| `flux.webhookBaseURL` | Base URL of the Flux notification-controller (required) | `\"\"` |\n| `flux.namespace` | Namespace where Flux is installed | `flux-system` |\n| `scan.allNamespaces` | Scan all namespaces for ImageRepository resources | `true` |\n| `scan.excludeNamespaces` | Namespaces to skip | `[kube-system, kube-public, kube-node-lease]` |\n| `aws.region` | AWS region | `\"\"` |\n| `aws.irsaRoleArn` | IAM Role ARN for IRSA on EKS | `\"\"` |\n| `aws.manageInfrastructure` | Create and manage cloud resources automatically | `true` |\n| `aws.appName` | Prefix for cloud resource names (useful for isolation) | `flux2-ecr-webhook` |\n| `controller.resyncInterval` | Periodic resync interval | `5m` |\n| `excludeAnnotation` | Annotation to exclude a repo | `ecr-webhook.io/skip` |\n\n## Deployment Modes\n\n### Single Cluster (default)\n\nOne cluster, one set of cloud resources. No extra configuration needed.\n\n```bash\nhelm install flux2-ecr-webhook ./helm/flux2-ecr-webhook \\\n  --namespace flux-system \\\n  --set flux.webhookBaseURL=https://flux.example.com \\\n  --set aws.region=us-east-1 \\\n  --set aws.irsaRoleArn=arn:aws:iam::123456789012:role/my-role\n```\n\n### Multi-Cluster (shared cloud account)\n\nMultiple clusters sharing the same cloud account and resources. Each controller automatically identifies itself using the `webhookBaseURL` hostname — no extra configuration required.\n\n```mermaid\ngraph TB\n  subgraph STG Account\n    subgraph STG Cluster\n      C1[Controller STG]\n    end\n  end\n  subgraph PROD Account\n    subgraph PROD Cluster\n      C2[Controller PROD]\n    end\n  end\n  subgraph Shared Account\n    ECR --\u003e|push event| EB[EventBridge]\n    EB --\u003e SQS --\u003e Lambda\n    SS[SecretsManager\u003cbr/\u003emerged mapping]\n    Lambda --\u003e|reads| SS\n  end\n  C1 --\u003e|merge own entries| SS\n  C2 --\u003e|merge own entries| SS\n  Lambda --\u003e|regex match?| W1[STG Flux Receiver]\n  Lambda --\u003e|regex match?| W2[PROD Flux Receiver]\n```\n\n\u003e Example uses AWS terminology. The concept applies to any supported cloud provider (GCP projects, Azure subscriptions, etc.).\n\nEach controller uses a **read → merge → write** cycle so entries from other clusters are preserved. The mapping keys are automatically prefixed with the cluster identity:\n\n```json\n{\n  \"my-app\": {\n    \"flux.stg.example.com::my-app-receiver\": {\n      \"webhook\": [\"https://flux.stg.example.com/hook/abc123\"],\n      \"token\": \"stg-token\",\n      \"regex\": \"^stg-.*\"\n    },\n    \"flux.prod.example.com::my-app-receiver\": {\n      \"webhook\": [\"https://flux.prod.example.com/hook/xyz789\"],\n      \"token\": \"prod-token\",\n      \"regex\": \"^prod-.*\"\n    }\n  }\n}\n```\n\nInstall on each cluster — only `webhookBaseURL` and `irsaRoleArn` differ:\n\n```bash\n# STG cluster\nhelm install flux2-ecr-webhook ./helm/flux2-ecr-webhook \\\n  --namespace flux-system \\\n  --set flux.webhookBaseURL=https://flux.stg.example.com \\\n  --set aws.region=us-east-1 \\\n  --set aws.irsaRoleArn=arn:aws:iam::123456789012:role/stg-role\n\n# PROD cluster\nhelm install flux2-ecr-webhook ./helm/flux2-ecr-webhook \\\n  --namespace flux-system \\\n  --set flux.webhookBaseURL=https://flux.prod.example.com \\\n  --set aws.region=us-east-1 \\\n  --set aws.irsaRoleArn=arn:aws:iam::123456789012:role/prod-role\n```\n\nIf you ever reach the secret size limit (64 KB, unlikely for most setups), use `aws.appName` to create a separate set of cloud resources for additional clusters.\n\n### External Infrastructure\n\nSet `aws.manageInfrastructure=false` when you manage cloud resources externally (Terraform, CDK, etc.). The controller will only sync the mapping and event filters.\n\n```bash\nhelm install flux2-ecr-webhook ./helm/flux2-ecr-webhook \\\n  --namespace flux-system \\\n  --set flux.webhookBaseURL=https://flux.example.com \\\n  --set aws.region=us-east-1 \\\n  --set aws.manageInfrastructure=false \\\n  --set aws.irsaRoleArn=arn:aws:iam::123456789012:role/my-role\n```\n\n## AWS IAM Setup\n\nThe controller needs an IAM role with permissions to manage Lambda, SQS, EventBridge, SecretsManager, IAM, and CloudWatch Logs. See the full policy example at [`examples/aws/iam-policy.json`](examples/aws/iam-policy.json).\n\n\u003e Replace `\u003cREGION\u003e` and `\u003cACCOUNT_ID\u003e` with your values.\n\n### Option 1: Same Account\n\nCreate an IAM role in the same account where the EKS cluster runs. Attach the policy and configure an OIDC trust relationship for IRSA pointing to the cluster's service account (`flux-system:flux2-ecr-webhook`).\n\n### Option 2: Cross-Account (shared account)\n\nWhen the cluster runs in one account (e.g. STG) but the cloud resources live in a **shared account**:\n\n1. Create an [OIDC Identity Provider](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) in the **shared account** using the EKS OIDC issuer URL from the cluster account\n2. Create the IAM role in the **shared account** with the policy and an OIDC trust relationship pointing to the identity provider created in step 1\n3. Use the shared account role ARN in `aws.irsaRoleArn`\n\nThe pod assumes the role directly in the shared account via OIDC federation. Repeat for each cluster that needs access.\n\n## Exclude a Repository\n\nAdd the exclusion annotation to skip a specific ImageRepository:\n\n```yaml\napiVersion: image.toolkit.fluxcd.io/v1beta2\nkind: ImageRepository\nmetadata:\n  name: my-repo\n  annotations:\n    ecr-webhook.io/skip: \"true\"\n```\n\n## Cloud Provider Support\n\n| Provider | Status |\n|----------|--------|\n| AWS (ECR) | Implemented and tested |\n| GCP (Artifact Registry) | Planned |\n| Azure (ACR) | Planned |\n\n---\n\n## v1 — Terraform Module (maintenance)\n\nA Terraform module that configures an AWS Lambda to fire on ECR push events with manual `repo_mapping`.\n\n\u003e For v1 docs and usage, see the [`1.x` branch](https://github.com/fabidick22/flux2-ecr-webhook/tree/1.x).\n\n```hcl\nmodule \"flux2-ecr-webhook\" {\n  source = \"github.com/fabidick22/flux2-ecr-webhook?ref=v1.2.0\"\n\n  app_name = \"flux-ecr-webhook\"\n  repo_mapping = {\n    my-ecr-repo = {\n      prod = {\n        webhook = [\"https://domain.com/hook/1111111\"]\n        regex   = \"prod-(?P\u003cversion\u003e.*)\"\n      }\n    }\n  }\n  webhook_token = var.webhook_token\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabidick22%2Fflux2-ecr-webhook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabidick22%2Fflux2-ecr-webhook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabidick22%2Fflux2-ecr-webhook/lists"}