Modifying AWS Route53 Records in vRealize Automation – Part 2

20161114-1In part 1 of this short series, I set the scene for a blueprint VM requiring a DNS record to be created in AWS Route53. I documented the vRO resource and configuration elements that would be needed, along with a handful of actions. In the final part, we tackle the main workflow plus

an extremely important plugin which brings the entire solution together.

Other posts in this series:

  1. Part 1
  2. Part 2

Plugin

To be able to communicate with AWS we will need to be able to hash and encrypt our API requests. Unfortunately, vRealize Orchestrator has no way to natively do this, so requires a third-party plugin.

Thankfully VMware has already thought of this and has created the crypto plugin, which can be found at https://github.com/vmware/o11n-plugin-crypto.

Download and install the plugin in the vRO Control Center (on port 8283):

Main Workflow

Create a new workflow called Modify Route53 DNS. Create four inputs all of type string:

  • inAction
  • inRecord
  • inTtl
  • inType
  • inValue

As we will be presenting this workflow as XaaS in vRealize Automation, take a few minutes to adjust the presentation – this will save having to do it later. For example, I present the user with a set of predefined answers for the inAction input of “CREATE” and “DELETE”.

Add the following attributes:

  • attAccessKey
  • attAmzAuthDate
  • attAmzDate
  • attAuthString
  • attBody
  • attCanonicalRequestHash
  • attHashedPayload
  • attHostedZone
  • attHttpRequestMethod
  • attRegion
  • attRestHost
  • attSecretKey
  • attService
  • attSignature
  • attSignedHeaders
  • attSigningKey
  • attStringToSign
  • attUrl
  • attXmlConfigBody

All these are of type string, except for attRestHost (type: Any) and attXmlConfigBody (type: Resource Element).

Link the attAccessKey and attSecretKey configurations to the login configuration elements we created in part 1. Repeat the procedure for the attHostedZone, attRegion, attService and attUrl configurations. Set attXmlConfigBody to the route53.xml XML file we uploaded. Finally, set attHttpRequestMethod to POST.

How the attributes should look

Create a scriptable task on the canvas. Call it “Define Request Body” and bind all the available inputs, along with the attXmlConfigBody attribute. Set attBody as the out attribute:

Paste in the following code:


//Define XML properties
var xmlDetail = new Properties();
xmlDetail.put("{attXmlConfigAction}",inAction);
xmlDetail.put("{attXmlConfigRecord}",inRecord);
xmlDetail.put("{attXmlConfigType}",inType);
xmlDetail.put("{attXmlConfigTtl}",inTtl);
xmlDetail.put("{attXmlConfigValue}",inValue);
//Replace placeholders in XML file with correct values
var attBody = attXmlConfigBody.getContentAsMimeAttachment().content;
for each (var key in xmlDetail.keys) {
attBody = attBody.replace(key, xmlDetail.get(key));
}

view raw

defineBody.js

hosted with ❤ by GitHub

Task 1

Create another scriptable task called “Task 1”. Drag the following in attributes in:

  • attBody
  • attHostedZone
  • attHttpRequestMethod
  • attUrl
  • And the following

And the following out attributes:

  • attAmzDate
  • attCanonicalRequestHash
  • attHashedPayload
  • attSignedHeaders

Task 1

Paste in the following code:


// Hash the payload
attHashedPayload = CryptoEncoding.base64toHex(CryptoDigest.sha256(attBody));
// Format the date
attAmzDate = System.getModule("com.hobbitcloud.aws.date").getAmzDate(new Date().toISOString());
var canonicalUri = "/2013-04-01/hostedzone/" + attHostedZone + "/rrset";
var canonicalQueryString = "";
var canonicalHeaders = 'host:' + attUrl + '\n' +
'x-amz-content-sha256:' + attHashedPayload + '\n' +
'x-amz-date:' + attAmzDate + '\n'
attSignedHeaders = 'host;x-amz-content-sha256;x-amz-date';
// Define the request
var canonicalRequest = attHttpRequestMethod + '\n' + canonicalUri + '\n' + canonicalQueryString + '\n' + canonicalHeaders + '\n' + attSignedHeaders + '\n' + attHashedPayload;
System.log("The canonical request is: " + '\n' + '\n' + canonicalRequest + '\n');
// Hash the request
attCanonicalRequestHash = CryptoEncoding.base64toHex(CryptoDigest.sha256(canonicalRequest));
System.log("The canonical request hash is: " + attCanonicalRequestHash);

view raw

task1.js

hosted with ❤ by GitHub

Task 2

Repeat the same process for Task 2.

In attributes:

  • attAmzDate
  • attCanonicalRequest
  • attRegion
  • attService

Out attributes:

  • attAmzAuthDate
  • attStringtoSign

Code:


// Format the auth date
attAmzAuthDate = attAmzDate.split("T")[0];
// Define string to sign
attStringToSign = 'AWS4-HMAC-SHA256\n' + attAmzDate + '\n' + attAmzAuthDate + '/' + attRegion + '/' + attService + '/aws4_request\n'+ attCanonicalRequestHash;
System.log("The string to sign is: " + '\n' + '\n' + attStringToSign + '\n');

view raw

task2.js

hosted with ❤ by GitHub

Signing Key

Now we have calculated our string to sign, we need to produce the signing key.

