Desktop and Application Streaming

Using Amazon API Gateway with Amazon WorkSpaces to interact with AWS resources

There are situations where Amazon WorkSpaces users access AWS resources through API calls. For example, to upload files to an Amazon S3 bucket, or to access parameters from AWS Systems Manager Parameter Store. There is no built-in option to attach an IAM role to an Amazon WorkSpace. However, we can assume an IAM role via a private API in Amazon API Gateway. This API can grant temporary credentials to access AWS services.

This post describes how to create a private API in API Gateway as an endpoint for Amazon WorkSpaces to access AWS services. I show you how to create an AWS integration method for AWS Security Token Service in the API to assume an IAM role. To demonstrate, I use PowerShell on a WorkSpace to retrieve temporary credentials.

Prerequisites

You need the following in order to successfully follow the steps in this post:

Architecture

 

WorkSpaces API Gateway integration architecture

A client, Amazon WorkSpaces, makes an HTTPS request with TLS v.1.2 to the API Gateway VPC endpoint.

  • I use a private DNS name of the private API in this example.
  • There are two other possible ways to invoke. One is through a public DNS name of the interface VPC endpoint. The other is through Route 53 alias record. Read more in How to Invoke a Private API.

API Gateway receives an HTTP request and send an HTTP response to the client.

NOTE: Private APIs in API Gateway only accept TLS v1.2.

The following list outlines the steps involved:

  1. Create a VPC endpoint
  2. Create an IAM Role with a Trust Policy.
  3. Create a private API in API Gateway.
  4. Make an HTTPS Request to the API from a WorkSpace.
  5. Cleaning up deployed resources

Walkthrough

Step1: Create a VPC endpoint

Create an interface VPC endpoint for a private API in API Gateway. The security group attached to the endpoint must allow a TCP port 443 from within the VPC.

Take note of the VPC endpoint ID. Later, when you create a WorkSpace, make sure to put them in this VPC.

Step 2: Create an IAM role with a Trust Policy

To assume an IAM role, you create an assumed role. For the permissions of the assumed role, you can provide access to any service you would like. The following sample policy allows the IAM roles within the account that has the “sts.AssumeRole” permissions to be able to assume this IAM role.

First, create an IAM role called ExampleAPIExecutionRole for API Gateway. Add the following policy. Retain this IAM role to assume another role created in the next step.

Sample IAM policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::[your-account]:role/ExampleAssumedRole"
        }
    ]
}

Sample IAM trust relationship policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Next, create another IAM role called ExampleAssumedRole and add the following policy. The policy allows the ExampleAPIExecutionRole IAM role to assume this role. Add IAM permissions if WorkSpaces interact with AWS services. In this example, I am not adding any permissions.

Sample IAM trust relationship policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::[your-account]:role/ExampleAPIExecutionRole"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Step 3: Create a private API in API Gateway

Follow these steps to create a private API in API Gateway.

First, create a new private API in API Gateway, and name it ExamplePrivateAPI, as shown in the following screenshot.

After you create the private API, create a resource path and an AWS integration GET method. For more information, read TUTORIAL: Build an API Gateway API with AWS Integration.

Under the API, create two resources:

  • /sts
  • Under /sts, assumerole

Sample resource/method

  • Resource path: /sts/assumerole
  • Method: GET
    • Integration type: AWS Service
    • AWS Region: us-east-1
    • AWS Service: Security Token Service (STS)
    • HTTP method: GET
    • Action Type: Path override
    • Path override: ?Version=2011-06-15&Action=AssumeRole
    • Execution role: arn:aws:iam::[your-account]:role/ExampleAPIExecutionRole

Go to method request for /sts/assumerole GET. Add the following to URL Query String Parameters:

  • DurationSeconds
  • RoleArn
  • RoleSessionName

Go to integration request for /sts/assumerole GET. Add the following to URL Query String Parameters:

  • Name: DurationSeconds
  • Mapped from: method.request.querystring.DurationSeconds
  • Name: RoleArn
  • Mapped from: method.request.querystring.RoleArn
  • Name: RoleSessionName
  • Mapped from: method.request.querystring.RoleSessionName

In the resource policy, only allow access from the VPC endpoint to Amazon WorkSpaces. Use the VPC endpoint ID for the VPC endpoint that you created in Step 1. Other resources that can access the VPC endpoint can also access the private API through this VPC endpoint. In a production environment, please validate if the resource policy should be further restricted.

Example resource policy:

