{"id":13531716,"url":"https://github.com/jcouyang/dhall-aws-cloudformation","last_synced_at":"2025-07-13T20:33:35.085Z","repository":{"id":42462169,"uuid":"349661232","full_name":"jcouyang/dhall-aws-cloudformation","owner":"jcouyang","description":"Typecheck, template and modularize your AWS CloudFormation with Dhall ","archived":false,"fork":false,"pushed_at":"2024-03-03T01:28:59.000Z","size":51883,"stargazers_count":30,"open_issues_count":0,"forks_count":10,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-07T18:13:54.273Z","etag":null,"topics":["aws","cloudformation","dhall"],"latest_commit_sha":null,"homepage":"https://gh.1punch.dev/dhall-aws-cloudformation/package.dhall.html","language":"Haskell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jcouyang.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}},"created_at":"2021-03-20T07:51:07.000Z","updated_at":"2024-09-30T19:45:08.000Z","dependencies_parsed_at":"2024-11-02T18:32:51.963Z","dependency_job_id":"f85bd04f-2c1c-4d31-bed1-177694a68082","html_url":"https://github.com/jcouyang/dhall-aws-cloudformation","commit_stats":null,"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/jcouyang/dhall-aws-cloudformation","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouyang%2Fdhall-aws-cloudformation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouyang%2Fdhall-aws-cloudformation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouyang%2Fdhall-aws-cloudformation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouyang%2Fdhall-aws-cloudformation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jcouyang","download_url":"https://codeload.github.com/jcouyang/dhall-aws-cloudformation/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcouyang%2Fdhall-aws-cloudformation/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265200091,"owners_count":23726768,"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","dhall"],"created_at":"2024-08-01T07:01:05.085Z","updated_at":"2025-07-13T20:33:33.124Z","avatar_url":"https://github.com/jcouyang.png","language":"Haskell","funding_links":[],"categories":["Libraries","aws"],"sub_categories":[],"readme":"# Dhall AWS CloudFormation\n\n`dhall-aws-cloudformation` contains [Dhall](https://github.com/dhall-lang/dhall-lang) bindings to AWS CloudFormation, so you can generate CloudFormation template from Dhall expressions. This will let you easily typecheck, template and modularize your CloudFormation definitions.\n\n## :mag: [References](https://gh.1punch.dev/dhall-aws-cloudformation/package.dhall.html)\n## :bulb: [Examples](https://gh.1punch.dev/dhall-aws-cloudformation/examples/index.html)\n\n## :book: Usage\n\n### Use resource schema\nAWS Cloudformation has massive amount of specifications, to load all\ndhall remotely will be very slow and impractical.\n\nOne simply way to make import faster is by only importing just each resource you need\n#### Remote import resource\n```dhall\nlet Function =\n    -- import Lambda Function type definition\n      https://raw.githubusercontent.com/jcouyang/dhall-aws-cloudformation/0.10.82/cloudformation/AWS::Lambda::Function.dhall\n        sha256:c2e1876f87edb0e7c08058a8a0c90c6a56be530b6c4c7ba3ef3c897feb75cde1\n\nlet Fn =\n    -- Intrinsic functions\n      https://raw.githubusercontent.com/jcouyang/dhall-aws-cloudformation/0.10.82/Fn.dhall\n        sha256:b2cf7212998902c44ba1bf1670a8e0bc40562542b9b525587cd044f317644e47\n\nlet S =\n    {-\n    Each AWS String field can be either a String or a Intrinsic function, we can use `Fn.renderText \"abc\"` to create static string\n\n    Or `Fn.render (Ref \"abc\")` to create a function that ref to a string\n    -}   Fn.renderText\n\nlet render =\n    -- function can be nested `render (Fn.Ref (Fn.GetAtt (Fn.String \"abc.property\")))`\n      Fn.render\n\nlet example0 =\n      { Resources.HelloWorldFunction\n        = Function.Resources::{\n        , Properties = Function.Properties::{\n          , Handler = Some (S \"index.handler\")\n          , Code = Function.Code::{\n            , S3Bucket = Some (S \"lambda-functions\")\n            , S3Key = Some (S \"amilookup.zip\")\n            }\n          , Runtime = Some (S \"nodejs12.x\")\n          , Role = render (Fn.Ref \"role logical id\")\n          , Timeout = Some +25\n          , TracingConfig = Some { Mode = Some (S \"Active\") }\n          }\n        }\n      }\n\nin  example0\n\n```\n\nto convert to CloudFormation JSON file just\n\n```\ndhall-to-json \u003c ./template.dhall \u003e ./template.json\n```\n\nwhich generates\n\n```json\n{\n  \"Resources\": {\n    \"HelloWorldFunction\": {\n      \"Properties\": {\n        \"Code\": {\n          \"S3Bucket\": \"lambda-functions\",\n          \"S3Key\": \"amilookup.zip\"\n        },\n        \"Handler\": \"index.handler\",\n        \"Role\": {\n          \"Ref\": \"role logical id\"\n        },\n        \"Runtime\": \"nodejs12.x\",\n        \"Timeout\": 25,\n        \"TracingConfig\": {\n          \"Mode\": \"Active\"\n        }\n      },\n      \"Type\": \"AWS::Lambda::Function\"\n    }\n  }\n}\n\n```\n\nOther way around is build the binary of subset of the resources using nix\n\n#### Build and load package.dhall binary to local cache\n\nHave something like `./examples/example0.nix`, and the dhall file you want to compile `./examples/example0.dhall`\n\n```nix\nlet aws =\n      missing\n        sha256:a04e4db67b092e40987639cca5cd845f452b3984ee7ec77172f815a31e830325\n\nlet Function = aws.Cloudformation.`AWS::Lambda::Function`\n\nlet Fn = aws.Fn\n\nlet S =\n    {-\n    Each AWS String field can be either a String or a Intrinsic function, we can use `Fn.renderText \"abc\"` to create static string\n\n    Or `Fn.render (Ref \"abc\")` to create a function that ref to a string\n    -}   Fn.renderText\n\nlet render =\n    -- function can be nested `render (Fn.Ref (Fn.GetAtt (Fn.String \"abc.property\")))`\n      Fn.render\n\nlet example0 =\n      { Resources.HelloWorldFunction\n        = Function.Resources::{\n        , Properties = Function.Properties::{\n          , Handler = Some (S \"index.handler\")\n          , Code = Function.Code::{\n            , S3Bucket = Some (S \"lambda-functions\")\n            , S3Key = Some (S \"amilookup.zip\")\n            }\n          , Runtime = Some (S \"nodejs12.x\")\n          , Role = render (Fn.Ref \"role logical id\")\n          , Timeout = Some +25\n          , TracingConfig = Some { Mode = Some (S \"Active\") }\n          }\n        }\n      }\n\nin  example0\n\n```\nAdd all the resources you need to `cf-preset`, run nix-build, if the subset is not too large it will be very quick, and you will see something like:\n```\n ⚠ ─ If error occured, you may need to update the sha256 in your dhall file e.g.\n │ let aws = missing sha256:a04e4db67b092e40987639cca5cd845f452b3984ee7ec77172f815a31e830325\n```\n\nActually the first time it will fail since you can't guess the correct sha of the subset binary, now if you update example0.dhall with the correct sha,\nit should then be able to compile to `./result/example0.yaml`\n```dhall\nlet aws =\nmissing\nsha256:a04e4db67b092e40987639cca5cd845f452b3984ee7ec77172f815a31e830325\n\nlet Function = aws.Cloudformation.`AWS::Lambda::Function`\n```\n\n### Intrinsic Function\n\nThe following intrinsic functions are implemented, you can find examples of using intrinsic function in [Fn.dhall document](https://oyanglul.us/dhall-aws-cloudformation/Fn.dhall.html)\n- [x] Fn::Base64\n- [x] Fn::Cidr\n- [x] Condition functions\n- [x] Fn::FindInMap\n- [x] Fn::GetAtt\n- [x] Fn::GetAZs\n- [x] Fn::ImportValue\n- [x] Fn::Join\n- [x] Fn::Select\n- [x] Fn::Split\n- [x] Fn::Sub\n- [x] Fn::Transform\n- [x] Ref\n\n### Type Safe `Fn::GetAttr`\nInstead of manually looking for AWS documents to make sure the resource has what attributes, we can just use `\u003cResource\u003e.GetAttr.\u003cattribute name\u003e`:\n\n```dhall\nrender (Role.GetAttr.Arn \"HelloWorldFunctionRole\")\n```\n\nSo the compiler can just help you find the correct attributes available.\n\n### Sam Policy Templates\nCloudformation's Policy document is loosy type as just JSON, it is hard to get the policy right and too many boilerplates to create a Dhall JSON data\nthanks to [AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html) there are some common policy documents we can laverage\n\nthese templates are translated into Dhall functions, so you don't need to use SAM to be able to use these policy documents.\n\n```dhall\nlet Policy = https://github.com/jcouyang/dhall-aws-cloudformation/raw/0.10.82/cloudformation/AWS::IAM::Role/Policy.dhall\nlet Sam/Policy = https://github.com/jcouyang/dhall-aws-cloudformation/raw/0.10.82/sam/policy-template/package.dhall\n...\n  Policies = Some [Policy::{\n    , PolicyDocument = Sam/Policy.DynamoDBReadPolicy (Fn.String \"DBName\")\n    , PolicyName = s \"dynamo read only\"\n  }]\n...\n```\nwill generates\n```json\n{\n  \"Policies\": [\n    {\n      \"PolicyDocument\": {\n        \"Statement\": [\n          {\n            \"Action\": [\n              \"dynamodb:GetItem\",\n              \"dynamodb:Scan\",\n              \"dynamodb:Query\",\n              \"dynamodb:BatchGetItem\",\n              \"dynamodb:DescribeTable\"\n            ],\n            \"Effect\": \"Allow\",\n            \"Resource\": [\n              {\n                \"Fn::Sub\": [\n                  \"arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tableName}\",\n                  {\n                    \"tableName\": \"DBName\"\n                  }\n                ]\n              },\n              {\n                \"Fn::Sub\": [\n                  \"arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tableName}/index/*\",\n                  {\n                    \"tableName\": \"DBName\"\n                  }\n                ]\n              }\n            ]\n          }\n        ]\n      },\n      \"PolicyName\": \"dynamo read only\"\n    }\n  ]\n}\n```\n## :coffee: Contribute\n### Build and Test\n\n```\n\u003e nix-shell\n$ cabal build\n$ cabal test\n```\n### Generate Type Definitions\ne definitions are generated from config file `./config.dhall` which contains specifications used by [AWS CDK](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/cfnspec/build-tools/update.sh) as well:\n- [cloudformation](https://d1uauaxba7bl26.cloudfront.net/latest/gzip/CloudFormationResourceSpecification.json)\n- [sam](https://raw.githubusercontent.com/awslabs/goformation/master/generate/sam-2016-10-31.json)\n\nregenerate types definition files, simply run\n```\n$ cabal run\n```\nif you just want to regenerate dhall files without setting up haskell dev environment, just\n```sh\ndocker run --rm -v $(pwd):/data -w /data ghcr.io/jcouyang/dhall-aws-cloudformation\n```\n## :warning: Known Issue\nThe following CloudFormation definitions will raise assertion error due to invalid type definition such as empty type or cyclic import\n- `AWS::EMR::Cluster`\n- `AWS::EMR::InstanceGroupConfig`\n- `AWS::EMR::InstanceFleetConfig`\n- `AWS::Macie::FindingsFilter`\n- `AWS::Connect::EvaluationForm`\n- `AWS::IoTTwinMaker::ComponentType`\n- `AWS::IoTTwinMaker::Entity`\n- `AWS::Lex::Bot`\n- `AWS::DataBrew::Recipe`\n- `AWS::FIS::ExperimentTemplate`\n- `AWS::SageMaker::ModelBiasJobDefinition`\n- `AWS::SageMaker::ModelQualityJobDefinition`\n- `AWS::SageMaker::MonitoringSchedule`\n- `AWS::SageMaker::DataQualityJobDefinition`\n- `AWS::SageMaker::ModelExplainabilityJobDefinition`\n- `AWS::S3::StorageLens`\n- `AWS::StepFunctions::StateMachine`\n- `AWS::MWAA::Environment`\n- `AWS::WAFv2::RuleGroup`\n- `AWS::WAFv2::WebACL`\n- `AWS::ServiceDiscovery::PrivateDnsNamespace`\n- `AWS::ServiceDiscovery::PublicDnsNamespace`\n- `AWS::AmplifyUIBuilder::Component`\n- `AWS::AmplifyUIBuilder::Theme`\n- `AWS::EMRServerless::Application`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcouyang%2Fdhall-aws-cloudformation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcouyang%2Fdhall-aws-cloudformation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcouyang%2Fdhall-aws-cloudformation/lists"}