Desktop and Application Streaming

Use Amazon CloudWatch Events with Amazon WorkSpaces and AWS Lambda for greater fleet visibility

Amazon WorkSpaces is a managed, secure cloud desktop service. You can use Amazon WorkSpaces to provision either Windows or Linux desktops, and quickly scale to provide thousands of desktops to workers across the globe. With Amazon WorkSpaces, users can connect securely from anywhere using a software client or a supported web browser. Administrators have full control of the platforms their users can connect from, and any additional factors that are needed. They also have access to 11 Amazon CloudWatch metrics in two dimensions providing visibility into their Amazon WorkSpaces fleets. For more information about these metrics, see Monitor Your WorkSpaces Using CloudWatch Metrics. 

In this post, we log all successful Amazon WorkSpaces connections in an AWS Region by using two recent releases: Amazon CloudWatch Events for Amazon WorkSpaces and PowerShell Core 6.0 for AWS Lambda. For each connection, we use the AWS Tools for PowerShell to retrieve additional data about the target WorkSpaces, and geolocation data about the connection. We then store this data in Amazon DynamoDB, where we can perform a query at any time.

Prerequisites

To follow the steps in this post, you need the following:

Step 1: Create an IAM role for your Lambda function

To grant our Lambda function permissions to access multiple services, create a role and a policy in IAM by doing the following:

  1. Open the IAM console at https://console.aws.amazon.com/iam/
  2. In the navigation pane, choose Roles.
  3. Choose Create Role.
  4. Select AWS Service, Lambda.
  5. Choose Next: Permissions.
  6. In the filter dialog box, enter AWS Lambda.
  7. Select AWSLambdaExecute and AWSLambdaVPCAccessExecutionRole.
  8. Choose Next: Tags.
  9. Choose Next: Review.
  10. Provide a name and description for your role.
  11. Choose Create role.
  12. Open the service role that you just created.
  13. Choose the Permissions tab, then choose Add inline policy.
  14. Choose the JSON tab.
  15. Enter the following syntax. For the highlighted portion in the syntax, change the 123456789012:table to the AWS account number this is to be deployed in. Change wsaccess to match the name of the DynamoDB table, which you create in the next step, Create your DynamoDB table.
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "workspaces:DescribeWorkspacesConnectionStatus",
                "dynamodb:ListTables",
                "workspaces:DescribeWorkspaces"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "dynamodb:BatchGetItem",
                "dynamodb:BatchWriteItem",
                "dynamodb:PutItem",
                "dynamodb:DescribeTable",
                "dynamodb:GetItem",
                "dynamodb:UpdateItem"
            ],
            "Resource": "arn:aws:dynamodb:*:123456789012:table/wsaccess"
        }
    ]
}

16.   Choose Review policy.
17.   Provide a name for your policy.
18.   Choose Create policy.

Step 2: Create your DynamoDB table

In this step, we use Amazon DynamoDB as the central repository for all connection events.  This lets administrators query and visualize connection patterns for a given IP, user, workspace ID, and more.

We use a table with a primary sort key. This lets us have multiple unique entries with the same primary partition key, which in our case, is workspaceId. The sort key is a timestamp of the login date and time.

Create a new DynamoDB table by doing the following:

  1. Open the DynamoDB console at https://console.aws.amazon.com/dynamodb/
  2. In the navigation pane, choose Tables.
  3. Choose Create table.
  4. In the Create DynamoDB Table screen:
    1. Name your table; for example, wsaccess.
    2. For Primary Partition Key, enter workspaceId. Keep String as the default entry.
    3. Choose Add sort key.
    4. For Sort key, enter loginTime. Keep String as the default entry.
    5. Choose Create.

Note

If you want to deploy this solution in multiple AWS Regions, add the Regions on the Global Tables tab before implementing.

Step 3: Create your Lambda function

In this step, we create the Lambda function that will run at each login.

Create the job by doing the following:

  1. Open your PowerShell editor of choice, and create a new script, naming it something like wsaccess-lambda.ps1.
  2. Enter the following code:
#Requires -Modules @{ModuleName='AWSPowerShell.NetCore';ModuleVersion='3.3.343.0'}

# Defaults
$region = "us-east-1"
$tableName = $env:tableName

# Functions
function Put-DDBItem{
    param (
        [string]$tableName,
        $inputobj
        )
    $req = New-Object Amazon.DynamoDBv2.Model.PutItemRequest
    $req.TableName = $tableName
    $req.Item = New-Object 'system.collections.generic.dictionary[string,Amazon.DynamoDBv2.Model.AttributeValue]'

    foreach($item in $inputobj.keys){
          $valObj = New-Object Amazon.DynamoDBv2.Model.AttributeValue
          $valObj.S = $inputobj[$item]
          $req.Item.Add($item, $valObj)
          }
    $dbClient.PutItemAsync($req)
    }

