Starting, Stopping and Managing AWS Instances: Managing Infrastructure with RapidIdentity, Part VI

    

As more and more organizations realize the benefits of “cloud computing," the demand to host applications and services in the cloud continues to rise. Identity Automation and many of our customers have moved significant workloads into Amazon Web Services (AWS) or other cloud-hosted environments, and as a result, the need to automate and manage tasks in these environments has become more commonplace.

Amazon has provided management API’s to accommodate these needs and RapidIdentity allows customers to use them, in order to provide the same automation and “ease of use” in managing their cloud resources that they have come to rely upon for their day-to-day, on-premise IT processes.

In this final installment of our “Managing Infrastructure with RapidIdentity” blog series, I’ll demonstrate just a few of the many ways in which the RapidIdentity Connect Web Service adapter can be used to manage an Amazon Web Services environment. Specifically, I’ll provide examples of starting, stopping, and “managing tags” for individual hosts in AWS Elastic Cloud Compute (EC2).

By following these examples, and using the additional API’s that Amazon provides, our customers can realize the ease with which their AWS cloud resources can be managed.

Our Action

With this action, Connect users may start or stop a given AWS EC2 host, query for tag information from the host metadata in AWS, or add and delete tags from the EC2 host, as necessary.

The action requires two input variables, and Connect users can optionally provide two additional input variables.

  • awsAction (Required - value to be chosen from those below)
    • start
    • stop
    • get_tags
    • add_tag
    • delete_tag
  • instance_id (Required)
  • tagKey (Optional - only required if awsAction = “add_tag” or “delete_tag”)
  • tagValue (Optional - only required if awsAction = “add_tag”)

To begin, import the Global attributes (i.e. Globals) which define two specific attributes for this action: Global.AWSAccessKey and Global.AWSSecretKey. These keys are assigned specific permissions in AWS and facilitate the action to make specific API calls.

# Globals
globals()

Next, define the proper AWS namespace, specific to Amazon EC2, and the primary URL which will be used to access specific API’s, based upon the input variables provided to the action.

Additionally, if required input is missing for the associated awsAction input, an error message is logged, and the Connect action exits.


 # Setup URL for start / stop, get / add / delete tag(s)
 {
    aws = new Namespace("http://ec2.amazonaws.com/doc/2013-06-15/")
    if(awsAction == "start") {
        url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=StartInstances"
        url = url+"&InstanceId.1="+instance_id
    } else {
        if(awsAction == "stop") {
            url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=StopInstances"
            url = url+"&InstanceId.1="+instance_id
        } else {
            if(awsAction == "add_tag") {
                if((tagKey == null) || (tagValue == null)) {
                    log("Job was run with awsAction set to \"add_tag\", but either tagKey or tagValue (or both) were not provided...", "red")
                    return()
                } else {
                }
                url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=CreateTags&ResourceId.1=" + instance_id + "&Tag.1.Key=" + tagKey + "&Tag.1.Value=" + tagValue
            } else {
                if(awsAction == "delete_tag") {
                    if((tagKey == null)) {
                        log("Job was run with awsAction set to \"delete_tag\", but tagKey was not provided...", "red")
                        return()
                    } else {
                    }
                    url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=DeleteTags&ResourceId.1=" + instance_id + "&Tag.1.Key=" + tagKey
                } else {
                    if(awsAction == "get_tags") {
                        url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=DescribeTags&Filter.1.Name=resource-id&Filter.1.Value.1=" + instance_id
                    } else {
                    }
                }
            }
        }
    }
}

Now that the proper base URLs are configured, it is necessary to set three additional variables that will be used within the parameters of the API call to ensure proper permissions and timestamping for this action set.


# Set AWS Access Key
 {
    awsDate = formatDate(now(), "yyyy-MM-dd'T'HH:mm:ss'Z'", "UTC")
    accountKey = Global.AWSAccessKey
    secretKey = Global.AWSSecretKey
}

 The action continues by building out the remainder of the request — appending parameters to the API call — to encode the data using the Global.AWSSecretKey to ensure the request is formatted properly and transmitted securely to AWS, where it can be decoded and interpreted by the backend AWS web services.


