AWS Service Account Integrations

Problem

You are building interactive or rendered content in Python or R which needs to access a private AWS resource using an IAM role. The content must authenticate to AWS using a workload identity token that is generated by Connect. This is made possible by adding an AWS service account integration to your content in the content access panel.

Solution

The examples below illustrate how to use the OAuth integrations feature of Posit Connect to delegate authorization for access to an external provider’s resources. In this case, we are focusing on S3 within AWS. These examples use the viewer’s OIDC token to assume an AWS IAM role that provides temporary session credentials to the content. These credentials are used to call the AWS API on behalf of the content viewer.

Ensure the following dependencies are installed:

  • boto3
  • posit-sdk>=0.9.0

Make sure to set the following additional environment variables for local development:

  • AWS_REGION
  • AWS_BUCKET_NAME
  • AWS_S3_FILEPATH
import os

import pandas as pd
import boto3
from posit.connect import Client
from posit.connect.external import aws

# Obtain AWS credentials and start a session:
# 1. If running on Posit Connect, perform a credential exchange with the
#    content session token that is provided at app startup.
# 2. If running locally, initialize a session using your preferred method.
#    This example assumes the use of the aws cli to authenticate.
if os.getenv("POSIT_PRODUCT") == "CONNECT":
  client = Client()
  # exchange content session token for Service Account AWS credentials
  # Connect makes the content session token available in the 
  # environment variable `CONNECT_CONTENT_SESSION_TOKEN`.
  aws_credentials = aws.get_content_credentials(client)
  aws_session = boto3.Session(
    aws_access_key_id=aws_credentials["aws_access_key_id"],
    aws_secret_access_key=aws_credentials["aws_secret_access_key"],
    aws_session_token=aws_credentials["aws_session_token"],
  )
else:
  # Use default credentials from environment/config
  aws_session = boto3.Session()


# Create an S3 client using the AWS session
s3 = aws_session.client("s3")

# Define bucket and key (file path)
bucket_name = os.getenv("AWS_BUCKET_NAME")
key = os.getenv("AWS_S3_FILEPATH")

# Get the object from S3
obj = s3.get_object(Bucket=bucket_name, Key=key)

# Read the CSV data into a pandas DataFrame
df = pd.read_csv(obj["Body"])

Ensure you have the following dependencies installed:

  • connectapi
  • arrow

Make sure to set the following additional environment variables for local development:

  • AWS_REGION
  • AWS_BUCKET_NAME
  • AWS_S3_FILEPATH
library(connectapi)
library(arrow)

# Obtain AWS credentials and start a session:
# 1. If running on Posit Connect, perform a credential exchange with the
#    content session token that is provided at app startup.
# 2. If running locally, initialize a session using your preferred method.
#    This example assumes the use of the aws cli to authenticate.
bucket <- NULL
bucket_name <- Sys.getenv("AWS_BUCKET_NAME")
filepath <- Sys.getenv("AWS_S3_FILEPATH")

# if running on Connect
if (Sys.getenv("POSIT_PRODUCT") == "CONNECT") {
  # init Posit Connect API client
  client <- connect()
  credentials <- get_aws_content_credentials(client)
  bucket <- arrow::s3_bucket(bucket_name,
                              access_key = credentials$access_key_id,
                              secret_key = credentials$secret_access_key,
                              session_token = credentials$session_token
  )
} else {
  # Use default credentials from environment/config
  bucket <- arrow::s3_bucket(bucket_name)
}

# Get the object from S3 and read into dataframe
# Assumes csv file. Use other file reader functions 
# for other file types e.g. arrow::read_parquet().
df <- arrow::read_csv_arrow(bucket$path(filepath))