# Noctilucent Transpiler v1 The goal of noctilucent is to take any template and transform it completely to a working CDK application in typescript. The remainder of this document is focusing on how that will be done. ## Breakdown of a CDK Typescript Class in Noctilucent Let's look at a simple cdk application: ``` import * as core from '@aws-cdk/core'; import * as ec2 from '@aws-cdk/ec2'; public interface BasicStackProps extends core.StackProps{ readonly AmiId: string; } export class BasicStack extends core.Stack { constructor(scope: core.App, id: string, props?: BasicStackProps) { super(scope, id, props); const vpc = new ec2.Vpc(this, "MyVpc", { maxAzs: 3 // Default is all AZs in region }); } } ``` Each stack file *can* be modelled off a template. Breaking down you have: ``` import * as core from '@aws-cdk/core'; import * as ec2 from '@aws-cdk/ec2'; public interface BasicStackProps extends core.StackProps{ readonly AmiId: string; } export class NAME extends core.Stack { constructor(scope: core.App, id: string, props?: PROPNAME) { super(scope, id, props; } const vpc = new ec2.Vpc(this, "MyVpc", { maxAzs: 3 // Default is all AZs in region }); } ``` ### Static Imports As part of the core template of a stack, there will always be classes that need importing, such as the core libraries. These don't need to be programmatically thought about, just add them at will. ### Resource Imports In cloudformation, every resource in your yaml has a type association. This section is identifying all your resource types and importing their names. ### Parameter Usage If you think of a cloudformation template as a giant "function", Parameters are the inputs into the function. If you have a single parameter called AmiId in your cloudformation template, then you need a prop that corresponds to that in your properties. There is an argument to be had about retaining parameters in the stack using `new CfnParameter` instead of props. Generally, a secondary goal of this project is to create *best practices* for the output CDK Class. CDK doesn't really `like` parameters, preferring properties instead. Until the need arises, we will not create CfnParameters in v1 because of this. ### Preconditions Conditions are booleans for later use in the template. Preconditions is the equivalent `Conditions` block, where every name of a condition will correspond with a variable that is a boolean. As an example, in one template, there is a Condition named `IsMxp`, which is an `Fn::Equal` of "eu-south-1 and AWS::Region. This would translate to: ```typescript const isMxp = ("eu-south-1" == this.region); ``` We will support all `AWS::` pseudo parameters, and all meta-functions in Cloudformation. * [List of pseudo-parameters](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html) * [List of all Intrinsic Functions](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html) * [List of all Condition Functions](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html) It seems Condition Functions are a subset of Intrinsic functions, and we will support all Intrinsic function rewrites. ### Instructions The core of instructions is attempting to replicate the entire `Resources` section of a Cloudformation template. Because of the additional work of intrinsic functions and condition rewrites, Instructions will be a mix of: * resolving intrinsic functions * ordering references / variables * generating resources As an example, let's say we have: ``` "NewVolume" : { "Type" : "AWS::EC2::Volume", "Condition" : "CreateProdResources", "Properties" : { "Size" : { "Ref": "SizeParam" }, "AvailabilityZone" : { "Fn::GetAtt" : [ "EC2Instance", "AvailabilityZone" ]} } ``` This translates to: ``` let ec2Instance = ...; if (createProdResources) { new ec2.Volume(this, 'NewVolume', { size: props.sizeParam, availabilityZone: ec2Instance.availabilityZone }); } ``` As you can see, we can't create a volume, without an instance because of the GetAtt call. We have to resolve conditions to then refer to them in Instructions, and we have to create a lookup table of Parameters + Logical Ids + Mappings to know what to output for certain values (size in this case was a parameter, so must refer to a prop from the parameter section). We will go into more detail about this in a later document. ### Lookup Table All Mappings will be in a Lookup Table area that are just giant hashmaps. If you have a large mappings like... ``` "AccessRole": { "ARN": { "PrincipalArn": "11111111111:role/Role", }, ... } ``` then this gets translated to ``` const AccessRole = Map> { "ARN" : { "PrincipalArn": "11111111111:role/Role", } } ``` and then all meta lookups into the table will be replaced with hashmap lookups.