{
    "Version": "2012-10-17",
    "Statement":
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:[your-region]:[your-account]:[your-api-id]/*",
            "Condition": {
                "StringEquals": {
                    "aws:sourceVpce": "[your-vpce-id]"
                }
            }
        }
}
  • Now deploy the API. Find the step-by-step guide on deploying the API.
    • Name the stage “v1” in this post.
  • Make sure to keep note of the private API endpoint for the deployment.
    • The API endpoint looks like: https://[your-api-id].execute-api.[your-region].amazonaws.com/v1

Step 4: Make an HTTPS request to the API from a WorkSpace

Connect to your WorkSpace. After logging in, launch PowerShell ISE and copy and paste the following script. Then, update $assumeRoleName and $assumeRoleURI with your account ID and API ID. Following the updates, execute the script from ISE. This creates a credentials file with a named profile called “Example” under the user profile directory. The temporary credentials expire after 15 minutes so the script creates a Task Scheduler task to refresh the credentials every 10 minutes. Fifteen minutes is the shortest duration allowed for the AssumeRole API action.

##################################
Import-Module AWSPowerShell
# Private APIs only support TLS 1.2 - Setting the TLS type to 1.2.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]('Tls12')
# Setting temporary credentials
$assumeRoleName = "arn:aws:iam::[your-account]:role/ExampleAssumedRole"
$assumeRoleSessionName = "Example"
$assumeRoleURI = "https://[your-api-id].execute-api.[your-region].amazonaws.com/v1/sts/assumerole"
$assumeRoleDurationSeconds = "900"
$fullAssumeRoleEndpointPath = "{0}?RoleArn={1}&RoleSessionName={2}&DurationSeconds={3}" -f $assumeRoleURI,$assumeRoleName,$assumeRoleSessionName,$assumeRoleDurationSeconds
$assumeRoleResponse = Invoke-RestMethod -Method GET -URI $fullAssumeRoleEndpointPath
$temporaryAccessKey = $assumeRoleResponse.AssumeRoleResponse.AssumeRoleResult.Credentials.AccessKeyId
$temporarySecretKey = $assumeRoleResponse.AssumeRoleResponse.AssumeRoleResult.Credentials.SecretAccessKey
$temporarySessionToken = $assumeRoleResponse.AssumeRoleResponse.AssumeRoleResult.Credentials.SessionToken

$credentialsFileContent = @'
[Example]
aws_access_key_id = {0}
aws_secret_access_key = {1}
aws_session_token = {2}
'@ -f $temporaryAccessKey,  $temporarySecretKey, $temporarySessionToken

# Create a .aws directory under the user profile in D drive for the temporary credentials.
New-Item -Path "D:\$($env:HOMEPATH)" -Name ".aws" -ItemType Directory -Force
New-Item -Path "D:\$($env:HOMEPATH)\.aws" -Name "credentials" -ItemType File -Value $credentialsFileContent -Force

# Since the temporary credentials expire after 15 minutes, schedule a job that runs every 10 minutes to refresh credentials.
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "Invoke-RestMethod -Method GET -URI $($fullAssumeRoleEndpointPath)"
$trigger = New-JobTrigger -RepeatIndefinitely -RepetitionInterval (New-TimeSpan -Minutes 10) -at (Get-Date) -Once
$settingsSet = New-ScheduledTaskSettingsSet -StartWhenAvailable
Register-ScheduledTask -Force -TaskName "AssumeRole" -Action $action -Trigger $trigger -Settings $settingsSet
##################################

Running the AWS Tools for PowerShell command below validates whether the named profile with temporary credentials is valid.

##################################
# Test the temporary credentials
Get-STSCallerIdentity -ProfileName Example
##################################

Response:

WorkSpaces STS response example

Cleaning up

After testing the private API and if it’s no longer required, clean up the deployed resources.

  1. Go to the VPC console and delete the VPC endpoint. Read more in Deleting a VPC Endpoint.
  2. Go to the IAM console and select the IAM role named Example. Read more in Deleting Roles or Instance Profiles.
  3. Go to the API Gateway console to delete the private API. Read more in Delete an API in API Gateway.
  4. Open a Windows PowerShell session and run the following command:
###
Unregister-ScheduledTask -TaskName "AssumeRole"
###

Conclusion

Now you have created a private API in API Gateway. Use this API to retrieve WorkSpace temporary credentials.

Based on the permissions given to the assumed role, you can run additional AWS Tools for PowerShell commands to access AWS resources. Also, the private API can provide other resources and methods. This tool opens the door to many solutions that require AWS credentials in Amazon WorkSpaces.