Drag an action element onto the canvas and select the getSigningKey we created in part 1. Match the inputs with the corresponding in attributes, and map the action output to the attSigningKey out attribute:

Get Signing Key

Now our workflow is starting to take shape!

Task 3

In task 3 we take our string and sign it. Create another scriptable task and call it “Task 3”.

Add two in attributes, attSigningKey and attStringToSign. Map the out attribute to attSignature.

Task 3

Paste in the following code:


// Define the signing key in base64
var kSigningB64 = CryptoEncoding.hexToBase64(attSigningKey);
System.log("The signing key in base64 is: " + kSigningB64);
// Define the string to sign in base 64
var stringToSignB64 = CryptoEncoding.base64Encode(attStringToSign);
System.log("The string to sign in base64 is: " + stringToSignB64);
// Calculate the signature
var signature = CryptoDigest.hmacSha256(kSigningB64, stringToSignB64);
attSignature = CryptoEncoding.base64toHex(signature);
System.log("The signature is: " + attSignature);

view raw

task3.js

hosted with ❤ by GitHub

Task 4

The last of the tasks is to put all our data together to form the auth string. This requires the following in attributes:

  • attAccessKey
  • attAmzAuthDate
  • attRegion
  • attService
  • attSignature
  • attSignedHeaders

The out attribute is simply the attAuthString:

Task 4

Finally, paste in the following code:


// Define the authentication header string
attAuthString = 'AWS4-HMAC-SHA256 Credential=' + attAccessKey + '/' + attAmzAuthDate + '/' + attRegion + '/' + attService + '/aws4_request, SignedHeaders=' + attSignedHeaders + ', Signature=' + attSignature;
System.log("Auth string is: " + attAuthString);

view raw

task4.js

hosted with ❤ by GitHub

Creating the REST Host

Now we have completed all of the AWS API tasks we need to build our REST request and submit it. To do this, drag another action to the canvas and select the createRestHost.

Select the inUrl in attribute and map the out attribute to attRestHost.

Submitting the Request

Now we come to the part where we submit our request.

Finally, create one last scriptable task called “Submit Request”. Use the following in attributes:

  • attAmzDate
  • attAuthString
  • attBody
  • attHashedPayload
  • attHostedZone
  • attRestHost

Paste in the following code:


//Compute the full URL
var requestUrlString = "/2013-04-01/hostedzone/" + attHostedZone + "/rrset";
var request = attRestHost.createRequest("POST", requestUrlString, attBody);
//Set the authentication header
request.setHeader("Authorization", attAuthString);
request.setHeader("X-Amz-Content-Sha256", attHashedPayload);
request.setHeader("X-Amz-Date", attAmzDate);
//Execute the request and get the response
var response;
response = request.execute();
var statusCode = response.statusCode;
var responseContent = response.contentAsString;
System.log(responseContent);
System.log("StatusCode = " + statusCode);
if (statusCode != 200) {
System.error("Error:");
throw new Error("Failed to modify Route53 hosted zone: " + statusCode);
}

When complete, the main workflow should look something like this:

Main Workflow

Run the Workflow

To confirm the workflow works as expected, run it manually before publishing in vRA:

If all goes well it should create a record in Route53:

Success!

If not, retrace your steps to see what you might have missed.

Publish XaaS

Now we have authored our workflow in vRO, it is time to publish it in vRealize Automation.

Navigate to the Design tab in vRA 7.x and click XaaS, followed by XaaS Blueprints. Click New, and then drill-down to find your workflow. Click Next >

Make any changes as needed, then click Next >

If you have taken the time to improve the workflow presentation this will be reflected here. Otherwise, that will need to be done now, and re-done should you ever need to re-generate the form.

Once you’re happy with the layout, click Next >. As there will be no resource type to output, click Next > followed by Finish.

Please note: don’t forget to publish your XaaS Blueprint! That gets me. Every. Single. Time.

Finally, publish your blueprint in the catalog ready for your users…

…and request it:

7 thoughts on “Modifying AWS Route53 Records in vRealize Automation – Part 2

  1. Pingback: Modifying AWS Route53 Records in vRealize Automation – Part 1 | virtualhobbit

  2. Hi Mark, thanks very much for posting this. I am trying to run through the steps, and then once I get it working hopefully do something similar to integrate vRO with AWS Systems Manager.
    Sorry for the stupid question, but the step to add an action called createRestHost, where are you getting that action from?
    TIA
    Dean

    Like

      • Really appreciate the fast turnaround on this thanks very much.
        I am getting this error now, but I will keep looking.

        (com.sovlabs.prerequisites/importCert) Error in (Dynamic Script Module name : importCert#5) com.vmware.o11n.plugins.configurator.util.CertificateException: route53.amazonaws.com: Name or service not known

        Like

      • Almost there now I think đŸ™‚

        I had 2 issues, DNS although working was slow, but that was because there was an issue in my lab. Second issue was a time issue on the vRO appliance so the auth was coming from the past and failing.

        Now I am getting this error, but that could actually be on the AWS side also. Great article Mark, thanks again, and thanks again for replying.

        Error in (Workflow:Modify Route 53 DNS / Submit Request (item8)#19) Error: Failed to modify Route53 hosted zone: 400

        Like

      • Missed the key item out of the error

        ?xml version=”1.0″?>
        SenderInvalidInput1 validation error detected: Value null at ‘changeBatch’ failed to satisfy constraint: Member must not be null

        Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.