RonSijm.Humidifier
0.2.0-182.0.0
Prefix Reserved
See the version list below for details.
dotnet add package RonSijm.Humidifier --version 0.2.0-182.0.0
NuGet\Install-Package RonSijm.Humidifier -Version 0.2.0-182.0.0
<PackageReference Include="RonSijm.Humidifier" Version="0.2.0-182.0.0" />
paket add RonSijm.Humidifier --version 0.2.0-182.0.0
#r "nuget: RonSijm.Humidifier, 0.2.0-182.0.0"
// Install RonSijm.Humidifier as a Cake Addin #addin nuget:?package=RonSijm.Humidifier&version=0.2.0-182.0.0&prerelease // Install RonSijm.Humidifier as a Cake Tool #tool nuget:?package=RonSijm.Humidifier&version=0.2.0-182.0.0&prerelease
Fork note
Q: What is this fork?
A: The original project hasn't been updated in 4 years, so I'm not really sure if it's still maintained. And since it was created 4 years ago, a lot has changed in CloudFormation. And I wanted to use this for my CloudFormation...
Luckily the original author Jake Scott probably anticipated that a lot would change, and made a generator to update everything.
This Fork runs the generator daily, and publishes a new nuget if there is an update available.
Q: Why did you prefix the fork with RonSijm.
A: I'm publishing a NuGet so other people can use it, I cannot use the exact same name (Humidifier) in Nuget.
Versioning syntax:
x.x-{aws-spec-version}
For compatibility reasons I haven't updated any or the original namespaces or whatever, so this fork should be compatible with the original version as substitute
What's next?
2 - I might try to generate a bunch of interfaces for classes, for example an IHaveTags
interface - or an IHaveEncryption
(KMS) - So I'll be easier to generically weave in settings to things that support it.
</end of Fork Note, original Readme starts below>
Humidifier
Humidifier allows you to build AWS CloudFormation templates programmatically. Stacks and resources are represented as C# objects with accessors for all their supported properties.
The code is automatically generated by parsing the official Cloudformation specification.
Similar Projects
- Humidifier (Ruby) https://github.com/localytics/humidifier
- GoFormation (Go) https://github.com/awslabs/goformation
New feature: Project templates
To 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 here.
Features:
- Cross platform deploy tool (Windows/Mac OSX/Linux) built using dotnet core.
- Cloudformation stacks are defined in strongly typed C# using Humidifer of course.
- A README that has a quickstart with commands generated for you to get up and running quickly.
- Includes a simple Lambda function that can be deployed in seconds, configured with Serilog and Cloudwatch metrics.
- Includes code for invoking the Lambda function, tailing the log and viewing the response.
- Config using .env files and environment variables for CI/CD.
- Secrets management using .aes files (encrypt/decrypt) and sync to parameter store.
- Unit test project
Step 1: Install the template
dotnet new -i Humidifier.Templates::*
This will download the template from https://www.nuget.org/packages/Humidifier.Templates and install it into the dotnet cli template cache.
Step 2: Check to see the template is installed
dotnet new --list
You should see output similar to this:
Templates | Short Name | Language | Tags |
---|---|---|---|
Humidifier Solution Template | humidifier.sln | [C#] | AWS/Lambda/Serverless |
Console Application | console | [C#], F#, VB | Common/Console |
Class library | classlib | [C#], F#, VB | Common/Library |
Unit Test Project | mstest | [C#], F#, VB | Test/MSTest |
xUnit Test Project | xunit | [C#], F#, VB | Test/xUnit |
global.json file | globaljson | Config | |
NuGet Config | nugetconfig | Config | |
Web Config | webconfig | Config | |
Solution File | sln | Solution |
Step 3: Create your project
dotnet new humidifier.sln --name Example --output Example --env test --region us-west-2 --stack example --profile default
This 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 here.
Step 3: Read the generated README.
The 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 to see what's included.
Getting started
Nuget:
dotnet add package Humidifier
dotnet add package Humidifier.Json
Stacks 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.
There's also a demo application which creates a template and writes it out to a file using JSON.
Example usage
using System.Collections.Generic;
using System.IO;
using Humidifier.Json;
namespace Humidifier.ConsoleTest
{
public static class Program
{
public static void Main(string[] args)
{
Stack stack = BuildStack();
var serializer = new JsonStackSerializer();
var template = serializer.Serialize(stack);
File.WriteAllText("cloudformation.template", template);
}
private static Stack BuildStack()
{
var stack = new Stack
{
AWSTemplateFormatVersion = "2010-09-09",
Description = "Description"
};
stack.Add("Environment", new Parameter
{
Type = "String",
Description = "Deployment environment",
MinLength = 3,
MaxLength = 4,
AllowedValues = new List<string> { "test", "uat", "prod" },
ConstraintDescription = "Allowed values: [test, uat, prod]"
});
stack.Add("AutomationStack", new Parameter
{
Type = "String",
Description = "Automation stack name",
MinLength = 1,
MaxLength = 255,
AllowedPattern = "^[a-zA-Z][-a-zA-Z0-9]*$",
ConstraintDescription = "Must be a valid Cloudformation Stack name"
});
stack.Add("CodeS3Key", new Parameter
{
Type = "String",
MinLength = 3
});
stack.Add("VPC", new EC2.VPC
{
CidrBlock = "10.0.0.0/16",
EnableDnsSupport = false,
EnableDnsHostnames = false,
InstanceTenancy = "dedicated",
Tags = new List<Tag>
{
new Tag { Key = "foo", Value = "bar" }
}
});
stack.Add("Subnet", new EC2.Subnet
{
VpcId = Fn.Ref("VPC"),
CidrBlock = "10.0.0.0/24",
AvailabilityZone = Fn.Select("0", Fn.GetAZs(Fn.Ref("AWS::Region")))
});
stack.Add("Ec2Instance", new EC2.Instance
{
ImageId = Fn.FindInMap("RegionMap", Fn.Ref("AWS::Region"), "64"),
InstanceType = "m1.small",
UserData = Fn.Base64(
@"#!/bin/bash -e
wget https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_11.6.2-1.ubuntu.12.04_amd64.deb
dpkg -i chef_11.6.2-1.ubuntu.12.04_amd64.deb"
)
});
stack.Add("AutomationServiceRole", new IAM.Role
{
AssumeRolePolicyDocument = new PolicyDocument
{
Statement = new List<Statement>
{
new Statement
{
Effect = "Allow",
Principal = new { Service = "cloudformation.amazonaws.com" },
Action = "sts:AssumeRole"
}
}
}
});
stack.Add("DeploymentBucket", new S3.Bucket { BucketName = Fn.Ref("AWS::StackName") });
stack.Add("DeploymentBucketPolicy", new S3.BucketPolicy
{
Bucket = Fn.Ref("DeploymentBucket"),
PolicyDocument = new PolicyDocument
{
Version = "2012-10-17",
Statement = new List<Statement>
{
new Statement
{
Effect = "Allow",
Principal = new
{
AWS = Fn.GetAtt("AutomationServiceRole", IAM.Role.Attributes.Arn)
},
Action = "s3:*",
Resource = new[]
{
Fn.Join("", "arn:aws:s3:::", Fn.Ref("DeploymentBucket")),
Fn.Join("", "arn:aws:s3:::", Fn.Ref("DeploymentBucket"), "/*")
}
}
}
}
});
stack.Add("LambdaFunction", new Lambda.Function
{
Timeout = 30,
FunctionName = new { Ref = "AWS::StackName" },
Runtime = "dotnetcore1.0",
Description = "",
Handler = "SomeProject::SomeProject.SomeFunction::FunctionHandler",
MemorySize = 256,
Code = new Code
{
S3Bucket = Fn.ImportValue(Fn.Sub("${AutomationStack}-DeploymentBucket")),
S3Key = new { Ref = "CodeS3Key" },
},
Environment = new Environment
{
Variables = new Dictionary<string, dynamic>
{
["EnvironmentName"] = Fn.Ref("Environment")
}
},
});
stack.Add("MonitoringSnsTopic", new SNS.Topic
{
DisplayName = Fn.Ref("AWS::StackName"),
Subscription = new List<SNS.Subscription>
{
new SNS.Subscription { Endpoint = "team@example.com", Protocol = "email" }
}
});
stack.Add("KmsKey", new KMS.Key
{
Description = "A Key",
KeyPolicy = new PolicyDocument
{
Id = "key-default-1",
Version = "2012-10-17",
Statement = new List<Statement>
{
new Statement
{
Sid = "Allow the administration of the key",
Effect = "Allows",
Principal = new {AWS = "arn:aws:iam::123456789012:user/Alice"},
Action = new[]
{
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
},
Resource = "*"
}
}
}
});
var regionMap = new Mapping
{
["us-east-1"] = new Dictionary<string, string> { ["32"] = "ami-6411e20d", ["64"] = "ami-7a11e213" },
["us-west-1"] = new Dictionary<string, string> { ["32"] = "ami-c9c7978c", ["64"] = "ami-cfc7978a" },
["ue-west-1"] = new Dictionary<string, string> { ["32"] = "ami-37c2f643", ["64"] = "ami-31c2f645" },
["ap-southeast-1"] = new Dictionary<string, string> { ["32"] = "ami-66f28c34", ["64"] = "ami-60f28c32" },
["ap-northeast-1"] = new Dictionary<string, string> { ["32"] = "ami-9c03a89d", ["64"] = "ami-a003a8a1" }
};
stack.Mappings.Add("RegionMap", regionMap);
return stack;
}
}
}
Functions
You 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.
Fn.FindInMap("RegionMap", Fn.Ref("AWS::Region"), "64");
Fn.GetAtt("MyElb", ElasticLoadBalancing.LoadBalancer.Attributes.DNSName);
Fn.GetAZs("us-east-2");
Fn.ImportValue(Fn.Sub("${NetworkStackNameParameter}-SubnetID"));
Fn.Join("", "arn:aws:s3:::", Fn.Ref("DeployBucket"), "/*");
Fn.Ref("BucketName");
Fn.Select("1", new[] { "a", "b", "c" });
Fn.Split("|", "a|b|c");
Fn.Sub("${AWS::StackName}-${AWS::Region}-bucket");
Fn.Select("1", Fn.Split("|", "a|b|c"));
NOTE: Because JSON doesn't allow newlines, there's a known hack where you can join multiple lines together using Fn::Join
Fn.Base64(Fn.Join("",
"#!/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"
));
But that's gross and unreadable when outputted as JSON. Instead use a multiline C# string, and let the library take care of encoding it for you:
- Whitespace on the start of the line is trimmed, which means you can indent your code nicely (Like you can in YAML).
- Newlines are encoded as
\r\n
automatically by NewtonSoft.Json.
Fn.Base64(
@"#!/bin/bash -e
wget https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_11.6.2-1.ubuntu.12.04_amd64.deb
dpkg -i chef_11.6.2-1.ubuntu.12.04_amd64.deb"
);
Conditions
stack.Add("CreateProdResources", new Condition(Fn.Equals(Fn.Ref("Environment"), "prod")));
stack.Add("CreateDevResources", new Condition(Fn.Equals(Fn.Ref("Environment"), "dev")));
stack.Add("NotCondition", new Condition(Fn.Not(Fn.Equals(Fn.Ref("Environment"), "prod"))));
stack.Add("AndCondition",
new Condition(
Fn.And(
Fn.Equals("sg-mysqgroup", Fn.Ref("SecurityGroup")),
new { Condition = "NotCondition" }
)
)
);
stack.Add("OrCondition",
new Condition(
Fn.Or(
Fn.Equals("sg-mysqgroup", Fn.Ref("SecurityGroup")),
new { Condition = "NotCondition" }
)
)
);
To specify a condition on a resource use the overload to stack.Add
and pass in the condition
parameter.
stack.Add("Volume", new EC2.Volume
{
Size = 100,
AvailabilityZone = Fn.GetAtt("Ec2Instance", EC2.Instance.Attributes.AvailabilityZone)
},
condition: "CreateProdResources");
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net6.0
- Microsoft.CSharp (>= 4.7.0)
-
net7.0
- Microsoft.CSharp (>= 4.7.0)
-
net8.0
- Microsoft.CSharp (>= 4.7.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on RonSijm.Humidifier:
Package | Downloads |
---|---|
RonSijm.Humidifier.Json
Json Serialization support for Humidifier. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.2.18600 | 100 | 10/29/2024 |
0.2.18500 | 124 | 9/7/2024 |
0.2.18400 | 117 | 9/2/2024 |
0.2.18300 | 95 | 8/3/2024 |
0.2.18000 | 138 | 7/22/2024 |
0.2.0-183.0.0 | 43 | 8/2/2024 |
0.2.0-182.0.0 | 54 | 7/26/2024 |
0.2.0-180.0.0 | 64 | 7/21/2024 |
0.1.0-180.0.0 | 57 | 7/20/2024 |
0.1.0-170.0.0 | 56 | 7/20/2024 |
0.0.1 | 128 | 7/20/2024 |