# Encode the request
 {
    jurl = new java.net.URL(url)
    stringToSign = "GET\n"
    stringToSign = stringToSign+jurl.getHost() + "\n"
    stringToSign = stringToSign + (String(jurl.getPath()) || "/") + "\n"
    params = splitString(jurl.getQuery() || "", "&")
    appendArrayItem(params, "AWSAccessKeyId=" + encodeURIComponent(accountKey))
    appendArrayItem(params, "SignatureVersion=2")
    appendArrayItem(params, "SignatureMethod=HmacSHA256")
    appendArrayItem(params, "Timestamp=" + encodeURIComponent(awsDate))
    sortArray(params, false)
    params = joinArray(params, "&")
    stringToSign = stringToSign + params
    dataBytes = new java.lang.String(stringToSign).getBytes("UTF-8")
    keyBytes = new java.lang.String(secretKey).getBytes("UTF-8")
    macB64 = macBytes(keyBytes, dataBytes, "HmacSHA256", "BASE64")
    url = stringReplaceFirst(url, /\?.*/, '', false)
    params = params + "&" + "Signature=" + encodeURIComponent(macB64)
    url = url + "?" + params
}

Now that the API call is prepared, the action can submit the API call to AWS and log the returned data.


# Submit the request and return
 {
    result = httpGET(url,)
    if(awsAction != "get_tags") {
        log(result)
    } else {
        resultData = createRecordFromXML(result['data'])
        log("Instance ID:\t" + instance_id)
        if(resultData['tagSet']['item'] instanceof Array) {
            forEach(item, resultData['tagSet']['item']) {
                log("Key Name:   \t" + item['key'])
                log("Key Value:\t" + item['value'])
            }
        } else {
            log("Key Name:   \t" + resultData['tagSet']['item']['key'])
            log("Key Value:\t" + resultData['tagSet']['item']['value'])
        }
    }
}

For simplicity’s sake, this example action is logging the entire response from the web service calls, except for get_tags (where we log only the returned Instance ID and tag details). The complete responses include the URL that this action posts to AWS, including the access key — consequently, the full log output is not displayed here, for security purposes — as well as a statusCode and statusReason.

Managing Infrastructure with RapidIdentity Part 6 - Image 1.png

A 200 - OK for statusCode and statusReason are indicators that the web service call returned ‘successful’.

The Amazon EC2 Management Console can be used to view the instance status. In this screenshot, the instanceID is hidden, but would typically appear between the instance name (ec2-teverson-remotelin) and instance type (t2.micro).

Managing Infrastructure with RapidIdentity Part 6 - Image 2 for real.png
Returning to the log results, in the cases of add_tag or delete_tag, if the specific tag’s ‘Name’ from AWS is not known at run time, Connect users may use get_tags to return the names and values of all tags on the AWS instance. The screenshot below show the results, with the instanceID hidden.

Managing Infrastructure with RapidIdentity Part 6 - Image 2.png

Finally, the awsAction=”delete_tag” can be used to remove tags present since this last action was run. For example, if the choice is made to identify tagKey=”Purpose”, when the action is run with awsAction=”delete_tag” and tagKey=”Purpose”, the log results from the next get_tags action run will show that the Key Name and Key Value of “Purpose” and “blog”, respectively, have been removed.

Managing Infrastructure with RapidIdentity Part 6 - Image 3.png

Conclusion

This blog post concludes the “Managing Infrastructure with RapidIdentity” series by using the RapidIdentity Connect Web Service adapter and Amazon’s API’s to start and stop an EC2 instance, as well as to manage some of the metadata associated with that instance by manipulating the “tags” stored for the instance.

In addition to these types of actions, customers can also take snapshots (point in time backups of an EC2 instance) and automate other management tasks within their EC2 environment. The functions that can be performed are limited only by the API’s provided by Amazon.

As shown throughout this blog series, RapidIdentity allows Identity Automation and our customers the ability to perform consistent and automated management of various components of the IT infrastructure, whether it be on-premises or in cloud-hosted environments.

