Example: Onboard Postgres and Snowflake with Claude and self-hosted credentials

Connect a Postgres database and a Snowflake account to Monte Carlo from Claude, through an AWS collection agent that reads credentials from your own AWS Secrets Manager.

This example walks through a complete onboarding done with Claude and the Agent Toolkit onboarding skill. At the end you have:

  • a collection agent running in your AWS account, registered with Monte Carlo;
  • a Postgres connection and a Snowflake connection, both validated;
  • credentials that never left your AWS account: Monte Carlo stores only the ARN of each secret, and the agent reads the value when it runs queries.

Claude does every Monte Carlo step through the Monte Carlo MCP server. You run the steps that happen in your own AWS account (storing the secrets, deploying the agent, granting it access), using the commands Claude gives you.

WhoWhatHow
YouStore the Postgres and Snowflake credentialsAWS CLI, in Secrets Manager
ClaudeCreate the agent deployment and read its External IDcreate_deployment, get_deployment
YouDeploy the agentAWS CLI, Monte Carlo's CloudFormation template
YouLet the agent read the secretsAWS CLI, IAM inline policy
ClaudeRegister the agent, check the secrets, create credentials, warehouses and connections, validateMCP tools (listed in step 6)

Before you start

  • Claude with the Agent Toolkit plugin, signed in to the Monte Carlo MCP server. See Agent Toolkit.
  • A Monte Carlo user who can manage integrations, and the Collection AWS account ID from Settings → Account information. This is Monte Carlo's account, not yours.
  • An AWS account where you can create CloudFormation stacks, IAM policies and Secrets Manager secrets, with the AWS CLI signed in to it.
  • A VPC with at least two private subnets that have NAT. The agent runs in them: it reaches your databases inside the VPC and Monte Carlo through NAT.
  • Postgres reachable from those subnets on its port, with a user that has the grants in Postgres, and at least one table (an empty database fails the table check).
  • Snowflake with a key-pair user that has the grants in Snowflake. If the account uses a network policy, allow your NAT gateway's public IP.
📘

Run the shell snippets on this page in bash, or keep the ${...} braces as written in zsh. In zsh, $VAR:s… is read as a substitution modifier and silently rewrites the value, which breaks ARNs.

1. Store the credentials in AWS Secrets Manager

The agent expects each secret as JSON with the connection's keys under connect_args. See Self-hosted credentials for every connection type.

Postgres. The password is read from a hidden prompt and passed to jq through the environment, so it isn't shown on screen, saved in your shell history, or visible in the process list:

REGION=us-east-1
read -r -s -p "Postgres password: " PGPASS; echo
PGPASS="$PGPASS" jq -n \
  --arg host "<postgres-host>" --argjson port 5432 \
  --arg db "<database>" --arg user "<monte-carlo-user>" \
  '{connect_args: {host: $host, port: $port, dbname: $db, user: $user, password: $ENV.PGPASS}}' \
  | aws secretsmanager create-secret --region "$REGION" \
      --name mc/postgres --secret-string file:///dev/stdin \
      --query ARN --output text
unset PGPASS

Snowflake. The private key must be an unencrypted PKCS#8 key. The secret holds the key body without its BEGIN/END lines:

KEY_FILE=<path-to-rsa_key.p8>
jq -n \
  --arg account "<account-identifier>" --arg user "<snowflake-user>" \
  --arg wh "<warehouse>" \
  --rawfile key <(grep -v -- '-----' "$KEY_FILE" | tr -d '\n') \
  '{connect_args: {account: $account, user: $user, warehouse: $wh, private_key: $key}}' \
  | aws secretsmanager create-secret --region "$REGION" \
      --name mc/snowflake --secret-string file:///dev/stdin \
      --query ARN --output text

Each command prints only the secret's ARN. Keep both ARNs for the next steps.

🔒

Never paste a password, key or token into the chat. Claude only needs the secret ARNs. If a secret does end up in the chat, Claude stops, asks you to rotate it, and continues with the ARN.

2. Ask Claude to onboard both integrations

Start the onboarding skill with what you want to connect:

/monte-carlo-onboarding connect our Postgres and our Snowflake through a new AWS agent; the credentials are in AWS Secrets Manager

Claude first shows what your account already has (deployments, agents, warehouses, connections) and checks that both integrations can be connected with self-hosted credentials. Because your credentials stay in your own AWS account, it proposes a new AWS collection agent: Monte Carlo's cloud can't read a secret you host.

3. Answer Claude's questions

Claude asks only for what it can't discover. Answer with references and network details, never secret values. For example:

1. Both secrets are in AWS Secrets Manager:
   Postgres:  arn:aws:secretsmanager:us-east-1:<account-id>:secret:mc/postgres-AbCdEf
   Snowflake: arn:aws:secretsmanager:us-east-1:<account-id>:secret:mc/snowflake-GhIjKl
   Each carries its keys under connect_args.
2. Region for the secrets and the agent: us-east-1, AWS account <account-id>.
3. Postgres is private, in VPC <vpc-id>. Snowflake: account <account-identifier>, user <snowflake-user>, warehouse <warehouse>.
4. Deploy the agent into the private subnets <subnet-a> and <subnet-b> (they have NAT).
5. Collection AWS account ID: <from Settings → Account information>.
6. Direct access: the agent's own role reads the secrets. No assumable role.
Samples may be stored by Monte Carlo.

