Creating an Automation Document Using C# and CDK

Introduction

When you are looking to deploy infrastructure into an AWS Account, one of the best practices is to use Infrastructure as Code (IaC). This is a recommended approach because it allows the process to be repeatable, and traceable. It also allows you, through the use of AWS Cloud Formation, to determine if you have had configuration drift.

The one hinderances, for me, in building out infrastructure as code using Cloud Formation, is that I have been writing C/C++ style code since the early 90s. I think in curly braces… so Cloud Formation is far less intuitive for me than writing C# code.

Enter CDK

The AWS Cloud Development Kit (CDK) is a toolset that allows developers to create complete sets of infrastructure using the programming languages that they are most familiar with. CDK supports a range of programming languages including:

  • C-Sharp
  • Java
  • Python
  • Java Script
  • Python

Under the covers CDK, and most other AWS automation tools, use the Cloud Formation service to create infrastructure on your behalf. This is a great help to people like me who gravitate towards C# code. I have been working with AWS and Cloud Formation for over 6 years now, and I still would not call myself proficient. However, CDK and specifically the C# flavor of CDK help me to be productive a lot faster than if I try and work through native Cloud formation.

Automation Documents

One of the great features of the AWS Systems Manager Service is the ability to use automation documents. These documents allow you to manage your instances and automate a lot of manual effort, all without having to ever log into the machine.

A couple of months ago I set out to build a CDK project that was going to include an automation document. The task was fairly simple, I wanted the document to automatically connect an instance that I had created to a managed Active Directory instance I had also created as part of the same stack. This is where things got a little difficult.

As with most CDK constructs, the code to create a Cloud Formation Document looks similar to this:

var ssmDocument = new CfnDocument(this, "mmad-ssd-doc", new CfnDocumentProps()
            {
                Name = "MCID-AD-Association",
                Content = myContent
            });

The configuration of the document happens in the initialization for the CfnDocumentProps class. The Content property is the body of the automation document. In this content I wanted to use the existing AWS managed automation document to join the instance to the domain, and provide the details of the Managed Microsoft Active Directory the instance was supposed to be joined to.

Looking through the documentation for CfnDocumentProps produced very few clues on how I was supposed to format the content property. I found plenty of examples in JavaScript and Python, however these examples let me to believe that I was able to provide a string representation of the document. This does not however work in a C# project.

Passing in a string, resulted in an exception being thrown when I tried to execute the CDK project. I spent a few hours trying a few random things and then finally came across the answer. CDK Expects that your content construct is passed in as a Dictionary! With this I was able to formulate the content to create my document.

Here is the code:

Dictionary<string, object> directoryIdProp = new Dictionary<string, object>();
            directoryIdProp.Add("Ref", "MMAD");

            Dictionary<string, object> dnsIpAddressesProp = new Dictionary<string, object>();
            dnsIpAddressesProp.Add("Fn::GetAtt", new string[] { "MMAD", "DnsIpAddresses"});

            Dictionary<string, object> properties = new Dictionary<string, object>();
            properties.Add("directoryId", directoryIdProp);
            properties.Add("directoryName", "example.com");
            properties.Add("dnsIpAddresses", dnsIpAddressesProp );

            Dictionary<string, object> domainjoin = new Dictionary<string, object>();
            domainjoin.Add("properties", properties);

            Dictionary<string, object> runtimeConfig = new Dictionary<string, object>();
            runtimeConfig.Add("aws:domainJoin", domainjoin);

            Dictionary<string, object> docProperties = new Dictionary<string, object>();
            docProperties.Add("schemaVersion", "1.2");
            docProperties.Add("description", "Join the instance to a MMAD domain");
            docProperties.Add("runtimeConfig", runtimeConfig);

            var ssmDocument = new CfnDocument(this, "mmad-ssd-doc", new CfnDocumentProps()
            {
                Name = "MCID-AD-Association",
                Content = docProperties
            }); 

This code uses several lookups to get values of other items that were created in the stack. Using this code, I created an Automation document that configures and runs the AWS supplied domain join automation, passing in the value to join my managed active directory.

Each level of the hierarchy in the document is represented as a new Dictionary. So for nested levels, I have a Dictionary inserted into a higher level dictionary as a named object.

If I run the command line CDK Synth in the CDK project directory, I get the following output. Note that the construction of the document is roughly reverse of my C# source code, because I am building my document from the inner most constructs to the outter most.

mmadssddoc:
    Type: AWS::SSM::Document
    Properties:
      Content:
        schemaVersion: "1.2"
        description: Join the instance to a MMAD domain
        runtimeConfig:
          aws:domainJoin:
            properties:
              directoryId:
                Ref: MMAD
              directoryName: example.com
              dnsIpAddresses:
                Fn::GetAtt:
                  - MMAD
                  - DnsIpAddresses
      Name: MCID-AD-Association
    Metadata:
      aws:cdk:path: BaseTemplateStack/mmad-ssd-doc

So there you have it. Building Automation documents with CDK is remarkably easy, once you know the constructs that you need to use.

If you would like me to dig in to CDK more, please reach out and comment at tom@basementprogrammer.com