# Find Dynamo DLL
$modulepath = get-module AWSPowerShell.NetCore |Select-Object -ExpandProperty Path |Split-Path
$dllPath = ls "$modulepath/AWSSDK.DynamoDBv2.dll"

# Enable DynamoDB class
Add-Type -Path $dllPath

# Create Dynamo Client
$regionEndpoint=[Amazon.RegionEndPoint]::GetBySystemName($region)
$dbClient = New-Object Amazon.DynamoDBv2.AmazonDynamoDBClient($regionEndpoint)

#Capture Geo Data
$ipAddress = $LambdaInput.detail.clientIpAddress
$ipLocations = $null
$ipLocations = Invoke-RestMethod http://ipinfo.io/$ipAddress/json/

# Workspace Details
$wksDetails = Get-WKSWorkspace -WorkspaceId $($LambdaInput.detail.workspaceId)

# Create hash object to upload to Dynamo
$data = @{
    account = $LambdaInput.account;
    region = $LambdaInput.region;
    clientIpAddress =  $ipAddress;
    actionType =  $LambdaInput.detail.actionType;
    workspacesClientProductName =  $LambdaInput.detail.workspacesClientProductName;
    loginTime =  $LambdaInput.detail.loginTime; #returns UTC
    clientPlatform =  $LambdaInput.detail.clientPlatform;
    directoryId =  ($LambdaInput.detail.directoryId).tostring().split("/")[1] 
    workspaceId =  $LambdaInput.detail.workspaceId;
    username = $wksDetails.UserName;
    computername = $wksDetails.ComputerName;
    hostname = $ipLocations.hostname;
    city = $ipLocations.city;
    state = $ipLocations.region;
    county = $ipLocations.country;
    org = $ipLocations.org;
    location = $ipLocations.loc
}

# Create DynamoDB entry
Put-DDBItem -tableName $tableName -input $data

3. Open a PowerShell terminal.

4. Import the AWSLambdaPSCore module by typing the following:

Import-Module AWSLambdaPSCore

5.   Create a new Lambda job from your code by typing the following:

Publish-AWSPowerShellLambda -ScriptPath .\wsaccess-lambda.ps1 -Name wsaccess-lambda -Region us-east-1

6.   When prompted, select the IAM role that you created earlier in Step 1.

 

Step 4: Create a new EC2 Security Group

The Lambda function above will have access to your VPC. To control access to and from the instances we will create a security group.

Create a new EC2 security group by doing the following:

  1. Open the EC2 console at https://console.aws.amazon.com/ec2/.
  2. Under Network & Security, from the section menu on the left, select Security Groups.
  3. Choose Create Security Group.
  4. Enter a name and description for the new group.
  5. Select the VPC to use for the Lambda function.
  6. Choose Create.

Step 5: Modify your Lambda function

The Lambda function above uses Environment Variables to identify the DynamoDB table it should write to. It also uses you VPC to access the geolocation api. In this section we will modify the Lambda function configuration to allow these.

Modify your Lambda job by doing the following:

  1. Open the Lambda console at https://console.aws.amazon.com/lambda/.
  2. Select the function that you just created.
  3. Go to the Environment variables section.
  4. Create a new environment variable, and name it tableName.
  5. Change the value for tableName to name of the table that you created earlier in Step 2.
  6. Go to the VPC section.
  7. Select a VPC that has internet access.
  8. Select at least one subnet in the VPC that has a route to the internet. Preferably, use two subnets.
  9. Select the security group that you created earlier in Step 4.

Step 6: Create an event listener in CloudWatch

Now that the Lambda function is configured, CloudWatch must be configured to trigger the function at each login.

  1. Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/.
  2. In the navigation pane, choose Events.
  3. Choose Create rule.
  4. For Event Source, do the following:
    1. Choose Event Pattern. Keep Build event pattern to match events by service as the default.
    2. For Service Name, choose WorkSpaces.
    3. For Event Type, choose WorkSpaces Access.
  5. For Targets, choose Add target, then choose the service that is to act when an Amazon WorkSpaces event is detected. Provide any information required by this service.
  6. Choose Configure details. For Rule definition, type a name and description.
  7. Choose Create rule.

For more information about CloudWatch events for Amazon WorkSpaces, see Monitor Your WorkSpaces Using CloudWatch Events.

Conclusion

In this post, we created a job to combine telemetry data, provided by the WorkSpaces service, with geographic information and details from the WorkSpaces API operation. With this job in place, we now have a record of when and from where users are connecting to their Amazon WorkSpaces.