{"id":13431132,"url":"https://github.com/jakejscott/Humidifier","last_synced_at":"2025-03-16T06:32:26.114Z","repository":{"id":24082657,"uuid":"100584993","full_name":"jakejscott/Humidifier","owner":"jakejscott","description":"AWS Cloudformation using C#","archived":false,"fork":false,"pushed_at":"2022-12-08T00:51:42.000Z","size":3038,"stargazers_count":46,"open_issues_count":4,"forks_count":7,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-09T18:36:26.445Z","etag":null,"topics":["aws","cloudformation","dotnet"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jakejscott.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-17T09:14:20.000Z","updated_at":"2024-07-12T11:23:26.000Z","dependencies_parsed_at":"2022-09-15T07:12:15.062Z","dependency_job_id":null,"html_url":"https://github.com/jakejscott/Humidifier","commit_stats":null,"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakejscott%2FHumidifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakejscott%2FHumidifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakejscott%2FHumidifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakejscott%2FHumidifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakejscott","download_url":"https://codeload.github.com/jakejscott/Humidifier/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243835952,"owners_count":20355611,"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","dotnet"],"created_at":"2024-07-31T02:01:00.761Z","updated_at":"2025-03-16T06:32:25.303Z","avatar_url":"https://github.com/jakejscott.png","language":"C#","readme":"# Humidifier [![Build status](https://ci.appveyor.com/api/projects/status/qidmpegskc7tp020/branch/master?svg=true)](https://ci.appveyor.com/project/superlogical/humidifier/branch/master) [![NuGet](https://img.shields.io/nuget/v/Humidifier.svg)](https://www.nuget.org/packages/Humidifier/)\n\nHumidifier allows you to build AWS CloudFormation templates programmatically. Stacks and resources are represented as C# objects with accessors for all their supported properties.\n\nThe code is automatically generated by parsing the official Cloudformation [specification](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html).\n\n## Similar Projects\n- Humidifier (Ruby) https://github.com/localytics/humidifier\n- GoFormation (Go) https://github.com/awslabs/goformation\n\n\n## New feature: Project templates\n\nTo get up and running with Humidifier quickly, we've included a production ready template for deploying and managing your AWS Cloudformation/Serverless projects. The template is installed using the `dotnet new` cli command. Your can [view the code](/templates/Humidifier.Solution.Template) here.\n\nFeatures:\n\n- Cross platform deploy tool (Windows/Mac OSX/Linux) built using dotnet core.\n- Cloudformation stacks are defined in strongly typed C# using Humidifer of course.\n- A README that has a quickstart with commands generated for you to get up and running quickly.\n- Includes a simple Lambda function that can be deployed in seconds, configured with Serilog and Cloudwatch metrics.\n- Includes code for invoking the Lambda function, tailing the log and viewing the response.\n- Config using .env files and environment variables for CI/CD.\n- Secrets management using .aes files (encrypt/decrypt) and sync to parameter store.\n- Unit test project\n\nStep 1: Install the template\n\n```ps\ndotnet new -i Humidifier.Templates::*\n```\n\nThis will download the template from https://www.nuget.org/packages/Humidifier.Templates and install it into the dotnet cli template cache.\n\nStep 2: Check to see the template is installed\n\n```ps\ndotnet new --list\n````\n\nYou should see output similar to this:\n\n| Templates                                         | Short Name          | Language          | Tags\n| --------------------------------------------------| --------------------| ------------------| ---------------------\n| Humidifier Solution Template                      | humidifier.sln      | [C#]              | AWS/Lambda/Serverless\n| Console Application                               | console             | [C#], F#, VB      | Common/Console\n| Class library                                     | classlib            | [C#], F#, VB      | Common/Library\n| Unit Test Project                                 | mstest              | [C#], F#, VB      | Test/MSTest\n| xUnit Test Project                                | xunit               | [C#], F#, VB      | Test/xUnit\n| global.json file                                  | globaljson          |                   | Config\n| NuGet Config                                      | nugetconfig         |                   | Config\n| Web Config                                        | webconfig           |                   | Config\n| Solution File                                     | sln                 |                   | Solution\n\nStep 3: Create your project\n\n```ps\ndotnet new humidifier.sln --name Example --output Example --env test --region us-west-2 --stack example --profile default\n```\n\nThis will generate the solution in the `--output` folder. The `--profile` flag is your AWS credential profile. To setup a AWS profile, follow the docs on how to [configure the AWS Cli](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html) here.\n\nStep 3: Read the generated README.\n\nThe template generates a README that has a quickstart with all the commands for building, testing, deploying and destroying your stacks. If your reading this on Github, browse to the [template README](templates/Humidifier.Solution.Template/README.md) to see what's included.\n \n-----------------\n\n## Getting started\n\nNuget:\n\n```powershell\ndotnet add package Humidifier\ndotnet add package Humidifier.Json\n```\n\nStacks are represented by the Humidifier.Stack class. Resources are represented by an exact mapping from AWS resource names to Humidifier resources names (e.g. AWS::EC2::Instance becomes Humidifier.EC2.Instance). Resources have properties for each JSON attribute.\n\nThere's also a [demo application](https://github.com/jakejscott/Humidifier/blob/master/src/Humidifier.ConsoleTest/Program.cs) which creates a template and writes it [out to a file](https://github.com/jakejscott/Humidifier/blob/master/src/Humidifier.ConsoleTest/cloudformation.template) using JSON.\n\n### Example usage\n````csharp\nusing System.Collections.Generic;\nusing System.IO;\nusing Humidifier.Json;\n\nnamespace Humidifier.ConsoleTest\n{\n    public static class Program\n    {\n        public static void Main(string[] args)\n        {\n            Stack stack = BuildStack();\n\n            var serializer = new JsonStackSerializer();\n            var template = serializer.Serialize(stack);\n\n            File.WriteAllText(\"cloudformation.template\", template);\n        }\n\n        private static Stack BuildStack()\n        {\n            var stack = new Stack\n            {\n                AWSTemplateFormatVersion = \"2010-09-09\",\n                Description = \"Description\"\n            };\n\n            stack.Add(\"Environment\", new Parameter\n            {\n                Type = \"String\",\n                Description = \"Deployment environment\",\n                MinLength = 3,\n                MaxLength = 4,\n                AllowedValues = new List\u003cstring\u003e { \"test\", \"uat\", \"prod\" },\n                ConstraintDescription = \"Allowed values: [test, uat, prod]\"\n            });\n\n            stack.Add(\"AutomationStack\", new Parameter\n            {\n                Type = \"String\",\n                Description = \"Automation stack name\",\n                MinLength = 1,\n                MaxLength = 255,\n                AllowedPattern = \"^[a-zA-Z][-a-zA-Z0-9]*$\",\n                ConstraintDescription = \"Must be a valid Cloudformation Stack name\"\n            });\n\n            stack.Add(\"CodeS3Key\", new Parameter\n            {\n                Type = \"String\",\n                MinLength = 3\n            });\n\n            stack.Add(\"VPC\", new EC2.VPC\n            {\n                CidrBlock = \"10.0.0.0/16\",\n                EnableDnsSupport = false,\n                EnableDnsHostnames = false,\n                InstanceTenancy = \"dedicated\",\n                Tags = new List\u003cTag\u003e\n                {\n                    new Tag { Key = \"foo\", Value = \"bar\" }\n                }\n            });\n\n            stack.Add(\"Subnet\", new EC2.Subnet\n            {\n                VpcId = Fn.Ref(\"VPC\"),\n                CidrBlock = \"10.0.0.0/24\",\n                AvailabilityZone = Fn.Select(\"0\", Fn.GetAZs(Fn.Ref(\"AWS::Region\")))\n            });\n\n            stack.Add(\"Ec2Instance\", new EC2.Instance\n            {\n                ImageId = Fn.FindInMap(\"RegionMap\", Fn.Ref(\"AWS::Region\"), \"64\"),\n                InstanceType = \"m1.small\",\n                UserData = Fn.Base64(\n                    @\"#!/bin/bash -e\n                    wget https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_11.6.2-1.ubuntu.12.04_amd64.deb\n                    dpkg -i chef_11.6.2-1.ubuntu.12.04_amd64.deb\"\n                )\n            });\n\n            stack.Add(\"AutomationServiceRole\", new IAM.Role\n            {\n                AssumeRolePolicyDocument = new PolicyDocument\n                {\n                    Statement = new List\u003cStatement\u003e\n                    {\n                        new Statement\n                        {\n                            Effect = \"Allow\",\n                            Principal = new { Service = \"cloudformation.amazonaws.com\" },\n                            Action = \"sts:AssumeRole\"\n                        }\n                    }\n                }\n            });\n\n            stack.Add(\"DeploymentBucket\", new S3.Bucket { BucketName = Fn.Ref(\"AWS::StackName\") });\n            stack.Add(\"DeploymentBucketPolicy\", new S3.BucketPolicy\n            {\n                Bucket = Fn.Ref(\"DeploymentBucket\"),\n                PolicyDocument = new PolicyDocument\n                {\n                    Version = \"2012-10-17\",\n                    Statement = new List\u003cStatement\u003e\n                    {\n                        new Statement\n                        {\n                            Effect = \"Allow\",\n                            Principal = new\n                            {\n                               AWS = Fn.GetAtt(\"AutomationServiceRole\", IAM.Role.Attributes.Arn)\n                            },\n                            Action = \"s3:*\",\n                            Resource = new[]\n                            {\n                                Fn.Join(\"\", \"arn:aws:s3:::\", Fn.Ref(\"DeploymentBucket\")),\n                                Fn.Join(\"\", \"arn:aws:s3:::\", Fn.Ref(\"DeploymentBucket\"), \"/*\")\n                            }\n                        }\n                    }\n                }\n            });\n\n            stack.Add(\"LambdaFunction\", new Lambda.Function\n            {\n                Timeout = 30,\n                FunctionName = new { Ref = \"AWS::StackName\" },\n                Runtime = \"dotnetcore1.0\",\n                Description = \"\",\n                Handler = \"SomeProject::SomeProject.SomeFunction::FunctionHandler\",\n                MemorySize = 256,\n                Code = new Code\n                {\n                    S3Bucket = Fn.ImportValue(Fn.Sub(\"${AutomationStack}-DeploymentBucket\")),\n                    S3Key = new { Ref = \"CodeS3Key\" },\n                },\n                Environment = new Environment\n                {\n                    Variables = new Dictionary\u003cstring, dynamic\u003e\n                    {\n                        [\"EnvironmentName\"] = Fn.Ref(\"Environment\")\n                    }\n                },\n            });\n\n            stack.Add(\"MonitoringSnsTopic\", new SNS.Topic\n            {\n                DisplayName = Fn.Ref(\"AWS::StackName\"),\n                Subscription = new List\u003cSNS.Subscription\u003e\n                {\n                    new SNS.Subscription { Endpoint = \"team@example.com\", Protocol = \"email\" }\n                }\n            });\n\n            stack.Add(\"KmsKey\", new KMS.Key\n            {\n                Description = \"A Key\",\n                KeyPolicy = new PolicyDocument\n                {\n                    Id = \"key-default-1\",\n                    Version = \"2012-10-17\",\n                    Statement = new List\u003cStatement\u003e\n                    {\n                        new Statement\n                        {\n                            Sid = \"Allow the administration of the key\",\n                            Effect = \"Allows\",\n                            Principal = new {AWS = \"arn:aws:iam::123456789012:user/Alice\"},\n                            Action = new[]\n                            {\n                                \"kms:Create*\",\n                                \"kms:Describe*\",\n                                \"kms:Enable*\",\n                                \"kms:List*\",\n                                \"kms:Put*\",\n                                \"kms:Update*\",\n                                \"kms:Revoke*\",\n                                \"kms:Disable*\",\n                                \"kms:Get*\",\n                                \"kms:Delete*\",\n                                \"kms:ScheduleKeyDeletion\",\n                                \"kms:CancelKeyDeletion\"\n                            },\n                            Resource = \"*\"\n                        }\n                    }\n                }\n            });\n\n            var regionMap = new Mapping\n            {\n                [\"us-east-1\"] = new Dictionary\u003cstring, string\u003e { [\"32\"] = \"ami-6411e20d\", [\"64\"] = \"ami-7a11e213\" },\n                [\"us-west-1\"] = new Dictionary\u003cstring, string\u003e { [\"32\"] = \"ami-c9c7978c\", [\"64\"] = \"ami-cfc7978a\" },\n                [\"ue-west-1\"] = new Dictionary\u003cstring, string\u003e { [\"32\"] = \"ami-37c2f643\", [\"64\"] = \"ami-31c2f645\" },\n                [\"ap-southeast-1\"] = new Dictionary\u003cstring, string\u003e { [\"32\"] = \"ami-66f28c34\", [\"64\"] = \"ami-60f28c32\" },\n                [\"ap-northeast-1\"] = new Dictionary\u003cstring, string\u003e { [\"32\"] = \"ami-9c03a89d\", [\"64\"] = \"ami-a003a8a1\" }\n            };\n\n            stack.Mappings.Add(\"RegionMap\", regionMap);\n\n            return stack;\n        }\n    }\n}\n\n````\n\n### Functions\n\nYou can use CFN intrinsic functions and references using Fn.[name]. Those will build appropriate structures that know how to be dumped to CFN syntax appropriately.\n\n```csharp\nFn.FindInMap(\"RegionMap\", Fn.Ref(\"AWS::Region\"), \"64\");\n```\n\n```csharp\nFn.GetAtt(\"MyElb\", ElasticLoadBalancing.LoadBalancer.Attributes.DNSName);\n```\n\n```csharp\nFn.GetAZs(\"us-east-2\");\n```\n\n```csharp\nFn.ImportValue(Fn.Sub(\"${NetworkStackNameParameter}-SubnetID\"));\n```\n\n```csharp\nFn.Join(\"\", \"arn:aws:s3:::\", Fn.Ref(\"DeployBucket\"), \"/*\");\n```\n\n```csharp\nFn.Ref(\"BucketName\");\n```\n\n```csharp\nFn.Select(\"1\", new[] { \"a\", \"b\", \"c\" });\n```\n\n```csharp\nFn.Split(\"|\", \"a|b|c\");\n```\n\n```csharp\nFn.Sub(\"${AWS::StackName}-${AWS::Region}-bucket\");\n```\n\n```csharp\nFn.Select(\"1\", Fn.Split(\"|\", \"a|b|c\"));\n```\n\nNOTE: Because JSON doesn't allow newlines, there's a known hack where you can join multiple lines together using Fn::Join\n\n```csharp\nFn.Base64(Fn.Join(\"\",\n  \"#!/bin/bash -e\\n\",\n  \"wget https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_11.6.2-1.ubuntu.12.04_amd64.deb\\n\",\n  \"dpkg -i chef_11.6.2-1.ubuntu.12.04_amd64.deb\\n\"\n));\n```\n\nBut that's gross and unreadable when outputted as JSON. Instead use a multiline C# string, and let the library take care \nof encoding it for you:\n\n - Whitespace on the start of the line is trimmed, which means you can indent your code nicely (Like you can in YAML).\n - Newlines are encoded as `\\r\\n` automatically by NewtonSoft.Json.\n\n```csharp\nFn.Base64(\n  @\"#!/bin/bash -e\n  wget https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_11.6.2-1.ubuntu.12.04_amd64.deb\n  dpkg -i chef_11.6.2-1.ubuntu.12.04_amd64.deb\"\n);\n````\n\n### Conditions\n\n````csharp\nstack.Add(\"CreateProdResources\", new Condition(Fn.Equals(Fn.Ref(\"Environment\"), \"prod\")));\n````\n\n````csharp\nstack.Add(\"CreateDevResources\", new Condition(Fn.Equals(Fn.Ref(\"Environment\"), \"dev\")));\n````\n\n````csharp\nstack.Add(\"NotCondition\", new Condition(Fn.Not(Fn.Equals(Fn.Ref(\"Environment\"), \"prod\"))));\n````\n\n````csharp\nstack.Add(\"AndCondition\", \n    new Condition(\n        Fn.And(\n            Fn.Equals(\"sg-mysqgroup\", Fn.Ref(\"SecurityGroup\")),\n            new { Condition = \"NotCondition\" }\n        )\n    )\n);\n````\n\n````csharp\nstack.Add(\"OrCondition\",\n    new Condition(\n        Fn.Or(\n            Fn.Equals(\"sg-mysqgroup\", Fn.Ref(\"SecurityGroup\")),\n            new { Condition = \"NotCondition\" }\n        )\n    )\n);\n````\n\nTo specify a condition on a resource use the overload to `stack.Add` and pass in the `condition` parameter.\n\n````csharp\nstack.Add(\"Volume\", new EC2.Volume\n{\n    Size = 100,\n    AvailabilityZone = Fn.GetAtt(\"Ec2Instance\", EC2.Instance.Attributes.AvailabilityZone)\n},\ncondition: \"CreateProdResources\");\n````","funding_links":[],"categories":["Frameworks, Libraries and Tools","框架, 库和工具","Misc"],"sub_categories":["Misc","大杂烩"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakejscott%2FHumidifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakejscott%2FHumidifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakejscott%2FHumidifier/lists"}