Claude then shows the full plan and waits for your approval. After you approve, it creates the deployment and gives you its External ID.

4. Deploy the agent

These are the same steps as Create and register an AWS collection agent, done in the middle of the onboarding. If you already have an agent that can reach both databases, skip to step 5 and tell Claude to reuse it.

Claude gives you these commands with your values filled in. Short lines with variables keep them intact when copied from a terminal, where long lines can wrap and split a value.

STACK=mcd-agent
TPL=https://mcd-public-resources.s3.amazonaws.com
TPL="${TPL}/cloudformation/aws_apollo_agent.yaml"
MC_ACCOUNT=<collection-aws-account-id>
EXT_ID=<external-id-from-claude>
VPC=<vpc-id>
SUB1=<subnet-a>
SUB2=<subnet-b>

aws cloudformation create-stack \
  --region "$REGION" --stack-name "$STACK" \
  --template-url "$TPL" \
  --capabilities CAPABILITY_IAM \
  --parameters \
    ParameterKey=CloudAccountId,ParameterValue="$MC_ACCOUNT" \
    ParameterKey=ExternalId,ParameterValue="$EXT_ID" \
    ParameterKey=ExistingVpcId,ParameterValue="$VPC" \
    "ParameterKey=ExistingSubnetIds,ParameterValue=${SUB1}\,${SUB2}"

aws cloudformation wait stack-create-complete \
  --region "$REGION" --stack-name "$STACK"

aws cloudformation describe-stacks \
  --region "$REGION" --stack-name "$STACK" \
  --query "Stacks[0].Outputs[].[OutputKey,OutputValue]" \
  --output table

The \, in the subnet list is intentional: it keeps the comma inside the parameter value. Send Claude the FunctionArn and InvocationRoleArn from the outputs table; they aren't secrets. See Create and register an AWS agent for the template's other options.

5. Let the agent read the secrets

The template doesn't grant access to your secrets, so attach an inline policy to the agent's execution role. Do this before Claude checks the secrets, or both checks fail with "Could not connect".

ROLE_NAME=$(aws cloudformation describe-stack-resource \
  --region "$REGION" --stack-name "$STACK" \
  --logical-resource-id ExecutionRole \
  --query 'StackResourceDetail.PhysicalResourceId' \
  --output text)
PG_ARN=<postgres-secret-arn>
SF_ARN=<snowflake-secret-arn>

cat > mc-secrets-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "secretsmanager:GetSecretValue",
    "Resource": ["${PG_ARN}", "${SF_ARN}"]
  }]
}
EOF
aws iam put-role-policy --role-name "$ROLE_NAME" \
  --policy-name monte-carlo-secrets \
  --policy-document file://mc-secrets-policy.json
aws iam list-role-policies --role-name "$ROLE_NAME"

list-role-policies should now include monte-carlo-secrets. If a secret is encrypted with a customer-managed KMS key, also allow kms:Decrypt on that key.

6. Claude finishes the onboarding

Tell Claude the policy is attached. It then works entirely through Monte Carlo MCP tools:

StepMCP tool
Register the agent and confirm the deployment is enabledregister_aws_collection_agent, get_deployment
Test-read each secret through the agent before saving anythingvalidate_aws_secrets_manager_credentials, get_validation_run
Save the secret references (ARNs only)create_aws_secrets_manager_credentials
Create the warehousescreate_warehouse (Postgres is filed under a transactional-db warehouse)
Create the connectionscreate_connection
Validate each connectionvalidate_connection, get_validation_run

Every run ends with a summary like this one:

Created in this run
  deployment    <id>  mcd-agent  COLLECTION_AGENT/AWS  enabled=true
  agent         <id>
  Snowflake
    credentials <id>  snowflake  aws_secrets_manager
    warehouse   <id>  snowflake
    connection  <id>  snowflake  validation passed 14/14
  Postgres
    credentials <id>  postgres  aws_secrets_manager
    warehouse   <id>  transactional-db
    connection  <id>  postgres   validation passed 5/5

Cleanup if you abandon this: delete_connection → delete_warehouse →
delete_aws_secrets_manager_credentials → delete_aws_collection_agent →
delete_deployment → aws cloudformation delete-stack

Collection starts on its own; the first metadata appears within about an hour. From here you can ask Claude to set up monitors on the new tables.

Troubleshooting

SymptomCause and fix
create-stack fails with S3 error: Access Denied, or a value is split across linesThe terminal wrapped a long line when you copied it. Use the short-line version with variables above.
put-role-policy fails with MalformedPolicyDocument, and the ARNs in the file look garbledzsh read $VAR:s… as a modifier. Use ${VAR} braces, check the file with cat, and run it again.
Both secret checks fail with "Could not connect", right after registering the agentThe agent can't read the secrets yet. Attach the policy from step 5 (and kms:Decrypt for a customer-managed key), then ask Claude to re-check.
Postgres passes the connect check but fails TablesThe user has no grants on the schemas, or the database has no tables yet. Grant USAGE and SELECT per Postgres, or create a table, then ask Claude to re-check.
Snowflake fails to connectCheck the key-pair user and warehouse in the secret, and allow the NAT gateway's public IP in any Snowflake network policy.

Did this page help you?