{"id":13742362,"url":"https://github.com/linki/cloudformation-operator","last_synced_at":"2025-03-16T19:32:10.762Z","repository":{"id":28458903,"uuid":"114269065","full_name":"linki/cloudformation-operator","owner":"linki","description":"A Kubernetes operator for managing CloudFormation stacks via a CustomResource","archived":false,"fork":false,"pushed_at":"2023-01-05T09:42:29.000Z","size":9958,"stargazers_count":102,"open_issues_count":14,"forks_count":26,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-05-22T13:32:48.830Z","etag":null,"topics":["aws","cloudformation","kubernetes","kubernetes-crd","kubernetes-operator"],"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/linki.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}},"created_at":"2017-12-14T15:51:15.000Z","updated_at":"2024-03-24T02:43:15.000Z","dependencies_parsed_at":"2023-01-14T08:51:22.839Z","dependency_job_id":null,"html_url":"https://github.com/linki/cloudformation-operator","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linki%2Fcloudformation-operator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linki%2Fcloudformation-operator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linki%2Fcloudformation-operator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linki%2Fcloudformation-operator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linki","download_url":"https://codeload.github.com/linki/cloudformation-operator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243826792,"owners_count":20354220,"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":["aws","cloudformation","kubernetes","kubernetes-crd","kubernetes-operator"],"created_at":"2024-08-03T05:00:30.550Z","updated_at":"2025-03-16T19:32:10.447Z","avatar_url":"https://github.com/linki.png","language":"Go","funding_links":[],"categories":["Repository is obsolete"],"sub_categories":["Awesome Operators in the Wild"],"readme":"[![GitHub release](https://img.shields.io/github/release/linki/cloudformation-operator.svg)](https://github.com/linki/cloudformation-operator/releases)\n[![Docker Repository on Quay](https://quay.io/repository/linki/cloudformation-operator/status \"Docker Repository on Quay\")](https://quay.io/repository/linki/cloudformation-operator)\n\n# cloudformation-operator\n\nA Kubernetes operator for managing CloudFormation stacks via `kubectl` and a custom resource definition.\n\n**Warning: this project is in alpha state. It should only be used to try out the demo and get the general idea.**\n\n**This version uses the new operator-sdk. It's untested and may not work correctly**\n\n# Deploy to a cluster\n\nYou need API access to a cluster running at least Kubernetes v1.17.0+.\n\nStart the CloudFormation operator in your cluster by using the provided manifests:\n\n```console\n$ make deploy IMG=quay.io/linki/cloudformation-operator:latest\n```\n\nModify the `region` flag to match your cluster's.\n\nAdditionally you need to make sure that the operator Pod has enough AWS IAM permissions to create, update and delete CloudFormation stacks as well as permission to modify any resources that are part of the CloudFormation stacks you intend to deploy. In order to follow the example below it needs access to CloudFormation as well as S3.\n\nThe operator will require an IAM role or user credentials. \nUse the following Policy document as a guideline in order to follow the tutorial:\n\n```yaml\nMyIAMRole:\n  Properties:\n    ...\n    Policies:\n    - PolicyDocument:\n        Statement:\n        - {Action: 'cloudformation:*', Effect: Allow, Resource: '*'}\n        - {Action: 's3:*', Effect: Allow, Resource: '*'}\n        Version: '2012-10-17'\n    ...\n```\n\nThe operator will usually use the IAM role of the EC2 instance it's running on, so you have to add those permissions to that role. If you're using [Kube2IAM](https://github.com/jtblin/kube2iam) or similar and give your Pod a dedicated IAM role then you have to add the permissions to that role.\n\nOnce running the operator should print some output but shouldn't actually do anything at this point. Leave it running, keep watching its logs and continue with the steps below.\n\n# Demo\n\n## Create stack\n\nCurrently you don't have any stacks.\n\n```console\n$ kubectl get stacks\nNo resources found.\n```\n\nLet's create a simple one that manages an S3 bucket:\n\n```yaml\napiVersion: cloudformation.linki.space/v1alpha1\nkind: Stack\nmetadata:\n  name: my-bucket\nspec:\n  template: |\n    ---\n    AWSTemplateFormatVersion: '2010-09-09'\n\n    Resources:\n      S3Bucket:\n        Type: AWS::S3::Bucket\n        Properties:\n          VersioningConfiguration:\n            Status: Suspended\n```\n\nThe Stack resource's definition looks a lot like any other Kubernetes resource manifest.\nThe `spec` section describes an attribute called `template` which contains a regular CloudFormation template.\n\nGo ahead and submit the stack definition to your cluster:\n\n```console\n$ kubectl apply -f config/samples/cfs-my-bucket-v1.yaml\nstack \"my-bucket\" created\n$ kubectl get stacks\nNAME        AGE\nmy-bucket   21s\n```\n\nOpen your AWS CloudFormation console and find your new stack.\n\n![Create stack](docs/img/stack-create.png)\n\nOnce the CloudFormation stack is created check that your S3 bucket was created as well.\n\nThe operator will write back additional information about the CloudFormation Stack to your Kubernetes resource's `status` section, e.g. the `stackID`:\n\n```console\n$ kubectl get stacks my-bucket -o yaml\nspec:\n  template:\n  ...\nstatus:\n  stackID: arn:aws:cloudformation:eu-central-1:123456789012:stack/my-bucket/327b7d3c-f27b-4b94-8d17-92a1d9da85ab\n```\n\nVoilà, you just created a CloudFormation stack by only talking to Kubernetes.\n\n## Update stack\n\nYou can also update your stack: Let's change the `VersioningConfiguration` from `Suspended` to `Enabled`:\n\n```yaml\napiVersion: cloudformation.linki.space/v1alpha1\nkind: Stack\nmetadata:\n  name: my-bucket\nspec:\n  template: |\n    ---\n    AWSTemplateFormatVersion: '2010-09-09'\n\n    Resources:\n      S3Bucket:\n        Type: AWS::S3::Bucket\n        Properties:\n          VersioningConfiguration:\n            Status: Enabled\n```\n\nAs with most Kubernetes resources you can update your `Stack` resource by applying a changed manifest to your Kubernetes cluster or by using `kubectl edit stack my-stack`.\n\n```console\n$ kubectl apply -f config/samples/cfs-my-bucket-v2.yaml\nstack \"my-bucket\" configured\n```\n\nWait until the operator discovered and executed the change, then look at your AWS CloudFormation console again and find your stack being updated, yay.\n\n![Update stack](docs/img/stack-update.png)\n\n## Tags\n\nYou may want to assign tags to your CloudFormation stacks. The tags added to a CloudFormation stack will be propagated to the managed resources. This feature may be useful in multiple cases, for example, to distinguish resources at billing report. Current operator provides two ways to assign tags:\n- `--tag` command line argument or `AWS_TAGS` environment variable which allows setting default tags for all resources managed by the operator. The format is `--tag=foo=bar --tag=wambo=baz` on the command line or with a line break when specifying as an env var. (e.g. in zsh: `AWS_TAGS=\"foo=bar\"$'\\n'\"wambo=baz\"`)\n- `tags` parameter at kubernetes resource spec:\n```yaml\napiVersion: cloudformation.linki.space/v1alpha1\nkind: Stack\nmetadata:\n  name: my-bucket\nspec:\n  tags:\n    foo: dataFromStack\n  template: |\n    ---\n    AWSTemplateFormatVersion: '2010-09-09'\n\n    Resources:\n      S3Bucket:\n        Type: AWS::S3::Bucket\n        Properties:\n          VersioningConfiguration:\n            Status: Enabled\n```\n\nResource-specific tags have precedence over the default tags. Thus if a tag is defined at command-line arguments and for a `Stack` resource, the value from the `Stack` resource will be used.\n\nIf we run the operation and a `Stack` resource with the described above examples, we'll see such picture:\n\n![Stack tags](docs/img/stack-tags.png)\n\n## Parameters\n\nHowever, often you'll want to extract dynamic values out of your CloudFormation stack template into so called `Parameters` so that your template itself doesn't change that often and, well, is really a *template*.\n\nLet's extract the `VersioningConfiguration` into a parameter:\n\n```yaml\napiVersion: cloudformation.linki.space/v1alpha1\nkind: Stack\nmetadata:\n  name: my-bucket\nspec:\n  parameters:\n    VersioningConfiguration: Enabled\n  template: |\n    ---\n    AWSTemplateFormatVersion: '2010-09-09'\n\n    Parameters:\n      VersioningConfiguration:\n        Type: String\n        Default: none\n        AllowedValues:\n        - \"Enabled\"\n        - \"Suspended\"\n\n    Resources:\n      S3Bucket:\n        Type: AWS::S3::Bucket\n        Properties:\n          VersioningConfiguration:\n            Status:\n              Ref: VersioningConfiguration\n```\n\nand apply it to your cluster:\n\n```console\n$ kubectl apply -f config/samples/cfs-my-bucket-v3.yaml\nstack \"my-bucket\" configured\n```\n\nSince we changed the template a little this will update your CloudFormation stack. However, since we didn't actually change anything because we injected the same `VersioningConfiguration` value as before, your S3 bucket shouldn't change.\n\nAny CloudFormation parameters defined in the CloudFormation template can be specified in the `Stack` resource's `spec.parameters` section. It's a simple key/value map.\n\n## Outputs\n\nFurthermore, CloudFormation supports so called `Outputs`. These can be used for dynamic values that are only known after a stack has been created.\nIn our example, we don't define a particular S3 bucket name but instead let AWS generate one for us.\n\nLet's change our CloudFormation template to expose the generated bucket name via an `Output`:\n\n```yaml\napiVersion: cloudformation.linki.space/v1alpha1\nkind: Stack\nmetadata:\n  name: my-bucket\nspec:\n  parameters:\n    VersioningConfiguration: Enabled\n  template: |\n    ---\n    AWSTemplateFormatVersion: '2010-09-09'\n\n    Parameters:\n      VersioningConfiguration:\n        Type: String\n        Default: none\n        AllowedValues:\n        - \"Enabled\"\n        - \"Suspended\"\n\n    Resources:\n      S3Bucket:\n        Type: AWS::S3::Bucket\n        Properties:\n          VersioningConfiguration:\n            Status:\n              Ref: VersioningConfiguration\n\n    Outputs:\n      BucketName:\n        Value: !Ref 'S3Bucket'\n        Description: Name of the sample Amazon S3 bucket.\n```\n\nApply the change to our cluster and wait until the operator has successfully updated the CloudFormation stack.\n\n```console\n$ kubectl apply -f config/samples/cfs-my-bucket-v4.yaml\nstack \"my-bucket\" configured\n```\n\nEvery `Output` you define will be available in your Kubernetes resource's `status` section under the `outputs` field as a key/value map.\n\nLet's check the name of our S3 bucket:\n\n```console\n$ kubectl get stacks my-bucket -o yaml\nspec:\n  template:\n  ...\nstatus:\n  stackID: ...\n  outputs:\n    BucketName: my-bucket-s3bucket-tarusnslfnsj\n```\n\nIn the template we defined an `Output` called `BucketName` that should contain the name of our bucket after stack creation. Looking up the corresponding value under `.status.outputs[BucketName]` reveals that our bucket was named `my-bucket-s3bucket-tarusnslfnsj`.\n\n## Delete stack\n\nThe operator captures the whole lifecycle of a CloudFormation stack. So if you delete the resource from Kubernetes, the operator will teardown the CloudFormation stack as well. Let's do that now:\n\n```console\n$ kubectl delete stack my-bucket\nstack \"my-bucket\" deleted\n```\n\nCheck your CloudFormation console once more and validate that your stack as well as your S3 bucket were deleted.\n\n![Delete stack](docs/img/stack-delete.png)\n\n# Command-line arguments\n\nArgument | Environment variable | Default value | Description\n---------|----------------------|---------------|------------\nassume-role | | | Assume AWS role when defined. Useful for stacks in another AWS account. Specify the full ARN, e.g. `arn:aws:iam::123456789:role/cloudformation-operator`\ncapability | | | Enable specified capabilities for all stacks managed by the operator instance. Current parameter can be used multiple times. For example: `--capability CAPABILITY_NAMED_IAM --capability CAPABILITY_IAM`. Or with a line break when specifying as an environment variable: `AWS_CAPABILITIES=CAPABILITY_IAM$'\\n'CAPABILITY_NAMED_IAM`\ndry-run | | | If true, don't actually do anything.\ntag ... | | | Default tags which should be applied for all stacks. The format is `--tag=foo=bar --tag=wambo=baz` on the command line or with a line break when specifying as an env var. (e.g. in zsh: `AWS_TAGS=\"foo=bar\"$'\\n'\"wambo=baz\"`)\nnamespace | WATCH_NAMESPACE | default | The Kubernetes namespace to watch\nregion | | | The AWS region to use\n\n# Cleanup\n\nClean up the resources:\n\n```console\n$ make undeploy\n```\n\n# Build and run locally\n\nThis project uses the [operator sdk](https://github.com/operator-framework/operator-sdk).\n\n```console\n$ make\n$ WATCH_NAMESPACE=default KUBERNETES_CONFIG=~/.kube/config make run OPERATOR_FLAGS=\"--region eu-central-1\"\n```\n\n## Build the docker image\n\n```console\n$ make docker-build quay.io/linki/cloudformation-operator:latest\n$ make docker-push quay.io/linki/cloudformation-operator:latest\n```\n\n## Test it locally\n\nYou can use `OPERATOR_FLAGS` to pass in flags using the operator-sdk.\n\nAssuming you are using minikube:\n\n```console\n$ minikube start # you will be have a kubeconfig read to use by cloudformation operator\n$ export AWS_PROFILE=my_profile # setup your aws config\n$ cd $GOPATH/src/github.com/linki/cloudformation-operator\n$ # run cloudformation operator based on previous settings and env vars\n$ WATCH_NAMESPACE=staging make run OPERATOR_FLAGS=\"--dry-run=true --region=eu-central-1\"\nI0122 16:31:14.509064  195514 request.go:645] Throttling request took 1.027790903s, request: GET:https://api.crc.testing:6443/apis/template.openshift.io/v1?timeout=32s\n2021-01-22T16:31:15.863-0500    INFO    controller-runtime.metrics      metrics server is starting to listen    {\"addr\": \":8080\"}\n2021-01-22T16:31:15.864-0500    INFO    setup   \n2021-01-22T16:31:15.864-0500    INFO    setup   starting manager\n2021-01-22T16:31:15.864-0500    INFO    controller-runtime.manager      starting metrics server {\"path\": \"/metrics\"}\n2021-01-22T16:31:15.864-0500    INFO    controller-runtime.manager.controller.stack     Starting EventSource    {\"reconciler group\": \"cloudformation.linki.space\", \"reconciler kind\": \"Stack\", \"source\": \"kind source: /, Kind=\"}\n2021-01-22T16:31:15.965-0500    INFO    controller-runtime.manager.controller.stack     Starting Controller     {\"reconciler group\": \"cloudformation.linki.space\", \"reconciler kind\": \"Stack\"}\n2021-01-22T16:31:15.965-0500    INFO    controller-runtime.manager.controller.stack     Starting workers        {\"reconciler group\": \"cloudformation.linki.space\", \"reconciler kind\": \"Stack\", \"worker count\": 1}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinki%2Fcloudformation-operator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinki%2Fcloudformation-operator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinki%2Fcloudformation-operator/lists"}