To summarize, this series addressed four major topics:

  1. Managing Linux and Windows hosts
  2. Checking the installation status of the RapidIdentity password filter on Active Directory Domain Controllers (similar actions could be used to detect the status of other software installations, as well)
  3. Scheduling and running OpenVAS vulnerability scans across the environment
  4. Managing cloud-hosted assets

Identity Automation encourages our customers to continue to explore new ways to use RapidIdentity to make managing their environment easier and looks forward to seeing and hearing about the implementations where these ideas provide added benefit to your organizations!

Files

StartStopTagAWSHosts (Input Properties:    awsAction:start:stop:get_tags:add_tag:delete_tag, instance_id:string, tagKey?:string, tagValue?:string)


# Globals
globals()
# Setup URL for start / stop
 {
    aws = new Namespace("http://ec2.amazonaws.com/doc/2013-06-15/")
    if(awsAction == "start") {
        url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=StartInstances"
        url = url+"&InstanceId.1="+instance_id
    } else {
        if(awsAction == "stop") {
            url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=StopInstances"
            url = url+"&InstanceId.1="+instance_id
        } else {
            if(awsAction == "add_tag") {
                if((tagKey == null) || (tagValue == null)) {
                    log("Job was run with awsAction set to \"add_tag\", but either tagKey or tagValue (or both) were not provided...", "red")
                    return()
                } else {
                }
                url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=CreateTags&ResourceId.1=" + instance_id + "&Tag.1.Key=" + tagKey + "&Tag.1.Value=" + tagValue
            } else {
                if(awsAction == "delete_tag") {
                    if((tagKey == null)) {
                        log("Job was run with awsAction set to \"delete_tag\", but tagKey was not provided...", "red")
                        return()
                    } else {
                    }
                    url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=DeleteTags&ResourceId.1=" + instance_id + "&Tag.1.Key=" + tagKey
                } else {
                    if(awsAction == "get_tags") {
                        url = "https://ec2.amazonaws.com/?Version=2013-06-15&Action=DescribeTags&Filter.1.Name=resource-id&Filter.1.Value.1=" + instance_id
                    } else {
                    }
                }
            }
        }
    }
}
# Set AWS Access Key
 {
    awsDate = formatDate(now(), "yyyy-MM-dd'T'HH:mm:ss'Z'", "UTC")
    accountKey = Global.AWSAccessKey
    secretKey = Global.AWSSecretKey
}
# Encode the request
 {
    jurl = new java.net.URL(url)
    stringToSign = "GET\n"
    stringToSign = stringToSign+jurl.getHost() + "\n"
    stringToSign = stringToSign + (String(jurl.getPath()) || "/") + "\n"
    params = splitString(jurl.getQuery() || "", "&")
    appendArrayItem(params, "AWSAccessKeyId=" + encodeURIComponent(accountKey))
    appendArrayItem(params, "SignatureVersion=2")
    appendArrayItem(params, "SignatureMethod=HmacSHA256")
    appendArrayItem(params, "Timestamp=" + encodeURIComponent(awsDate))
    sortArray(params, false)
    params = joinArray(params, "&")
    stringToSign = stringToSign + params
    dataBytes = new java.lang.String(stringToSign).getBytes("UTF-8")
    keyBytes = new java.lang.String(secretKey).getBytes("UTF-8")
    macB64 = macBytes(keyBytes, dataBytes, "HmacSHA256", "BASE64")
    url = stringReplaceFirst(url, /\?.*/, '', false)
    params = params + "&" + "Signature=" + encodeURIComponent(macB64)
    url = url + "?" + params
}
# Submit the request and return
 {
    result = httpGET(url,)
    if(awsAction != "get_tags") {
        log(result)
    } else {
        resultData = createRecordFromXML(result['data'])
        log("Instance ID:\t" + instance_id)
        if(resultData['tagSet']['item'] instanceof Array) {
            forEach(item, resultData['tagSet']['item']) {
                log("Key Name:   \t" + item['key'])
                log("Key Value:\t" + item['value'])
            }
        } else {
            log("Key Name:   \t" + resultData['tagSet']['item']['key'])
            log("Key Value:\t" + resultData['tagSet']['item']['value'])
        }
    }
}

 

 

Why-should-IAM-be-the-core-of-your-program

Comments

Subscribe Here!