# ContextIQ Essentials — Quickstart Guide

Deploy ContextIQ Essentials, create your first user, and start querying your knowledge bases.

## Prerequisites

- An AWS account in the **same region** for all stacks

### Who does what

| Role | What they do | What they need |
|------|-------------|----------------|
| **Admin** | Deploys/updates/deletes stacks, creates execution role and power user policy, distributes Cowork access | IAM admin permissions, AWS CLI v2, Python 3.10+ |
| **Regular user** *(optional)* | Can alternatively deploy the Essentials stack using the power user policy (created by admin). Cannot delete the stack. | Power user policy (`iam-essentials-deploy-policy.json`) attached by admin |
| **End user** | Uses ChatApp or Cowork to query knowledge bases | Python 3.10+ (no AWS CLI), connection config or URL from admin, Cognito credentials from admin |

- **Admin users** deploy directly — follow Steps 1–4 below
- **Regular users** — if an admin wants to delegate deployment, see [Appendix: Delegated Deployment](#appendix-delegated-deployment-regular-users)
- **End users** connect via Cowork or ChatApp — see [Distribute Cowork Access](#distribute-cowork-access-to-your-team)

---

## Deployment

### Step 1 — Deploy the Essentials Stack

This deploys the full ContextIQ environment (~30–35 minutes):

- **Networking:** VPC, subnets, and Application Load Balancer
- **Knowledge Bases:** Two Amazon Bedrock knowledge bases — **OpenSearch** (for regulatory docs, reference material) and **S3 Vectors** (for internal analysis, proprietary content) — each with a dedicated S3 document bucket and pre-loaded sample documents
- **MCP Servers:** Containerized query services running on ECS, fronted by the ALB
- **Auth:** Amazon Cognito user pool with pre-created access groups for knowledge base entitlements
- **Chat App:** Amplify-hosted web interface for querying your knowledge bases
- **Admin User:** Auto-created from the AdminEmail and AdminPassword you provide below

**Quick Create:** Copy the link below and open it in your browser. CloudFormation will open with the stack name and template pre-filled.

```
https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?stackName=contextiq-essentials&templateURL=https://contextiq-releases.s3.us-west-2.amazonaws.com/essentials/latest/templates/quickstart.yaml
```

On the Quick Create page:

1. Fill in **AdminEmail** — your email address for the initial admin account
2. Fill in **AdminPassword** — a temporary password (must include uppercase, lowercase, number, and special character)
3. All other parameters have sensible defaults — no changes needed
4. Check the **IAM capabilities** acknowledgment checkbox
5. Click **Create stack**

<details>
<summary>CLI alternative</summary>

```bash
aws cloudformation create-stack \
  --stack-name contextiq-essentials \
  --template-url https://contextiq-releases.s3.us-west-2.amazonaws.com/essentials/latest/templates/quickstart.yaml \
  --parameters ParameterKey=AdminEmail,ParameterValue=admin@yourcompany.com \
               ParameterKey=AdminPassword,ParameterValue='YourTempPass1!' \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --region {{REGION}}

aws cloudformation wait stack-create-complete \
  --stack-name contextiq-essentials --region {{REGION}}
```

</details>

---

## Admin Setup

### Step 2 — Verify with the ChatApp

The included Chat App is a lightweight web interface for basic testing — use it to verify that the deployment is working correctly in your environment. For production use, we recommend connecting via **Claude Desktop (Cowork)** in [Step 3](#step-3--connect-claude-desktop-cowork), which provides a richer experience with multi-turn conversations and full MCP integration.

1. Open the Chat App URL from CloudFormation outputs: [{{CHAT_APP_URL}}]({{CHAT_APP_URL}})
2. Click **Sign In** → enter the admin email and temporary password you provided during stack creation
3. Set a permanent password on first sign-in
4. Try sample queries to confirm the knowledge bases are responding properly (see [Step 4](#step-4--try-sample-queries))

**From AWS Console:** CloudFormation → Stacks → `contextiq-essentials` → Outputs tab → open `ChatAppUrl`.


### Step 3 — Connect Claude Desktop (Cowork)

This step verifies the deployment by connecting the admin's Claude Desktop (Cowork) to ContextIQ. Once verified, use the [Distribute Cowork Access to Your Team](#distribute-cowork-access-to-your-team) section below to onboard end users.

Claude Desktop (Cowork) is the preferred client for ContextIQ Essentials. It connects to your ContextIQ knowledge bases through a local MCP proxy that runs on your machine and communicates with the ContextIQ backend in your VPC.

**Download and extract the Cowork package:**

```bash
aws s3 cp s3://contextiq-releases/essentials/latest/cowork/contextiq-cowork.tar.gz .
tar xzf contextiq-cowork.tar.gz
cd contextiq-cowork
```

**Run the setup script** (same email and permanent password you set in Step 2):

```bash
./contextiq-setup --username <your@email.com>
```

The script discovers your deployment from CloudFormation, authenticates with Cognito, and writes the Claude Desktop configuration automatically.

**Restart Claude Desktop.** The ContextIQ tool will appear in your MCP connectors.

> **Important:** In Claude Desktop, open the ContextIQ connector settings and change the approval mode from **Needs approval** to **Always Allow** for a seamless experience.

In the cowork start with query - Tell me about ContextIQ. This will make Cowork load ContextIQ MCP server for further queries.

See [Step 4](#step-4--try-sample-queries) for sample queries.

### Distribute Cowork Access to Your Team

Once you've verified Cowork is working (Step 3), onboard your team without requiring them to have AWS CLI or backend access.

**1. Create Cognito accounts** for each user (see [User Management](#user-management--cognito) below). ContextIQ Essentials uses standalone Cognito user pools for authentication. To integrate with your enterprise Active Directory, SAML, or SSO provider, contact us about ContextIQ Enterprise at hello@contextiq.us.

**2. Generate and distribute a connection config** using one of these methods:

**Option A — Automated (S3 + email):** Publish the config to S3 and email setup instructions to all users in one command:

```bash
./contextiq-setup --export-config --publish \
  --email-to user1@co.com,user2@co.com \
  --sender-email admin@company.com
```

Each user receives an email with a pre-signed setup link. They run `./contextiq-setup --url <link>`, enter their password, and restart Cowork. Requires Amazon SES configured in your account (not deployed by ContextIQ — see [SES documentation](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html) to set up).

**Option B — Share a link:** Publish to S3 and share the URL via Slack, Teams, or your internal wiki:

```bash
./contextiq-setup --export-config --publish
```

**Option C — Email config as attachment:** Generate the config file and send it yourself:

```bash
./contextiq-setup --export-config
```

Writes `contextiq-connection.json` to the current directory. Email it to users — they save it to their Downloads folder and run `./contextiq-setup`. The script auto-detects the file.

**End user experience (all options):** Users install the ContextIQ Cowork plugin, run `contextiq-setup` with the link or config file provided, enter their username and password, and restart Cowork. No AWS CLI or backend access needed.

> **Note:** First-time users must sign in via the ChatApp first to set a permanent password (Cognito requires a password change on first login). After that, `contextiq-setup` will work with the permanent password.

---

## Try It Out

### Step 4 — Try Sample Queries

ContextIQ Essentials ships with sample documents pre-loaded into both knowledge bases.

**Verify the connection** by asking Claude Desktop:

```
Tell me about ContextIQ and what knowledge sources do you have access to.?
```

You should see `contextiq-essentials` in the server list.

> **Tip:** On first use, Claude Desktop may not automatically invoke the ContextIQ tool. If you don't see results from ContextIQ, prefix your first query with "From ContextIQ, ..." to prompt Claude to upload the tool first time— e.g. *"From ContextIQ, what are our current capital ratios?"*. After the first successful tool call, Claude will use it automatically for subsequent queries.

Once confirmed, try these sample queries from the Chat App or Claude Desktop based on the pre-ingested sample data files:

```
What are our current capital ratios and how much buffer do we have under stress?

What is our exposure to office commercial real estate and should we be concerned?

Are there any issues with our credit loss model and what does the regulator expect?

What is the minimum CET1 ratio under Basel III including the conservation buffer?

```

<details>
<summary>Example response — credit loss model query</summary>

The credit loss model query demonstrates multi-source retrieval. ContextIQ automatically searches both knowledge bases and synthesizes a unified answer:

**From OpenSearch KB** (regulatory docs): OCC CRE guidance (OCC 2024-03), Basel III capital requirements, Model Risk Management standards (SR 11-7 / OCC 2011-12)

**From S3 Vectors KB** (internal analysis): Model Validation Report for the CECL Credit Loss Model (ACL-01)

Sample response highlights:

- **Model status**: ACL-01 rated "Satisfactory with Findings" — Tier 1 (Critical) CECL model using PD/LGD/EAD sub-models with three-scenario weighting (baseline 50%, adverse 30%, severely adverse 20%)
- **Regulatory expectations**: OCC 2024 exam priorities include ACL methodology for CRE segments, stress testing quality, and qualitative adjustment justification
- **Key concerns**: CRE qualitative adjustments, scenario severity for office sector structural decline, and macroeconomic variable coverage
- **Sources cited**: model-validation-report-credit-loss.md, occ-cre-guidance-2024.md, model-risk-management-sr1107.md

This shows how ContextIQ retrieves from both regulatory reference material (OpenSearch) and proprietary internal documents (S3 Vectors) to provide grounded, cited answers.

</details>

### Step 5 — Compound Queries with Cowork (Claude Desktop)

Cowork enables a category of queries that the Chat App cannot support: **compound queries** that retrieve data from your enterprise knowledge bases and synthesize it with the latest publicly available data from the web to produce answers that neither source could provide alone.

These are not simple lookups. Each compound query triggers a multi-step workflow:

1. **Enterprise retrieval** — Claude calls ContextIQ to search your private knowledge bases (internal memos, risk assessments, model reports, regulatory guidance)
2. **Web research** — Claude searches the publicly available data on internet for the latest market data, industry reports, regulatory updates, or peer benchmarks
3. **Cross-source synthesis** — Claude synthesizes both data sets, identifies gaps or discrepancies, and produces an analytical response with actionable recommendations

This is the type of analysis that typically requires an analyst to spend hours manually pulling market reports and cross-referencing them against internal documents. With ContextIQ + Cowork, it happens in a single query.

#### Featured Example — CRE Vacancy Gap Analysis

```
Based on our CRE portfolio exposure and market positions, compare our office vacancy
assumptions against the latest publicly available CBRE or JLL market reports.
Are we being conservative enough?
```

<details>
<summary>What to expect</summary>

**Step 1 — Enterprise retrieval (ContextIQ):** Claude retrieves your internal CRE Portfolio Risk Assessment memo containing office vacancy assumptions by market (Chicago 22.1%, Dallas 18.8%, Denver 20.3%), concentration levels ($10.8B, 24% of total loans), and the OCC's guidance on CRE stress testing scenarios (OCC 2024-03).

**Step 2 — Web research:** Claude searches for the latest office market reports from CBRE, JLL, Cushman & Wakefield, and industry publications covering your specific footprint markets.

**Step 3 — Gap analysis:** Claude produces a market-by-market comparison table:

| Market          | Internal Assumption (Q3 2024) | Latest Public Data (Q1 2026) | Gap              |
|-----------------|-------------------------------|------------------------------|------------------|
| Chicago CBD     | 22.1%                         | ~24.3%                       | ~220 bps under   |
| Dallas          | 18.8%                         | ~24.5%                       | ~570 bps under   |
| Denver downtown | 20.3%                         | ~38.9%                       | ~1,860 bps under |

**Key findings:**

- **Denver is materially understated** — the internal assumption of 20.3% vacancy is nearly 1,900 bps below the actual downtown rate, which already exceeds the OCC's recommended 20-30% stress scenario range in non-stress conditions
- **Property class bifurcation matters** — Denver Class A vacancy is 19.9% while Class B/C is 44.4%, a 24-point spread that a single market-level assumption completely masks
- **Dallas is higher than assumed but improving** — the level is 570 bps worse but momentum has reversed with positive absorption and record Q1 leasing
- **National picture is better than internal tone suggested** — three consecutive quarters of positive net absorption and +2.2% YoY rent growth, though concentrated in prime/gateway markets

**Actionable recommendations included in the response:**

- Refresh office vacancy assumptions in the CRE memo and CECL qualitative overlay against current market data
- Recalibrate the severely adverse stress scenario — if Denver actual vacancy already exceeds the modeled stress case, the scenario lacks severity
- Decompose the office book by property class (A vs B/C) since market-level averages are hiding concentration risk
- Normalize data definitions (overall vs direct vs prime vacancy) before reporting to the Board Risk Committee

**Sources cited:** Internal CRE Portfolio Risk Assessment memo, OCC 2024-03 guidance (from ContextIQ), plus Q1 2026 market reports from CBRE, JLL, Cushman & Wakefield, Colorado Sun, The Real Deal, and CFO Dive (from web)

</details>

#### More Compound Queries

These queries follow the same pattern — retrieve from your enterprise knowledge bases, augment with public data, and synthesize:

```
Our CECL model uses a 28% equity market decline in the severely adverse scenario.
How does that compare to actual drawdowns in the 2008 and 2020 crises?
Are our stress assumptions severe enough?
```

```
We have $10.8B in CRE commitments with 24% concentration.
How does that compare to peer banks of similar asset size?
Are we above or below OCC screening thresholds?
```

```
Our model validation report rated the CECL model "Satisfactory with Findings."
What are the most common MRM findings cited in recent OCC and Fed enforcement actions?
Do any of our findings overlap?
```

#### Combining ContextIQ with Other MCP Servers

Claude Desktop supports multiple MCP server connections simultaneously. You can combine ContextIQ with other MCP data sources — such as [Bigdata.com](https://bigdata.com) for structured financial data, Google Drive for internal documents, or Slack for team context — to create even richer compound queries.

Example using ContextIQ + Bigdata.com:

```
From ContextIQ, what are our largest CRE loan exposures by sponsor?
Then from Bigdata.com, pull the latest financial health and news sentiment
for these publicly traded sponsors. Are any showing signs of distress that
should trigger a watchlist review?
```

This query retrieves your internal loan portfolio data from ContextIQ, then uses Bigdata.com to pull real-time financial health indicators and news sentiment for each sponsor — producing a watchlist risk assessment that combines proprietary exposure data with live market intelligence. Any MCP-compatible data source can be combined in the same way.

#### Fan-Out Queries — Comparing Knowledge Base Retrieval

ContextIQ Essentials includes two knowledge base backends with different retrieval strengths:

- **OpenSearch** — lexical/keyword search (BM25 scoring). Best for navigating regulatory corpora, matching specific terms, and finding documents with exact phrase overlap.
- **S3 Vectors** — semantic/dense vector search (cosine similarity). Best for conceptual matching, finding related documents even when the wording differs, and connecting external standards to internal assessments.

You can query both backends explicitly for the same question and compare results side by side. This is useful for auditing retrieval quality, understanding which backend serves a given question class better, and validating that documents are indexed correctly across both stores.

```
Query ContextIQ twice for "What is the minimum CET1 ratio under Basel III
and what happens when a bank breaches the conservation buffer?" —
once with source="opensearch" and once with source="s3vectors".
Show me the results side by side.
```

<details>
<summary>What to expect</summary>

Claude calls the ContextIQ tool twice — once targeting each backend — with the exact same query. Both calls return the same primary document (Basel III Capital Requirements, indexed in both knowledge bases), confirming retrieval consistency.

The key insight is in the **second-ranked companion chunk**, which reveals how each engine reasons about "related context":

| Dimension | OpenSearch                                             | S3 Vectors                                              |
|-----------|--------------------------------------------------------|---------------------------------------------------------|
| Top hit   | Basel III Capital Requirements (BIS)                   | Basel III Capital Requirements (BIS)                    |
| Score #1  | 0.0040                                                 | 0.8693                                                  |
| 2nd hit   | OCC 2024-03 — CRE Lending Guidance                    | Internal Capital Adequacy Assessment — Q4 2024          |
| Score #2  | 0.00256                                                | 0.6947                                                  |
| Latency   | 372 ms                                                 | 265 ms                                                  |
| Scoring   | Lexical (BM25-like, unbounded scores)                  | Semantic (cosine-similarity, 0-1 bounded)               |

**Why the companion chunks differ:**

- **OpenSearch** found another regulatory document (OCC 2024-03 CRE Lending Guidance) because it shares vocabulary tokens like "capital," "ratio," "buffer," and "banks" — this is lexical/keyword reasoning
- **S3 Vectors** found the internal Q4 2024 Capital Adequacy Assessment because semantic embeddings recognized "CET1 ratio" and "conservation buffer" as conceptually related to the bank's actual capital position document — even though that doc isn't a Basel III explainer

**Practical implications:**

- Use `opensearch` when you want regulatory-only context with no internal data blending in (e.g., training material, policy questions, public-facing answers)
- Use `s3vectors` when you want internal context blended with the regulatory framing (e.g., "what does this mean for us")
- The scoring scales are not comparable — OpenSearch returns BM25-style relevance scores (raw, unbounded, depend on term frequency / IDF) while S3 Vectors returns cosine-similarity scores bounded by 1.0. Rank order is comparable, magnitude is not. Do not apply the same threshold to both.
- Both backends return results well within interactive-query range — no meaningful latency tradeoff

</details>

---

## Managing Your Data

### Upload Documents

Each knowledge base has a dedicated S3 bucket. Bedrock automatically ingests uploaded documents.

**From AWS Console:** S3 → navigate to the bucket → Upload.

<details>
<summary>CLI alternative</summary>

```bash
# Upload to OpenSearch KB (regulatory docs, reference material)
aws s3 cp your-docs/ s3://{{OPENSEARCH_DOCS_BUCKET}}/ --recursive

# Upload to S3 Vectors KB (internal analysis, proprietary content)
aws s3 cp your-docs/ s3://{{S3VECTORS_DOCS_BUCKET}}/ --recursive
```

To manually trigger a sync:

**From AWS Console:** Amazon Bedrock → Knowledge bases → select KB → Data source → Sync.

</details>

### Supported Formats

PDF, TXT, MD, HTML, DOC/DOCX, CSV, XLS/XLSX. Documents are chunked (500 tokens, 20% overlap) and embedded using Amazon Titan Embed Text V2.

<details>
<summary>Remove demo data</summary>

```bash
aws s3 rm s3://{{OPENSEARCH_DOCS_BUCKET}}/demo/ --recursive
aws s3 rm s3://{{S3VECTORS_DOCS_BUCKET}}/demo/ --recursive

aws bedrock-agent start-ingestion-job \
  --knowledge-base-id {{OPENSEARCH_KB_ID}} \
  --data-source-id <DataSourceId> --region {{REGION}}

aws bedrock-agent start-ingestion-job \
  --knowledge-base-id {{S3VECTORS_KB_ID}} \
  --data-source-id <DataSourceId> --region {{REGION}}
```

</details>

---

## User Management — Cognito

Users and access are managed through Amazon Cognito. Your Cognito User Pool ID is available in the stack outputs (`CognitoUserPoolId`).

### Knowledge Base Access Groups

Three entitlement groups are pre-created during deployment. Each user must be assigned to a group to access knowledge bases:

| Group                  | Access                                        |
|------------------------|-----------------------------------------------|
| `contextiq-all-kb`     | Both OpenSearch and S3Vectors knowledge bases  |
| `contextiq-opensearch` | OpenSearch knowledge base only                 |
| `contextiq-s3vectors`  | S3Vectors knowledge base only                  |

### Add a User

**From AWS Console:** Cognito → User pools → select your pool → Users tab → Create user → enter email and temporary password. After creating, select the user → Group memberships → Add to group → select the appropriate group.

<details>
<summary>CLI alternative</summary>

```bash
aws cognito-idp admin-create-user \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --username newuser@company.com \
  --temporary-password 'TempPass1!' \
  --user-attributes Name=email,Value=newuser@company.com Name=email_verified,Value=true \
  --message-action SUPPRESS \
  --region {{REGION}}

aws cognito-idp admin-add-user-to-group \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --username newuser@company.com \
  --group-name contextiq-all-kb \
  --region {{REGION}}
```

</details>

### List Users

**From AWS Console:** Cognito → User pools → select your pool → Users tab.

<details>
<summary>CLI alternative</summary>

```bash
aws cognito-idp list-users --user-pool-id {{COGNITO_USER_POOL_ID}} --region {{REGION}}
```

</details>

### Remove a User

**From AWS Console:** Cognito → User pools → select your pool → Users tab → select the user → Delete user.

<details>
<summary>CLI alternative</summary>

```bash
aws cognito-idp admin-delete-user \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --username user@company.com \
  --region {{REGION}}
```

</details>

### Change a User's Group

**From AWS Console:** Cognito → User pools → select your pool → Users tab → select the user → Group memberships → remove from current group → add to new group.

<details>
<summary>CLI alternative</summary>

```bash
aws cognito-idp admin-remove-user-from-group \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --username user@company.com \
  --group-name contextiq-opensearch \
  --region {{REGION}}

aws cognito-idp admin-add-user-to-group \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --username user@company.com \
  --group-name contextiq-all-kb \
  --region {{REGION}}
```

</details>

### Bulk Import

**From AWS Console:** Cognito → User pools → select your pool → Users tab → Import users → Download CSV template → fill in rows → Upload and import. After import, assign users to groups individually.

<details>
<summary>CLI alternative</summary>

```bash
aws cognito-idp get-csv-header \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --region {{REGION}}

aws cognito-idp create-user-import-job \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --job-name bulk-import \
  --cloud-watch-logs-role-arn <CloudWatchLogsRoleArn> \
  --region {{REGION}}

aws cognito-idp start-user-import-job \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --job-id <job-id-from-previous-command> \
  --region {{REGION}}
```

</details>

---

## Uninstall

> **Admin only.** Stack deletion must be performed by a user with admin IAM permissions. Regular users with the power user policy do not have sufficient permissions to clean up all resources during deletion. Failed deletions can leave orphaned resources (OpenSearch policies, Bedrock Knowledge Bases, IAM roles) that require admin access to remove.

Delete stacks in this order:

1. **Delete the Essentials stack:** CloudFormation → Stacks → select `contextiq-essentials` → Delete. Wait for `DELETE_COMPLETE` (~10–15 minutes).
2. **Delete the Execution Role stack (if deployed via [Delegated Deployment](#appendix-delegated-deployment-regular-users)):** CloudFormation → Stacks → select `contextiq-execution-role` → Delete.

> **Note:** If stack deletion fails on S3 buckets (e.g., `DELETE_FAILED` on document buckets), manually empty and delete the buckets from the S3 console, then retry the stack deletion. CloudFormation cannot delete non-empty S3 buckets.

<details>
<summary>CLI alternative</summary>

```bash
aws cloudformation delete-stack --stack-name contextiq-essentials --region {{REGION}}
aws cloudformation wait stack-delete-complete --stack-name contextiq-essentials --region {{REGION}}

# Only if execution role stack was deployed (Delegated Deployment)
aws cloudformation delete-stack --stack-name contextiq-execution-role --region {{REGION}}
aws cloudformation wait stack-delete-complete --stack-name contextiq-execution-role --region {{REGION}}
```

</details>

---

## Upgrade

> **Admin only.** Upgrades require stack deletion and redeployment — see [Uninstall](#uninstall) requirements.

### Full Version Upgrade

A full version upgrade requires uninstalling the current deployment and redeploying. Follow the [Uninstall](#uninstall) steps first, then redeploy using [Step 1](#step-1--deploy-the-essentials-stack) with the new version's template URL.

User data in Cognito (users and groups) is preserved across reinstalls. Knowledge base content must be re-ingested after a full upgrade.

### Patch Update (Container Images)

Patch updates push new container images without redeploying the full stack. These are applied via a provided update script that refreshes the ECS task definitions with the latest image tags.

```bash
./contextiq-patch-update --region {{REGION}}
```

Patch updates do not affect user data or knowledge base content.

---

## Appendix: Delegated Deployment (Regular Users)

If an admin wants to delegate stack deployment to a regular user (non-admin), follow these steps. The regular user can deploy the Essentials stack but **cannot delete or update it** — those operations require admin permissions.

### A1 — Admin: Deploy the Execution Role Stack

This stack creates a scoped IAM execution role (`contextiq-cfn-service-role`) that CloudFormation assumes to deploy ContextIQ resources on behalf of a regular user. Deploy this once per account.

**Quick Create:** Copy the link below and open it in your browser.

```
https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?stackName=contextiq-execution-role&templateURL=https://contextiq-releases.s3.us-west-2.amazonaws.com/essentials/latest/templates/contextiq-execution-role.yaml
```

On the Quick Create page:

1. Leave all parameters at their defaults
2. Check the **IAM capabilities** acknowledgment checkbox
3. Click **Create stack**

Wait for `CREATE_COMPLETE` (~1–2 minutes).

<details>
<summary>CLI alternative</summary>

```bash
aws cloudformation create-stack \
  --stack-name contextiq-execution-role \
  --template-url https://contextiq-releases.s3.us-west-2.amazonaws.com/essentials/latest/templates/contextiq-execution-role.yaml \
  --capabilities CAPABILITY_NAMED_IAM \
  --region {{REGION}}

aws cloudformation wait stack-create-complete \
  --stack-name contextiq-execution-role --region {{REGION}}
```

</details>

### A2 — Admin: Attach the Power User Policy

Create the following IAM policy (`iam-essentials-deploy-policy.json`) and attach it to the regular user's IAM user or role. Replace `<account-id>` with your AWS account ID.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFormationFullAccess",
      "Effect": "Allow",
      "Action": "cloudformation:*",
      "Resource": "*"
    },
    {
      "Sid": "AllowPassExecutionRoleToCloudFormation",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::<account-id>:role/contextiq-cfn-service-role",
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "cloudformation.amazonaws.com"
        }
      }
    },
    {
      "Sid": "AllowListRolesForConsoleSelection",
      "Effect": "Allow",
      "Action": "iam:ListRoles",
      "Resource": "*"
    }
  ]
}
```

The `ListRoles` permission is needed to see `contextiq-cfn-service-role` in the CloudFormation console Permissions dropdown.

### A3 — Regular User: Deploy the Essentials Stack

Follow [Step 1 — Deploy the Essentials Stack](#step-1--deploy-the-essentials-stack) with one additional step:

> [!IMPORTANT]
> Scroll down to **Permissions** and select **`contextiq-cfn-service-role`** from the IAM role dropdown. **The deployment will fail without this step.** If you don't see the role, click the refresh button next to the dropdown, or verify that your admin completed step A1.

<details>
<summary>CLI alternative</summary>

```bash
aws cloudformation create-stack \
  --stack-name contextiq-essentials \
  --template-url https://contextiq-releases.s3.us-west-2.amazonaws.com/essentials/latest/templates/quickstart.yaml \
  --role-arn arn:aws:iam::<account-id>:role/contextiq-cfn-service-role \
  --parameters ParameterKey=AdminEmail,ParameterValue=admin@yourcompany.com \
               ParameterKey=AdminPassword,ParameterValue='YourTempPass1!' \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --region {{REGION}}

aws cloudformation wait stack-create-complete \
  --stack-name contextiq-essentials --region {{REGION}}
```

</details>

> **Important:** Stack deletion and updates must be performed by an admin. If a deployment fails and rolls back, orphaned resources may require admin-level permissions to clean up. The pre-flight cleanup Lambda handles this automatically on the next deployment attempt, but manual stack deletion should always be done by an admin.

---

## Appendix: Troubleshooting

<details>
<summary>S3 bucket "already exists" error</summary>

If you see `already exists` on an S3 bucket resource (e.g., `OpenSearchDocsBucketA70A6C98`), a bucket from a previous deployment was not cleaned up. Go to S3 console → find the bucket (e.g., `tmx-contextiq-docs-opensearch-<account-id>-<region>`) → Empty → Delete, then retry the stack deployment.

This typically happens after a failed deployment where the rollback could not delete the bucket because it contained objects.

</details>

<details>
<summary>Execution role "already exists" error</summary>

If you see `A policy called contextiq-cfn-* already exists` when deploying the execution role stack, IAM resources from a previous deployment were not cleaned up. Go to IAM console → Policies → search for `contextiq-cfn-` → delete all matching policies. Also delete the role `contextiq-cfn-service-role` if it exists. Then retry the stack creation.

If the execution role stack already exists in CloudFormation, **update** it instead of creating a new one.

</details>

<details>
<summary>Deployed without selecting the execution role (regular users)</summary>

If a regular user deploys the Essentials stack without selecting `contextiq-cfn-service-role` in the Permissions dropdown, the stack will begin creating resources using the user's own IAM permissions. It will partially succeed then fail when it hits a resource the user doesn't have permissions to create. The stack will enter `ROLLBACK_IN_PROGRESS` and then likely `ROLLBACK_FAILED` or `DELETE_FAILED` because the user also lacks permissions to delete the resources that were created.

**Symptoms:**
- Stack status: `ROLLBACK_FAILED` or `DELETE_FAILED`
- Events show errors like: `User: arn:aws:iam::<account>:user/<username> is not authorized to perform: <action>`
- Attempting to delete the stack fails on IAM roles, ECR pull-through cache rules, or nested stacks

**Recovery steps:**

1. **Delete the stack while retaining the failed resources.** Identify the logical resource IDs that failed to delete from the Events tab, then run:

```bash
aws cloudformation delete-stack \
  --stack-name contextiq-essentials \
  --retain-resources DocsBucketsStack PublicEcrPullThroughCache ECRCacheWarmBuildRole DemoSeederRole CleanupBucketRole \
  --region {{REGION}}
```

2. **If a nested stack is also stuck in DELETE_FAILED** (e.g., `contextiq-essentials-DocsBucketsStack-XXXX`), delete it the same way:

```bash
# Find the nested stack name
aws cloudformation list-stacks \
  --stack-status-filter DELETE_FAILED \
  --query 'StackSummaries[].StackName' \
  --output text --region {{REGION}}

# Delete it, retaining the failed resource
aws cloudformation delete-stack \
  --stack-name <nested-stack-name> \
  --retain-resources <failed-logical-resource-id> \
  --region {{REGION}}
```

3. **Clean up orphaned resources manually.** Check for and remove:

```bash
# ECR pull-through cache rules
aws ecr describe-pull-through-cache-rules --region {{REGION}}
aws ecr delete-pull-through-cache-rule --ecr-repository-prefix ecr-public --region {{REGION}}

# Orphaned IAM roles (look for contextiq- prefixed roles, NOT contextiq-cfn-service-role)
aws iam list-roles --query 'Roles[?contains(RoleName, `contextiq`)].RoleName' --output text

# Orphaned S3 buckets
aws s3 ls | grep contextiq
```

4. **Redeploy with the execution role.** Once the account is clean, redeploy — this time selecting `contextiq-cfn-service-role` in the Permissions dropdown.

</details>

<details>
<summary>Stack creation fails (general)</summary>

**From AWS Console:** CloudFormation → Stacks → `contextiq-essentials` → Events tab → filter by `CREATE_FAILED`.

```bash
aws cloudformation describe-stack-events \
  --stack-name contextiq-essentials \
  --query 'StackEvents[?ResourceStatus==`CREATE_FAILED`].[LogicalResourceId,ResourceStatusReason]' \
  --output table --region {{REGION}}
```

</details>

<details>
<summary>ALB returns 503</summary>

ECS services may still be starting. Wait 5–10 minutes and check service status:

```bash
aws ecs describe-services \
  --cluster contextiq-cluster \
  --services contextiq-query-service \
  --query 'services[0].{Status:status,Running:runningCount,Desired:desiredCount}' \
  --region {{REGION}}
```

</details>

<details>
<summary>Chat App shows "Loading authentication configuration..."</summary>

The Amplify build may not be complete. Wait a few minutes and refresh.

</details>

<details>
<summary>"Access denied" when signing in</summary>

Verify the user exists and check their status:

```bash
aws cognito-idp admin-get-user \
  --user-pool-id {{COGNITO_USER_POOL_ID}} \
  --username user@company.com \
  --region {{REGION}}
```

Users in `FORCE_CHANGE_PASSWORD` status need to complete their first sign-in to set a permanent password.

</details>

<details>
<summary>Entitlement groups missing</summary>

If the three groups (`contextiq-all-kb`, `contextiq-opensearch`, `contextiq-s3vectors`) are missing:

**From AWS Console:** CloudFormation → Stacks → filter by "Access" → Events tab.

</details>

<details>
<summary>Cowork issues</summary>

- **"Session expired"** — Re-run `./contextiq-setup` to refresh tokens. Tokens auto-refresh for 30 days.
- **"No connection config found"** — Save `contextiq-connection.json` to `~/Downloads/` or `~/.contextiq/`, or use `--config` / `--url` flag. End users without AWS CLI need a config file or URL from their admin.
- **"Could not resolve ALB DNS"** — Verify the Essentials stack is deployed and your AWS credentials have CloudFormation read access. End users should use a connection config instead of CloudFormation discovery.
- **Tools not appearing in Claude** — Restart Claude Desktop after setup. Verify `contextiq-mcp-proxy` is executable (`chmod +x contextiq-mcp-proxy`).

</details>

<details>
<summary>Verify deployment health</summary>

```bash
curl -s http://{{ALB_DNS_NAME}}:8005/health   # Query MCP Server
curl -s http://{{ALB_DNS_NAME}}:8001/health   # OpenSearch MCP Server
curl -s http://{{ALB_DNS_NAME}}:8002/health   # S3 Vectors MCP Server
```

**From AWS Console:** ECS → Clusters → `contextiq-cluster` → Services tab. All services should show Running count matching Desired count.

</details>

<details>
<summary>Execution role test (optional)</summary>

Validate the execution role before deploying:

```bash
aws cloudformation create-stack \
  --stack-name contextiq-smoke-test \
  --template-body file://smoke-test.yaml \
  --role-arn <ServiceRoleArn> \
  --capabilities CAPABILITY_NAMED_IAM \
  --region {{REGION}}
```

Creates minimal resources (~2 minutes). If it succeeds, the service role is ready.

```bash
aws cloudformation delete-stack --stack-name contextiq-smoke-test --region {{REGION}}
```

</details>
