{"id":22458383,"url":"https://github.com/xenitab/azcagit","last_synced_at":"2025-08-02T03:33:02.554Z","repository":{"id":58065214,"uuid":"498027260","full_name":"XenitAB/azcagit","owner":"XenitAB","description":"GitOps Engine for Azure Container Apps","archived":false,"fork":false,"pushed_at":"2024-02-12T12:37:57.000Z","size":1110,"stargazers_count":53,"open_issues_count":17,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-06T02:34:19.448Z","etag":null,"topics":["azure","azure-container-apps","gitops","golang","terraform"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/XenitAB.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2022-05-30T16:54:07.000Z","updated_at":"2024-11-26T08:24:00.000Z","dependencies_parsed_at":"2024-11-19T11:01:01.961Z","dependency_job_id":"aa93c89d-5d92-4ab2-a4d2-82b0e453c98d","html_url":"https://github.com/XenitAB/azcagit","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XenitAB%2Fazcagit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XenitAB%2Fazcagit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XenitAB%2Fazcagit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/XenitAB%2Fazcagit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/XenitAB","download_url":"https://codeload.github.com/XenitAB/azcagit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228432514,"owners_count":17918894,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["azure","azure-container-apps","gitops","golang","terraform"],"created_at":"2024-12-06T08:12:24.536Z","updated_at":"2024-12-06T08:12:25.808Z","avatar_url":"https://github.com/XenitAB.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# azcagit\n\nWelcome to `azcagit` (_/ɑːsk/ /eɪ/ /ɡɪt/ - ask a git_)!\n\nIt's [GitOps](https://opengitops.dev/#principles) for [Azure Container Apps](https://azure.microsoft.com/en-us/services/container-apps/#overview). You can call it a GitOps Engine for Azure Container Apps.\n\n\u003e Please note: This is an early version and will have lots of breaking changes in the future.\n\n## Changelog\n\nBelow, large (and eventually breaking) will be documented:\n\n### v0.0.19\n\nRefactored `azcagit` to run as an [Azure Container App Job](https://github.com/XenitAB/azcagit/pull/57) on a schedule. Lots of breaking changes.\n\n**BREAKING CHANGES**\n\n- trigger-client cli parameter: `--namespace` instead of `--fully-qualified-namespace` (note: don't use the full name anymore)\n- trigger-client cli parameter: `--queue` instead of `--topic`\n- CosmosDB is used for cache\n- Service Bus is now basic\n\n### v0.0.18\n\nSupport for `AzureContainerJob` was added.\n\n**BREAKING CHANGES**\n\n- `spec.remoteSecrets[].appSecretName` has been renamed `spec.remoteSecrets[].secretName`\n- `apiVersion` has been updated from `aca.xenit.io/v1alpha1` to `aca.xenit.io/v1alpha2` for kind `AzureContainerApp`\n\n## Overview\n\n![overview](docs/overview.png \"Overview of azcagit\")\n\nIn the test scenario, two resource groups are created:\n\n- platform\n- tenant\n\nPlatform is used for what we call \"platform services\", in this case the virtual network, Container Apps Managed Environment, Azure Container Registry and things to connect to using Dapr (like Storage Account or a Service Bus). In here, `azcagit` will also be setup to take care of the reconciliation of the tenant resource group.\n\nTenant is used only to synchronize the Container Apps manifests. The Container Apps that are created by `azcagit` will reside here.\n\nThe manifests are in the same format as Kubernetes manifests ([Kubernetes Resource Model aka KRM](https://cloud.google.com/blog/topics/developers-practitioners/build-platform-krm-part-2-how-kubernetes-resource-model-works)), but with a hard coupling to the [Azure Container Apps specification](https://docs.microsoft.com/en-us/azure/templates/microsoft.app/containerapps?pivots=deployment-language-arm-template) for `spec.app` when using `kind: AzureContainerApp` and [Azure Container Jobs specification](https://learn.microsoft.com/en-us/azure/templates/microsoft.app/jobs?pivots=deployment-language-arm-template) for `spec.job` when using `kind: AzureContainerJob`. Auto generated schemas can be found in the [schemas](schemas/) directory.\n\nAn example manifest of an app:\n\n```yaml\nkind: AzureContainerApp\napiVersion: aca.xenit.io/v1alpha2\nmetadata:\n  name: foobar\nspec:\n  locationFilter:\n    - West Europe\n  remoteSecrets:\n    - secretName: connection-string\n      remoteSecretName: mssql-connection-string\n  replacements:\n    images:\n      - imageName: \"mcr.microsoft.com/azuredocs/containerapps-helloworld\"\n        newImageTag: \"v0.1\"\n  app:\n    properties:\n      configuration:\n        activeRevisionsMode: Single\n      template:\n        containers:\n          - name: simple-hello-world-container\n            image: mcr.microsoft.com/azuredocs/containerapps-helloworld:latest\n            resources:\n              cpu: 0.25\n              memory: .5Gi\n            env:\n              - name: CONNECTION_STRING\n                secretRef: connection-string\n              - name: MEANING_WITH_LIFE\n                value: \"forty two\"\n        scale:\n          minReplicas: 1\n          maxReplicas: 1\n```\n\nexample manifest of a job:\n\n```yaml\nkind: AzureContainerJob\napiVersion: aca.xenit.io/v1alpha2\nmetadata:\n  name: foobar\nspec:\n  locationFilter:\n    - West Europe\n  remoteSecrets:\n    - secretName: connection-string\n      remoteSecretName: mssql-connection-string\n  replacements:\n    images:\n      - imageName: \"mcr.microsoft.com/k8se/quickstart-jobs\"\n        newImageTag: \"latest\"\n  job:\n    properties:\n      configuration:\n        scheduleTriggerConfig:\n          cronExpression: \"*/5 * * * *\"\n          parallelism: 1\n          replicaCompletionCount: 1\n        replicaRetryLimit: 1\n        replicaTimeout: 1800\n        triggerType: Schedule\n      template:\n        containers:\n          - name: main\n            image: mcr.microsoft.com/k8se/quickstart-jobs:foobar\n            resources:\n              cpu: 0.25\n              memory: .5Gi\n```\n\nYAML-files can contain one or more documents (with `---` as a document separator). As of right now, all files in the git repository path (configured with `--git-path` when launching `azcagit`) needs to pass validation for any deletion to occur (deletion will be disabled if any manifests contains validation errors).\n\n## Features\n\n- Synchronize git repository (using https only, public and private) to a specific resource group\n- Choose what folder in the git repository to synchronize\n- Trigger manual synchronization using CLI\n- Populate Container Apps secrets from Azure KeyVault\n- Populate Container Apps registries with default registry credential\n- Send notifications to the git commits\n- Filter locations, making it possible to specify in the manifest what regions can run the app\n- Push custom metrics to Azure monitor\n- Functionality to replace the image tag using `spec.replacements.images`\n\n## Frequently Asked Questions\n\n\u003e What happens if a manifest can't be parsed?\n\nReconciliation will stop an no changes (add/delete/update) will be made until the parse error is fixed.\n\n\u003e What happens if a secret in the KeyVault is defined in a manifest but doesn't exist?\n\nReconciliation will stop an no changes (add/delete/update) will be made until the secret is added to the KeyVault or it's removed from the manifest.\n\n\u003e What happens if a secret is changed in the KeyVault?\n\nThe Container App will be updated at the next reconcile.\n\n\u003e What happens if I add the tag `aca.xenit.io=true` to a Container App in the tenant resource group, without the app being defined in a manifest?\n\nIt will be removed at the next reconcile.\n\n\u003e What happens if I remove the tag `aca.xenit.io=true` from a Container App in the tenant resource group, while still having a manifest for it?\n\nIt won't be reconciled anymore. Depending on the order, a few apps before will still be reconciled but none after.\n\n\u003e What happens if I add the tag `aca.xenit.io=true` to a Container App in the tenant resource group, while it's also defined in a manifest?\n\nIt will be updated based on the manifest.\n\n\u003e What properties, as of now, can't be used even though they are defined in the Azure Container Apps specification?\n\n- `spec.app.properties.managedEnvironmentID`: it's defined by azcagit\n- `spec.app.location`: it's defined by azcagit\n\n\u003e What git providers are supported?\n\nMost likely those supported by go-git, but with that said has only Azure DevOps (Azure Repositories) and GitHub been tested. If you need it with on-prem/enterprise variants of them or another git provider doesn't work as expected, create an issue or PR.\n\n\u003e Are private git repositories supported?\n\nYes, as long as you provide credentials in `--git-url` like `https://username:token@provider.io/repo`.\n\n\u003e I'm using a public repository without credentials but `azcagit` throws an error that it needs credentials, isn't it supported to use public repositories without credentials?\n\nIt is supported to use public repositories without credentials, but if you have enabled notifications (`--notifications-enabled`) then credentials are required to be able to push the git status to the commit.\n\n\u003e How does a notification look?\n\nIn GitHub, a successful notification looks like this:\n\n![example-notification](docs/example-notification.png \"Example of a notification in GitHub\")\n\n\u003e Is multi region supported?\n\nIt sure is! You can find an example for the setup using terraform [here](test/terraform-multi-region/main.tf). We've also recorded a short video showing it in action:\n\n[![Watch the video](docs/multi-region-thumbnail.jpg)](https://youtu.be/9SwfSIfa6I0)\n\n\u003e What is the location filter feature?\n\nIt makes it possible to specify `spec.locationFilter` with an array of what Azure regions are allowed to run this specific app.\n\n![multi-region-location-filter](docs/multi-region-location-filter.png \"Example of a notification in GitHub\")\n\n\u003e How does the location filter work?\n\n- No change if `spec.locationFilter` isn't defined\n- No change if `spec.locationFilter` is an empty list\n- No change if `spec.locationFilter` contains the location of azcagit (defined with `--location`)\n- If `spec.locationFilter` has a value, of values, where it or none of them match the location of azcagit - we'll skip it (only logged with `--debug` enabled)\n\n\u003e Where can I find the custom metrics?\n\nIf you open the `azcagit` container app (in the platform resource group) and go to Monitoring and then Metrics, you can choose the namespace azcagit and then the specific metrics you want to look at.\n\n![custom-metrics](docs/custom-metrics.png \"Example custom metrics in Azure\")\n\n\u003e How does the image tag replacement work?\n\nIf an image replacement is configured, it will match for the image name and if found it will apply the newImageTag.\n\n## Things TODO in the future\n\n- [x] Append secrets to Container Apps from KeyVault\n- [x] ~~Better error handling of validation failures (should deletion be stopped?)~~ _stop reconciliation on any parsing error_\n- [x] Push git commit status (like [Flux notification-controller](https://fluxcd.io/docs/components/notification/provider/#git-commit-status))\n- [ ] Health checks\n- [x] Metrics\n- [x] Manually trigger reconcile\n- [x] Enforce Location for app\n- [x] Add Container Registry credentials by default\n- [x] Add location filter\n\n## Usage\n\n`azcagit` will connect to a git repository (over https) and syncronize it on an interval. If changes are identified, it will push them to Azure. It can create, update and delete Azure Container Apps.\n\nThe easiest way to test it is using the terraform code which you can find in `test/terraform`. You may have to update a few names to get it working.\n\n### Manually trigger reconcile\n\nIf you have used the example terraform, there will be a service bus created with a queue. `azcagit-trigger` will start and then trigger `azcagit-reconcile` when a message is received on the queue.\n\nYou can use `azcagit-trigger-client` to trigger it:\n\n```go\ngo run ./trigger-client -n namespace -q queue\n```\n\nPlease note that this requires you to be authenticated with either the Azure CLI and have access to publish to this topic with your current user, or use environment varaibles with a service principal that has access.\n\n## Local development\n\n### Configuration parameters\n\n#### Environment variables\n\nCreate an environment file named `.tmp/env`.\n\nThe following parameters can be used\n\n```env\nRG_NAME=resource_group_name\nSUB_ID=azure_subscription_id\nME_ID=azure_container_apps_managed_environment_id\nKV_NAME=kvcontainerapps\nGIT_URL_AND_CREDS=git_url_with_optional_credentials\n```\n\nThe `GIT_URL_AND_CREDS` can be in either the format `https://github.com/simongottschlag/aca-test-yaml.git` or `https://username:secret@github.com/simongottschlag/aca-test-yaml-priv.git`.\n\nTo use a service principal with it's client_id and client_secret, add the following environment variables:\n\n```env\nTENANT_ID=azure_tenant_id\nCLIENT_ID=service_principal_application_id\nCLIENT_SECRET=service_principal_secret\n```\n\n### Terraform\n\nCreate a file named `.tmp/lab.tfvars` and configure the following:\n\n```terraform\ngit_config = {\n  url      = \"github.com/simongottschlag/aca-test-yaml.git\"\n  branch   = \"main\"\n  path     = \"yaml\"\n  username = \"\"\n  secret   = \"\"\n}\n\nazcagit_version = \"vX.Y.Z\"\n```\n\nMake sure url doesn't contain `https://`, it will be appended by terraform. If you want to use a private repository, add username and secret (PAT). Path is where you want `azcagit` to start traversing the directory tree.\n\nRun the following to setup an environment (you may have to change storage account names etc): `make terraform-up`\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxenitab%2Fazcagit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxenitab%2Fazcagit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxenitab%2Fazcagit/lists"}