This project offers a Kotlin DSL for AWS cloudformation. Kotlin brings the advantages of type safety, IDE autocomplete and the ability to embed code within templates. This is a work in progress - please provide feedback!
cd cloudformation2kotlin gradle publishToMavenLocal
This will take the aws json schema definitions (copied from https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-resource-specification.html) convert them to kotlin and bundle them into a maven artifact along with some helper code. It also includes some classes create manually because I cant find a schema definition for them e.g. IamPolicy and serverless resources such as AWS::Serverless::Function. There are also some properties that are mapped to Any? because I havent mapped the schema manually yet.
In build.gradle.kts
repositories {
mavenCentral()
}
dependencies {
compile("com.typedpath:cloudformation2kotlin:2.0.0")
}
in pom.xml
<dependency>
<groupId>com.typedpath</groupId>
<artifactId>cloudformation2kotlin</artifactId>
<version>2.0.0</version>
</dependency>
make sure the default aws user in ~/.aws has admin permissions!
cd cloudformationsamples gradle clean build -x test gradle build
This will run the junit tests, creating cloud cloudformation stacks from (kotlin) templates and testing the resources in the created stacks. These are the tests (the xxTemplate.kt files are the source files, the xxxTemplate.yaml files are generated ):
Test | Template | Test Description |
---|---|---|
S3Test.kt | S3PublicReadableCloudFormationTemplate.kt S3PublicReadableCloudFormationTemplate.yaml | write to s3 bucket in stack , read from s3 bucket |
LambdaTest.kt | LambdaCloudFormationTemplate.kt LambdaCloudFormationTemplate.yaml | call lambda in stack |
UnzipFunctionTest.kt | UnzipS3FunctionTemplate.kt UnzipS3FunctionTemplate.yaml | create a stack with an s3 bucket and an unzipping lambda function, upload a zip file and unzip it with the lambda function |
PipelineTest.kt | PipelineCloudFormationTemplate.kt PipelineCloudFormationTemplate.yaml | create stack with 4 stage pipeline + code repository, checkin code and test lambda created by pipeline |
LambdaServerlessTest.kt | LambdaServerlessTemplate.kt LambdaServerlessTemplate.yaml | create a SAM stack containing a lambda and call the lambda |
ServerlessBackendApiTest.kt | ServerlessBackendApiTemplate.kt ServerlessBackendApiTemplate.yaml, ServerlessBackendApiRefactoredTemplate.kt ( templates functionally equivalent) | create a SAM stack implementing a REST api with API gateway, lambda functions and dynamo db. Test with http put, get and d)lete calls |
S3ObjectCreatedEventTest.kt | S3ObjectCreatedEventTemplate.kt S3ObjectCreatedEventTemplate.yaml | create a SAM stack containing an s3 bucket with lambda triggered by object create events. The lambda code is in a seperate kotlin project "lambdas/s3objectcreated" and adds a tag on creation. Write to the s3 bucket and check the tag has been created. |
AuroraServerlessTest.kt | AuroraServerlessTemplate.kt AuroraServerlessTemplate.yaml | create a stack containing an aurora serverless rds cluster. Using rds data client api create a database, table insert a row and read the row back |
AuroraBackendApiTest.kt | AuroraBackendApiTemplate.kt AuroraBackendApiTemplate.yaml | Create a stack containing an aurora serverless rds cluster fronted by a REST api providing a generic typesafe CRUD service over a relational database. More details here |
The examples listed above create templates by extension e.g.:
class S3PublicReadableCloudFormationTemplate(bucketName: String) : CloudFormationTemplate() {...
templates declared in this way will have a cloudformation resource for every property that extends com.typedpath.awscloudformation.Resource. Properties in superclasses will be included. In the case of S3PublicReadableCloudFormationTemplate.kt these 2 property declarations map to an s3 bucket and a s3 bucket policy in the stack:
val s3Bucket = AWS_S3_Bucket() . . . .
val s3BucketPolicy = AWS_S3_BucketPolicy(ref(s3Bucket), policyDocument)
val db = AWS_RDS_DBCluster("aurora" /* mandatory fields!*/) {
// optional fields
engineMode="serverless"
. . . .
Templates are mapped to yaml with the toYaml function e.g.:
val strTemplate = toYaml(S3PublicReadableCloudFormationTemplate(bucketName))
In this example this will give:
AWSTemplateFormatVersion: '2010-09-09'
Resources:
s3Bucket:
Type: 'AWS::S3::Bucket'
Properties:
. . . .
s3BucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref s3Bucket
PolicyDocument:
Statement:
- Action:
- 's3:GetObject'
Effect: Allow
db:
Type: 'AWS::RDS::DBCluster'
Properties:
EngineMode: 'serverless'
. . . .
Note the use of helper function ref(s3Bucket)) - this makes it difficult to create an invalid cloudformation reference. Note also that all mandatory fields are in the constructor and optional fields can be specified in the optional init block.
The property definition for policyDocument is:
val policyDocument = IamPolicy {
statement {
effect = IamPolicy.EffectType.Allow
principal = mapOf(
Pair(IamPolicy.PrincipalType.AWS, listOf("*"))
)
action(S3Action.GetObject)
resource +=join("", listOf("arn:aws:s3:::", ref(s3Bucket), "/*"))
}
}
The constructor for IamPolicy has no arguments because there are no mandatory properties. Non mandatory properties are specified in the init block. Note also that policyDocument does not appear in the top level of the yaml template, it is inlined where it is referenced. This is because it is not a cloud formation resource i.e. not an instance of com.typedpath.awscloudformation.Resource. Class IamPolicy was created manually (unlike Kotlin resource definiton classes) because I couldnt find an amazon supplied schema (json or otherwise) for it.