val template = KloudFormationTemplate.create {}
val template = KloudFormationTemplate.create {
val email = parameter<String>("EmailAddress")
}
// Result:
// AWSTemplateFormatVersion: "2010-09-09"
// Parameters:
// EmailAddress:
// Type: "String"
Pseudo parameters can be used anywhere a reference can be.
They exist within the KloudFormationTemplate.Builder
type and are available within the create
braces
Here is the full list of available pseudo parameters:
val awsAccountId = Reference<String>("AWS::AccountId")
val awsNotificationArns = Reference<List<String>>("AWS::NotificationARNs")
fun <T> awsNoValue() = Reference<T>("AWS::NoValue")
val awsPartition = Reference<String>("AWS::Partition")
val awsRegion = Reference<String>("AWS::Region")
val awsStackId = Reference<String>("AWS::StackId")
val awsStackName = Reference<String>("AWS::StackName")
val awsUrlSuffix = Reference<String>("AWS::URLSuffix")
val template = KloudFormationTemplate.create {
topic()
}
// Result:
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// Topic:
// Type: "AWS::SNS::Topic"
To get a reference to any resource or parameter simply invoke the ref()
function
val template = KloudFormationTemplate.create {
val email = parameter<String>("EmailAddress")
val topic = topic()
subscription {
topicArn(topic.ref())
endpoint(email.ref())
protocol("email")
}
}
// Result:
// AWSTemplateFormatVersion: "2010-09-09"
// Parameters:
// EmailAddress:
// Type: "String"
// Resources:
// Topic:
// Type: "AWS::SNS::Topic"
// Subscription:
// Type: "AWS::SNS::Subscription"
// Properties:
// Endpoint:
// Ref: "EmailAddress"
// Protocol: "email"
// TopicArn:
// Ref: "Topic"
Required properties appear in the resources building function itself, like cidrBlock in the following example. Other properties can be set in the builder function passed to resource builder and are all exposed as functions themselves. (see enableDnsHostnames)
val template = KloudFormationTemplate.create{
vPC(cidrBlock = +"0.0.0.0/0"){
enableDnsHostnames(true)
}
}
// Result:
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// VPC:
// Type: "AWS::EC2::VPC"
// Properties:
// CidrBlock: "0.0.0.0/0"
// EnableDnsHostnames: true
Each class representing an AWS Resource contains a list of available attributes. Attributes can be used anywhere a reference can be used or a primitive type. The following example shows how a bucket could be named with the name of a topic
val template = KloudFormationTemplate.create{
val topic = topic()
bucket {
bucketName(topic.TopicName())
}
}
// Result:
// AWSTemplateFormatVersion: "2010-09-09"
// Description: ""
// Resources:
// Topic:
// Type: "AWS::SNS::Topic"
// Bucket:
// Type: "AWS::S3::Bucket"
// Properties:
// BucketName:
// Fn::GetAtt:
// - "Topic"
// - "TopicName"
Where primitive types, like String are required the actual value can either be the primitive type itself, a reference, an attribute or a join.
Each type can be combined using the plus operator to create a join. The following example is the same as the above example except it adds the string Bucket
to the value returned from getting the topics name at runtime.
val template = KloudFormationTemplate.create{
val topic = topic()
bucket {
bucketName(topic.TopicName() + "Bucket")
}
}
// Result:
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// Topic:
// Type: "AWS::SNS::Topic"
// Bucket:
// Type: "AWS::S3::Bucket"
// Properties:
// BucketName:
// Fn::Join:
// - ""
// - - Fn::GetAtt:
// - "Topic"
// - "TopicName"
// - "Bucket"
Some sections in a cloudformation template take raw JSON. Anywhere that takes JSON you can use the json
function to pass a map.
Alternatively, if a policy document is needed there are specially designed objects to build those up, shown below.
AWS simply state that certain resources are of the type JsonNode. To remedy this we have wrapped that in our Value type so that our PolicyDocument type can be used instead. PolicyDocument lets you build fully type safe iam policies as shown in the example below.
val template = KloudFormationTemplate.create{
val topic = topic(logicalName = "NotificationTopic") // Provide custom logical names
role(
assumeRolePolicyDocument = policyDocument {
statement(action("sts:AssumeRole")) {
principal(PrincipalType.SERVICE, +"lambda.amazonaws.com")
}
}
){
policies(listOf(
Policy(
policyDocument {
statement(
action("sns:*"),
IamPolicyEffect.Allow,
resource(topic.ref())
) {
principal(PrincipalType.AWS, +"Some AWS Principal")
condition(ConditionOperators.stringEquals, ConditionKeys.awsUserName, listOf("brian"))
}
},
policyName = +"LambdaSnsPolicy"
)
))
}
}
// Result:
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// NotificationTopic:
// Type: "AWS::SNS::Topic"
// Role:
// Type: "AWS::IAM::Role"
// Properties:
// AssumeRolePolicyDocument:
// Statement:
// - Effect: "Allow"
// Action:
// - "sts:AssumeRole"
// Principal:
// Service: "lambda.amazonaws.com"
// Policies:
// - PolicyDocument:
// Statement:
// - Effect: "Allow"
// Action:
// - "sns:*"
// Resource:
// - Ref: "NotificationTopic"
// Principal:
// AWS: "Some AWS Principal"
// Condition:
// StringEquals:
// aws:username:
// - "brian"
// PolicyName: "LambdaSnsPolicy"
val template = KloudFormationTemplate.create {
topic().also { myTopic ->
subscription {
topicArn(myTopic.ref())
}
}
}
Each builder function takes a dependsOn
parameter that can be used to set a dependency as follows
val topic = topic()
val q = queue(dependsOn = listOf(topic.logicalName))
Alternatively you can chain resources using the then
function as follows
topic().then{ queue() }
You can also combine resources for multi dependant relationships as follows
(topic() and topic()).then{ topic() }
// Result
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// Topic:
// Type: "AWS::SNS::Topic"
// Topic2:
// Type: "AWS::SNS::Topic"
// Topic3:
// Type: "AWS::SNS::Topic"
// DependsOn:
// - "Topic"
// - "Topic2"
There is a function exposed when inside the create
braces called mappings
that allows you to create the mappings section within your template.
It takes a three level nested map. The following example shows how you might create an ec2 instance based on the region your stack is being created in.
Note here that the awsRegion
is one of the Pseudo Parameters (see above for full list)
val template = KloudFormationTemplate.create {
val regions = "RegionMap"
val usEast1 = "us-east-1"
val euWest1 = "eu-west-1"
val bits32 = "32"
mappings(
regions to mapOf(
usEast1 to mapOf( bits32 to +"ami-6411e20d"),
euWest1 to mapOf( bits32 to +"ami-37c2f643")
)
)
instance{
instanceType("m1.small")
imageId(FindInMap(+regions, awsRegion, +bits32))
}
}
// Result
// AWSTemplateFormatVersion: "2010-09-09"
// Mappings:
// RegionMap:
// us-east-1:
// 32: "ami-6411e20d"
// eu-west-1:
// 32: "ami-37c2f643"
// Resources:
// Instance:
// Type: "AWS::EC2::Instance"
// Properties:
// ImageId:
// Fn::FindInMap:
// - "RegionMap"
// - Ref: "AWS::Region"
// - "32"
// InstanceType: "m1.small"
There is a function exposed when inside the create
braces called conditions
that allows you to create the conditions section within your template.
Here is an example of how to create resources only in production based on an environment variable that is passed in.
val template = KloudFormationTemplate.create {
val environment = parameter<String>(
logicalName = "Environment",
description = "AWS Environmnet",
default = "nonprod",
allowedValues = listOf("nonprod", "prod")
)
val inProduction = "InProduction"
conditions(
inProduction to (environment.ref() eq +"prod")
)
instance(condition = inProduction){
instanceType("m1.small")
}
}
// Result
// AWSTemplateFormatVersion: "2010-09-09"
// Parameters:
// Environment:
// Type: "String"
// AllowedValues:
// - "nonprod"
// - "prod"
// Default: "nonprod"
// Description: "AWS Environmnet"
// Conditions:
// InProduction:
// Fn::Equals:
// - Ref: "Environment"
// - "prod"
// Resources:
// Instance:
// Type: "AWS::EC2::Instance"
// Condition: "InProduction"
// Properties:
// InstanceType: "m1.small"
Within the conditions section shown above within a template you will have full access to any of the conditional functions shown below.
val envIsProd = environment.ref() eq +"prod"
val envIsDevOrTest = (environment.ref() eq +"dev") or (environment.ref() eq +"test")
val envIsProdAndHuge = (environment.ref() eq +"prod") and (size.ref() eq +"huge")
val envIsNotProd = not(environment.ref() eq +"prod")
Once a Condition is set up you can use the if function to pick a value based on the condition being true or false
In the following example an ec2 instance will be created. If in prod it will be an m1.large but in dev it will be a t2.micro
val template = KloudFormationTemplate.create {
val environment = parameter<String>(
logicalName = "Environment",
description = "AWS Environmnet",
default = "nonprod",
allowedValues = listOf("nonprod", "prod")
)
val inProduction = "InProduction"
conditions(
inProduction to (environment.ref() eq +"prod")
)
instance{
instanceType(If(inProduction, +"m1.large", +"t2.micro"))
}
}
// Result
// AWSTemplateFormatVersion: "2010-09-09"
// Parameters:
// Environment:
// Type: "String"
// AllowedValues:
// - "nonprod"
// - "prod"
// Default: "nonprod"
// Description: "AWS Environmnet"
// Conditions:
// InProduction:
// Fn::Equals:
// - Ref: "Environment"
// - "prod"
// Resources:
// Instance:
// Type: "AWS::EC2::Instance"
// Properties:
// InstanceType:
// Fn::If:
// - "InProduction"
// - "m1.large"
// - "t2.micro"
There are two types of custom resources: AWS::CloudFormation::CustomResource
and Custom::<some string>
.
Both types can be made with the customResource function provided. In order to change the type to Custom::<some string>
call a method named asCustomResource
after building.
Shown Below:
val template = KloudFormationTemplate.create {
val standardCustomResource = customResource(
logicalName = "DatabaseInitializer",
serviceToken = +"arn:aws::xxxx:xxx",
metadata = json(mapOf(
"SomeKey" to "SomeValue"
))
)
val customNameCustomResource = customResource(
logicalName = "DatabaseInitializer2",
serviceToken = +"arn:aws::xxxx:xxx",
metadata = json(mapOf(
"SomeKey" to "SomeValue"
))
).asCustomResource("DBInit")
}
// Result
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// DatabaseInitializer:
// Type: "AWS::CloudFormation::CustomResource"
// Metadata:
// SomeKey: "SomeValue"
// Properties:
// ServiceToken: "arn:aws::xxxx:xxx"
// DatabaseInitializer2:
// Type: "Custom::DBInit"
// Metadata:
// SomeKey: "SomeValue"
// Properties:
// ServiceToken: "arn:aws::xxxx:xxx"
val template = KloudFormationTemplate.create {
autoScalingGroup(minSize = +"1", maxSize = +"4"){
availabilityZones(GetAZs(+""))
}
}
// Result
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// AutoScalingGroup:
// Type: "AWS::AutoScaling::AutoScalingGroup"
// Properties:
// MaxSize: "4"
// MinSize: "1"
// AvailabilityZones:
// Fn::GetAZs:
// - ""
val template = KloudFormationTemplate.create {
alias(
functionName = lambda.ref(),
functionVersion = lambdaVersion2.Version(),
name = +"MyAlias",
updatePolicy = UpdatePolicy(
codeDeployLambdaAliasUpdate = CodeDeployLambdaAliasUpdate(
applicationName = application.ref(),
deploymentGroupName = deploymentGroup.ref()
)
)
)
}
// Result
// Alias:
// Type: "AWS::Lambda::Alias"
// UpdatePolicy:
// CodeDeployLambdaAliasUpdate:
// ApplicationName:
// Ref: "Topic"
// DeploymentGroupName:
// Ref: "Topic3"
// Properties:
// FunctionName:
// Ref: "Topic2"
// FunctionVersion:
// Fn::GetAtt:
// - "Version"
// - "Version"
// Name: "MyAlias"
val template = KloudFormationTemplate.create {
bucket(deletionPolicy = DeletionPolicy.RETAIN.policy)
}
// Result
// AWSTemplateFormatVersion: "2010-09-09"
// Resources:
// Bucket:
// Type: "AWS::S3::Bucket"
// DeletionPolicy: "Retain"