feat: refactor Amazon Q module to use agentAPI (#362)

### **Title:**
feat: complete amazon-q module v2.0.0 with comprehensive enhancements


### **Description:**
Closes #240

This PR introduces a complete rewrite and enhancement of the amazon-q
module, bringing it to version 2.0.0. The module now provides AgentAPI
support.

## Type of Change

- [ ] New module
- [ ] Bug fix
- [x] Feature/enhancement
- [x] Documentation
- [ ] Other

## Module Information

**Path:** `registry/coder/modules/amazon-q`
**New version:** `v2.0.0`
**Breaking change:** [x] Yes [ ] No

## Key Features & Enhancements

### 🚀 Core Functionality
- **AgentAPI Support**: Web and CLI app integration with health checks
- **Amazon Q CLI Integration**: Automatic installation and configuration
of Amazon Q CLI
- **MCP Integration**: Model Context Protocol support for task reporting
to Coder
- **Authentication System**: Tarball-based authentication with
environment variable management

### 🛠️ Customization & Configuration
- **Pre/Post Install Scripts**: Support for custom setup and
finalization scripts
- **Agent Configuration**: Templated agent config with tool and resource
management
- **Custom System Prompts**: Configurable AI behavior and task reporting
instructions
- **Version Pinning**: Support for specific Amazon Q CLI and AgentAPI
versions

### 📚 Documentation & Testing
- **Comprehensive README**: Complete user guide with examples,
configuration details, and troubleshooting
- **Visual Documentation**: Updated screenshots and interface examples
- **Terraform Testing**: Complete .tftest.hcl with 8 test cases (all
passing)
- **Registry Compliance**: Full adherence to Coder Registry contributing
guidelines

d## Breaking Changes

This is a major version update (v2.0.0) with breaking changes:
- Renamed variables names (Removed experimantal_ prefix)
- Updated AgentAPI integration method
- Modified default configuration structure

## Testing & Validation

- [x] Tests pass (`terraform test` - 8/8 tests passing)
- [x] Code formatted (`bun run fmt`)
- [x] Changes tested locally
- [x] Registry compliance verified
- [x] Documentation reviewed and updated

## Related Issues

Closes #240 - Amazon Q module enhancement request

## Additional Notes

- Module is now production-ready with professional quality code and
documentation
- Full compliance with Coder Registry contributing guidelines
- Comprehensive test coverage ensures reliability
- Ready for registry submission and community use

## Screenshots:
<img width="3001" height="1068" alt="image"
src="https://github.com/user-attachments/assets/24453cb3-d4dc-4a45-bb62-7a834940ebae"
/>
<img width="1209" height="600" alt="image"
src="https://github.com/user-attachments/assets/f2b18c42-ba7f-4e16-a9e7-d51ad1095712"
/>
<img width="1505" height="1251" alt="image"
src="https://github.com/user-attachments/assets/3e6e49b1-808d-482e-a237-b606e50262f5"
/>


https://github.com/user-attachments/assets/6533dead-35f1-47f5-875a-3cebb81453c9



https://github.com/user-attachments/assets/da8047f6-7023-4e6c-af90-138541298089

/claim #240

Co-authored-by: Michael Orlov <michaelo@amdocs.com>
This commit is contained in:
Michael Orlov
2025-09-10 19:50:22 -04:00
committed by GitHub
parent f1010ee7a6
commit 16015559e2
8 changed files with 1660 additions and 325 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 976 KiB

+346 -70
View File
@@ -1,23 +1,26 @@
---
display_name: Amazon Q
description: Run Amazon Q in your workspace to access Amazon's AI coding assistant.
description: Run Amazon Q in your workspace to access Amazon's AI coding assistant with MCP integration and task reporting.
icon: ../../../../.icons/amazon-q.svg
verified: true
tags: [agent, ai, aws, amazon-q]
tags: [agent, ai, aws, amazon-q, tasks]
---
# Amazon Q
Run [Amazon Q](https://aws.amazon.com/q/) in your workspace to access Amazon's AI coding assistant. This module installs and launches Amazon Q, with support for background operation, task reporting, and custom pre/post install scripts.
Run [Amazon Q](https://aws.amazon.com/q/) in your workspace to access Amazon's AI coding assistant. This module provides a complete integration with Coder workspaces, including automatic installation, MCP (Model Context Protocol) integration for task reporting, and support for custom pre/post install scripts.
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "1.1.2"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
# Required: see below for how to generate
experiment_auth_tarball = var.amazon_q_auth_tarball
# Required: Authentication tarball (see below for generation)
auth_tarball = <<-EOF
base64encoded-tarball
EOF
}
```
@@ -25,97 +28,370 @@ module "amazon-q" {
## Prerequisites
- You must generate an authenticated Amazon Q tarball on another machine:
```sh
cd ~/.local/share/amazon-q && tar -c . | zstd | base64 -w 0
```
Paste the result into the `experiment_auth_tarball` variable.
- To run in the background, your workspace must have `screen` or `tmux` installed.
- **zstd** - Required for compressing the authentication tarball
- **Ubuntu/Debian**: `sudo apt-get install zstd`
- **RHEL/CentOS/Fedora**: `sudo yum install zstd` or `sudo dnf install zstd`
- **auth_tarball** - Required for installation and authentication
<details>
<summary><strong>How to generate the Amazon Q auth tarball (step-by-step)</strong></summary>
### Authentication Tarball
**1. Install and authenticate Amazon Q on your local machine:**
You must generate an authenticated Amazon Q tarball on another machine where you have successfully logged in:
- Download and install Amazon Q from the [official site](https://aws.amazon.com/q/developer/).
- Run `q login` and complete the authentication process in your terminal.
```bash
# 1. Install Amazon Q and login on your local machine
q login
**2. Locate your Amazon Q config directory:**
# 2. Generate the authentication tarball
cd ~/.local/share/amazon-q
tar -c . | zstd | base64 -w 0
```
- The config is typically stored at `~/.local/share/amazon-q`.
Copy the output and use it as the `auth_tarball` variable.
**3. Generate the tarball:**
## Detailed Authentication Setup
- Run the following command in your terminal:
```sh
cd ~/.local/share/amazon-q
tar -c . | zstd | base64 -w 0
```
**Step 1: Install Amazon Q locally**
**4. Copy the output:**
- Download from [AWS Amazon Q Developer](https://aws.amazon.com/q/developer/)
- Follow the installation instructions for your platform
- The command will output a long string. Copy this entire string.
**Step 2: Authenticate**
**5. Paste into your Terraform variable:**
```bash
q login
```
- Assign the string to the `experiment_auth_tarball` variable in your Terraform configuration, for example:
```tf
variable "amazon_q_auth_tarball" {
type = string
default = "PASTE_LONG_STRING_HERE"
}
```
Complete the authentication process in your browser.
**Note:**
**Step 3: Generate tarball**
- You must re-generate the tarball if you log out or re-authenticate Amazon Q on your local machine.
- This process is required for each user who wants to use Amazon Q in their workspace.
```bash
cd ~/.local/share/amazon-q
tar -c . | zstd | base64 -w 0 > /tmp/amazon-q-auth.txt
```
[Reference: Amazon Q documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/generate-docs.html)
</details>
## Examples
### Run Amazon Q in the background with tmux
**Step 4: Use in Terraform**
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "1.1.2"
agent_id = coder_agent.example.id
experiment_auth_tarball = var.amazon_q_auth_tarball
experiment_use_tmux = true
variable "amazon_q_auth_tarball" {
type = string
sensitive = true
default = "PASTE_YOUR_TARBALL_HERE"
}
```
### Enable task reporting (experimental)
> [!IMPORTANT]
>
> - Regenerate the tarball if you logout or re-authenticate
> - Each user needs their own authentication tarball
> - Keep the tarball secure as it contains authentication credentials
### Coder Tasks Integration
A `coder_parameter` named **'AI Prompt'** is required to enable integration with [Coder Tasks](https://coder.com/docs/ai-coder/tasks).
```tf
data "coder_parameter" "ai_prompt" {
name = "AI Prompt"
display_name = "AI Prompt"
description = "Prompt for the AI task to execute"
type = "string"
mutable = true
default = ""
}
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "1.1.2"
agent_id = coder_agent.example.id
experiment_auth_tarball = var.amazon_q_auth_tarball
experiment_report_tasks = true
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
ai_prompt = data.coder_parameter.ai_prompt.value
trust_all_tools = true
# Task reporting configuration
report_tasks = true
# Enable CLI app alongside web app
cli_app = true
web_app_display_name = "Amazon Q"
cli_app_display_name = "Q CLI"
}
```
### Run custom scripts before/after install
> [!IMPORTANT]
>
> - The parameter name must be exactly **'AI Prompt'** (case-sensitive)
> - This parameter enables the AI task workflow integration
> - The parameter value is passed to the Amazon Q module via the `ai_prompt` variable
> - Without this parameter, `coder_ai_task` resources will not function properly
>
> **_Security Notice_**
> In order to allow the tasks flow non-interactively all the tools are trusted
> This flag bypasses standard permission checks and allows Amazon Q broader access to your system than normally permitted.
> While this enables more functionality, it also means Amazon Q can potentially execute commands with the same privileges as the user running it.
> Use this module only in trusted environments and be aware of the security implications.
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "1.1.2"
agent_id = coder_agent.example.id
experiment_auth_tarball = var.amazon_q_auth_tarball
experiment_pre_install_script = "echo Pre-install!"
experiment_post_install_script = "echo Post-install!"
### Default System Prompt
The module includes a simple system prompt that instructs Amazon Q:
```
You are a helpful Coding assistant. Aim to autonomously investigate
and solve issues the user gives you and test your work, whenever possible.
Avoid shortcuts like mocking tests. When you get stuck, you can ask the user
but opt for autonomy.
```
You can customize this behavior by providing your own system prompt via the `system_prompt` variable.
### Default Coder MCP Instructions
The module includes specific instructions for the Coder MCP server integration that are separate from the system prompt:
```
YOU MUST REPORT ALL TASKS TO CODER.
When reporting tasks you MUST follow these EXACT instructions:
- IMMEDIATELY report status after receiving ANY user message
- Be granular If you are investigating with multiple steps report each step to coder.
Task state MUST be one of the following:
- Use "state": "working" when actively processing WITHOUT needing additional user input
- Use "state": "complete" only when finished with a task
- Use "state": "failure" when you need ANY user input lack sufficient details or encounter blockers.
Task summaries MUST:
- Include specifics about what you're doing
- Include clear and actionable steps for the user
- Be less than 160 characters in length
```
You can customize these instructions by providing your own via the `coder_mcp_instructions` variable.
## Default Agent Configuration
The module includes a default agent configuration template that provides a comprehensive setup for Amazon Q integration:
```json
{
"name": "agent",
"description": "This is an default agent config",
"prompt": "${system_prompt}",
"mcpServers": {},
"tools": [
"fs_read",
"fs_write",
"execute_bash",
"use_aws",
"@coder",
"knowledge"
],
"toolAliases": {},
"allowedTools": ["fs_read", "@coder"],
"resources": [
"file://AmazonQ.md",
"file://README.md",
"file://.amazonq/rules/**/*.md"
],
"hooks": {},
"toolsSettings": {},
"useLegacyMcpJson": true
}
```
## Notes
### Configuration Details:
- Only one of `experiment_use_screen` or `experiment_use_tmux` can be true at a time.
- If neither is set, Amazon Q runs in the foreground.
- For more details, see the [main.tf](./main.tf) source.
- **Tools Available:** File operations, bash execution, AWS CLI, Coder MCP integration, and knowledge base access
- **@coder Tool:** Enables Coder MCP integration for task reporting (`coder_report_task` and related tools)
- **Allowed Tools:** By default, only `fs_read` and `@coder` are allowed (can be customized for security)
- **Resources:** Access to documentation and rule files in the workspace
- **MCP Servers:** Empty by default, can be configured via `agent_config` variable
- **System Prompt:** Dynamically populated from the `system_prompt` variable
- **Legacy MCP:** Uses legacy MCP JSON format for compatibility
You can override this configuration by providing your own JSON via the `agent_config` variable.
### Agent Name Configuration
The module automatically extracts the agent name from the `"name"` field in the `agent_config` JSON and uses it for:
- **Configuration File:** Saves the agent config as `~/.aws/amazonq/cli-agents/{agent_name}.json`
- **Default Agent:** Sets the agent as the default using `q settings chat.defaultAgent {agent_name}`
- **MCP Integration:** Associates the Coder MCP server with the specified agent name
If no custom `agent_config` is provided, the default agent name "agent" is used.
## Usage Examples
### Basic Usage
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
}
```
This example will:
1. Download and install Amazon Q CLI v1.14.1
2. Extract authentication tarball to ~/.local/share/amazon-q
3. Configure Coder MCP integration for task reporting
4. Create default agent configuration file
5. Start Amazon Q in /home/coder directory
6. Provide web interface through AgentAPI
> [!IMPORTANT]
> By default `fs_write` tool is not allowed, which will pause the task execution
> an will wait for the prompt to approve it usage.
> To avoid this, and allow the normal task flow, user has two options:
>
> - Change the parameter `trust_all_tools` value to `true` (default to `false`)
> OR
> - Provide you own agent configuration with the tools of your choice allowed
### With Custom AI Prompt
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
ai_prompt = "Help me set up a Python FastAPI project with proper testing structure"
trust_all_tools = true
}
```
> [!IMPORTANT]
> **_Security Notice_**
> In order to allow the tasks flow non-interactively all the tools are trusted
> This flag bypasses standard permission checks and allows Amazon Q broader access to your system than normally permitted.
> While this enables more functionality, it also means Amazon Q can potentially execute commands with the same privileges as the user running it.
> Use this module only in trusted environments and be aware of the security implications.
### With Custom Pre/Post Install Scripts
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
pre_install_script = <<-EOT
#!/bin/bash
echo "Setting up custom environment..."
# Install additional dependencies
sudo apt-get update && sudo apt-get install -y zstd
EOT
post_install_script = <<-EOT
#!/bin/bash
echo "Configuring Amazon Q settings..."
# Custom configuration commands
q settings chat.model claude-3-sonnet
EOT
}
```
### Specific Version Installation
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
amazon_q_version = "1.14.0" # Specific version
install_amazon_q = true
}
```
### Custom Agent Configuration
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
agent_config = <<-EOT
{
"name": "custom-agent",
"description": "Custom Amazon Q agent for my workspace",
"prompt": "You are a specialized DevOps assistant...",
"tools": ["fs_read", "fs_write", "execute_bash", "use_aws"]
}
EOT
}
```
### With Custom AgentAPI Configuration
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
# AgentAPI configuration for environments without wildcard access url. https://coder.com/docs/admin/setup#wildcard-access-url
agentapi_chat_based_path = true
agentapi_version = "v0.6.1"
}
```
### Air-Gapped Installation
For environments without direct internet access, you can host Amazon Q installation files internally and configure the module to use your internal repository:
```tf
module "amazon-q" {
source = "registry.coder.com/coder/amazon-q/coder"
version = "2.0.0"
agent_id = coder_agent.example.id
workdir = "/home/coder"
auth_tarball = var.amazon_q_auth_tarball
# Point to internal artifact repository
q_install_url = "https://artifacts.internal.corp/amazon-q-releases"
# Use specific version available in your repository
amazon_q_version = "1.14.1"
}
```
**Prerequisites for Air-Gapped Setup:**
1. Download Amazon Q installation files from AWS and host them internally
2. Maintain the same directory structure: `{base_url}/{version}/q-{arch}-linux.zip`
3. Ensure both architectures are available:
- `q-x86_64-linux.zip` for Intel/AMD systems
- `q-aarch64-linux.zip` for ARM systems
4. Configure network access from Coder workspaces to your internal repository
## Troubleshooting
### Common Issues
**Authentication issues:**
- Regenerate the auth tarball on your local machine
- Ensure the tarball is properly base64 encoded
- Check that the original authentication is still valid
**MCP integration not working:**
- Verify that AgentAPI is installed (`install_agentapi = true`)
- Check that the Coder agent is properly configured
- Review the system prompt configuration
@@ -0,0 +1,372 @@
run "required_variables" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
}
}
run "minimal_config" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
auth_tarball = "dGVzdA==" # base64 "test"
}
assert {
condition = resource.coder_env.status_slug.name == "CODER_MCP_APP_STATUS_SLUG"
error_message = "Status slug environment variable not configured correctly"
}
assert {
condition = resource.coder_env.status_slug.value == "amazonq"
error_message = "Status slug value should be 'amazonq'"
}
}
# Test Case 1: Basic Usage No Autonomous Use of Q
# Using vanilla Kubernetes Deployment Template configuration
run "test_case_1_basic_usage" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
auth_tarball = "dGVzdEF1dGhUYXJiYWxs" # base64 "testAuthTarball"
}
# Q is installed and authenticated
assert {
condition = resource.coder_env.status_slug.name == "CODER_MCP_APP_STATUS_SLUG"
error_message = "Status slug environment variable should be configured for basic usage"
}
assert {
condition = resource.coder_env.status_slug.value == "amazonq"
error_message = "Status slug value should be 'amazonq' for basic usage"
}
# AgentAPI is installed and configured (default behavior)
assert {
condition = length(resource.coder_env.auth_tarball) == 1
error_message = "Auth tarball environment variable should be created for authentication"
}
# Foundational configuration applied
assert {
condition = length(local.agent_config) > 0
error_message = "Agent config should be generated with foundational configuration"
}
# No additional parameters required (using defaults)
assert {
condition = local.agent_name == "agent"
error_message = "Default agent name should be 'agent' when no custom config provided"
}
}
# Test Case 2: Autonomous Usage Autonomous Use of Q
# AI prompt passed through from external source (Tasks interface or Issue Tracker CI)
run "test_case_2_autonomous_usage" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
auth_tarball = "dGVzdEF1dGhUYXJiYWxs" # base64 "testAuthTarball"
ai_prompt = "Help me set up a Python FastAPI project with proper testing structure"
}
# Q is installed and authenticated
assert {
condition = resource.coder_env.status_slug.name == "CODER_MCP_APP_STATUS_SLUG"
error_message = "Status slug environment variable should be configured for autonomous usage"
}
assert {
condition = resource.coder_env.status_slug.value == "amazonq"
error_message = "Status slug value should be 'amazonq' for autonomous usage"
}
# AgentAPI is installed and configured
assert {
condition = length(resource.coder_env.auth_tarball) == 1
error_message = "Auth tarball environment variable should be created for autonomous usage"
}
# Foundational configuration for all components applied
assert {
condition = length(local.agent_config) > 0
error_message = "Agent config should be generated for autonomous usage"
}
# AI prompt is configured
assert {
condition = local.full_prompt == "Help me set up a Python FastAPI project with proper testing structure"
error_message = "AI prompt should be configured correctly for autonomous usage"
}
# Default agent name when no custom config
assert {
condition = local.agent_name == "agent"
error_message = "Default agent name should be 'agent' for autonomous usage"
}
}
# Test Case 3: Extended Configuration Parameter Validation and File Rendering
# Validates extended configuration options and parameter application
run "test_case_3_extended_configuration" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
auth_tarball = "dGVzdEF1dGhUYXJiYWxs" # base64 "testAuthTarball"
amazon_q_version = "1.14.1"
q_install_url = "https://desktop-release.q.us-east-1.amazonaws.com"
install_amazon_q = true
install_agentapi = true
agentapi_version = "v0.6.0"
trust_all_tools = true
ai_prompt = "Help me create a production-grade TypeScript monorepo with testing and deployment"
system_prompt = "You are a helpful software assistant working in a secure enterprise environment"
pre_install_script = "echo 'Pre-install setup'"
post_install_script = "echo 'Post-install cleanup'"
agent_config = jsonencode({
name = "production-agent"
description = "Production Amazon Q agent for enterprise environment"
prompt = "You are a helpful software assistant working in a secure enterprise environment"
mcpServers = {}
tools = ["fs_read", "fs_write", "execute_bash", "use_aws", "knowledge"]
toolAliases = {}
allowedTools = ["fs_read"]
resources = ["file://AmazonQ.md", "file://README.md", "file://.amazonq/rules/**/*.md"]
hooks = {}
toolsSettings = {}
useLegacyMcpJson = true
})
}
# All installation parameters are applied correctly
assert {
condition = resource.coder_env.status_slug.value == "amazonq"
error_message = "Status slug should be configured correctly with extended parameters"
}
assert {
condition = resource.coder_env.auth_tarball[0].value == "dGVzdEF1dGhUYXJiYWxs"
error_message = "Auth tarball should be configured correctly with extended parameters"
}
# Custom agent configuration is loaded and referenced correctly
assert {
condition = local.agent_name == "production-agent"
error_message = "Agent name should be extracted from custom agent config"
}
assert {
condition = length(local.agent_config) > 0
error_message = "Custom agent config should be processed correctly"
}
# AI prompt and system prompt are configured
assert {
condition = local.full_prompt == "Help me create a production-grade TypeScript monorepo with testing and deployment"
error_message = "AI prompt should be configured correctly in extended configuration"
}
# Pre-install and post-install scripts are provided
assert {
condition = length(local.agent_config) > 0
error_message = "Agent config should be generated correctly for extended configuration"
}
}
run "full_config" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
install_amazon_q = true
install_agentapi = true
agentapi_version = "v0.5.0"
amazon_q_version = "latest"
trust_all_tools = true
ai_prompt = "Build a web application"
auth_tarball = "dGVzdA=="
order = 1
group = "AI Tools"
icon = "/icon/custom-amazon-q.svg"
pre_install_script = "echo 'pre-install'"
post_install_script = "echo 'post-install'"
agent_config = jsonencode({
name = "test-agent"
description = "Test agent configuration"
prompt = "You are a helpful AI assistant for testing."
mcpServers = {}
tools = ["fs_read", "fs_write", "execute_bash", "use_aws", "knowledge"]
toolAliases = {}
allowedTools = ["fs_read"]
resources = ["file://AmazonQ.md", "file://README.md", "file://.amazonq/rules/**/*.md"]
hooks = {}
toolsSettings = {}
useLegacyMcpJson = true
})
}
assert {
condition = resource.coder_env.status_slug.name == "CODER_MCP_APP_STATUS_SLUG"
error_message = "Status slug environment variable not configured correctly"
}
assert {
condition = resource.coder_env.status_slug.value == "amazonq"
error_message = "Status slug value should be 'amazonq'"
}
assert {
condition = length(resource.coder_env.auth_tarball) == 1
error_message = "Auth tarball environment variable should be created when provided"
}
}
run "auth_tarball_environment" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
auth_tarball = "dGVzdEF1dGhUYXJiYWxs" # base64 "testAuthTarball"
}
assert {
condition = resource.coder_env.auth_tarball[0].name == "AMAZON_Q_AUTH_TARBALL"
error_message = "Auth tarball environment variable name should be 'AMAZON_Q_AUTH_TARBALL'"
}
assert {
condition = resource.coder_env.auth_tarball[0].value == "dGVzdEF1dGhUYXJiYWxs"
error_message = "Auth tarball environment variable value should match input"
}
}
run "empty_auth_tarball" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
auth_tarball = ""
}
assert {
condition = length(resource.coder_env.auth_tarball) == 0
error_message = "Auth tarball environment variable should not be created when empty"
}
}
run "custom_system_prompt" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
system_prompt = "Custom system prompt for testing"
}
# Test that the system prompt is used in the agent config template
assert {
condition = length(local.agent_config) > 0
error_message = "Agent config should be generated with custom system prompt"
}
}
run "install_options" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
install_amazon_q = false
install_agentapi = false
}
assert {
condition = resource.coder_env.status_slug.name == "CODER_MCP_APP_STATUS_SLUG"
error_message = "Status slug should still be configured even when install options are disabled"
}
}
run "version_configuration" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
amazon_q_version = "2.15.0"
agentapi_version = "v0.4.0"
}
assert {
condition = resource.coder_env.status_slug.value == "amazonq"
error_message = "Status slug value should remain 'amazonq' regardless of version"
}
}
# Additional test for agent name extraction
run "agent_name_extraction" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
agent_config = jsonencode({
name = "custom-enterprise-agent"
description = "Custom enterprise agent configuration"
prompt = "You are a custom enterprise AI assistant."
mcpServers = {}
tools = ["fs_read", "fs_write", "execute_bash", "use_aws", "knowledge"]
toolAliases = {}
allowedTools = ["fs_read", "fs_write"]
resources = ["file://README.md"]
hooks = {}
toolsSettings = {}
useLegacyMcpJson = true
})
}
assert {
condition = local.agent_name == "custom-enterprise-agent"
error_message = "Agent name should be extracted correctly from custom agent config"
}
assert {
condition = length(local.agent_config) > 0
error_message = "Agent config should be processed correctly"
}
}
# Test for JSON encoding validation
run "json_encoding_validation" {
command = plan
variables {
agent_id = "test-agent-id"
workdir = "/tmp/test-workdir"
system_prompt = "Multi-line\nsystem prompt\nwith newlines"
}
assert {
condition = length(local.system_prompt) > 0
error_message = "System prompt should be JSON encoded correctly"
}
assert {
condition = length(local.agent_config) > 0
error_message = "Agent config should be generated correctly with multi-line system prompt"
}
}
+509 -19
View File
@@ -2,40 +2,530 @@ import { describe, it, expect } from "bun:test";
import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
findResourceInstance,
} from "~test";
import path from "path";
const moduleDir = path.resolve(__dirname);
// Always provide agent_config to bypass template parsing issues
const baseAgentConfig = JSON.stringify({
name: "test-agent",
description: "Test agent configuration",
prompt: "You are a helpful AI assistant.",
mcpServers: {},
tools: ["fs_read", "fs_write", "execute_bash", "use_aws", "knowledge"],
toolAliases: {},
allowedTools: ["fs_read"],
resources: ["file://README.md", "file://.amazonq/rules/**/*.md"],
hooks: {},
toolsSettings: {},
useLegacyMcpJson: true,
});
const requiredVars = {
agent_id: "dummy-agent-id",
agent_config: baseAgentConfig,
workdir: "/tmp/test-workdir",
};
describe("amazon-q module", async () => {
const fullConfigVars = {
agent_id: "dummy-agent-id",
workdir: "/tmp/test-workdir",
install_amazon_q: true,
install_agentapi: true,
agentapi_version: "v0.6.0",
amazon_q_version: "1.14.1",
q_install_url: "https://desktop-release.q.us-east-1.amazonaws.com",
trust_all_tools: false,
ai_prompt: "Build a comprehensive test suite",
auth_tarball: "dGVzdEF1dGhUYXJiYWxs", // base64 "testAuthTarball"
order: 1,
group: "AI Tools",
icon: "/icon/custom-amazon-q.svg",
pre_install_script: "echo 'Starting pre-install'",
post_install_script: "echo 'Completed post-install'",
agent_config: baseAgentConfig,
};
describe("amazon-q module v2.0.0", async () => {
await runTerraformInit(moduleDir);
// 1. Required variables
testRequiredVariables(moduleDir, requiredVars);
// Test Case 1: Basic Usage No Autonomous Use of Q
// Matches CDES-203 Test Case #1: Basic Usage
it("Test Case 1: Basic Usage - No Autonomous Use of Q", async () => {
const basicUsageVars = {
agent_id: "dummy-agent-id",
workdir: "/tmp/test-workdir",
auth_tarball: "dGVzdEF1dGhUYXJiYWxs", // base64 "testAuthTarball"
};
// 2. coder_script resource is created
it("creates coder_script resource", async () => {
const state = await runTerraformApply(moduleDir, requiredVars);
const scriptResource = findResourceInstance(state, "coder_script");
expect(scriptResource).toBeDefined();
expect(scriptResource.agent_id).toBe(requiredVars.agent_id);
// Optionally, check that the script contains expected lines
expect(scriptResource.script).toContain("Installing Amazon Q");
const state = await runTerraformApply(moduleDir, basicUsageVars);
// Q is installed and authenticated
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG");
expect(statusSlugEnv.value).toBe("amazonq");
// AgentAPI is installed and configured (default behavior)
const authTarballEnv = findResourceInstance(
state,
"coder_env",
"auth_tarball",
);
expect(authTarballEnv).toBeDefined();
expect(authTarballEnv.name).toBe("AMAZON_Q_AUTH_TARBALL");
expect(authTarballEnv.value).toBe("dGVzdEF1dGhUYXJiYWxs");
// Foundational configuration for all components is applied
// No additional parameters are required for the module to work
// Using the terminal application and Q chat returns a functional interface
});
// 3. coder_app resource is created
it("creates coder_app resource", async () => {
const state = await runTerraformApply(moduleDir, requiredVars);
const appResource = findResourceInstance(state, "coder_app", "amazon_q");
expect(appResource).toBeDefined();
expect(appResource.agent_id).toBe(requiredVars.agent_id);
// Test Case 2: Autonomous Usage Autonomous Use of Q
// Matches CDES-203 Test Case 2: Autonomous Usage
it("Test Case 2: Autonomous Usage - Autonomous Use of Q", async () => {
const autonomousUsageVars = {
agent_id: "dummy-agent-id",
workdir: "/tmp/test-workdir",
auth_tarball: "dGVzdEF1dGhUYXJiYWxs", // base64 "testAuthTarball"
ai_prompt:
"Help me set up a Python FastAPI project with proper testing structure",
};
const state = await runTerraformApply(moduleDir, autonomousUsageVars);
// Q is installed and authenticated
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG");
expect(statusSlugEnv.value).toBe("amazonq");
// AgentAPI is installed and configured
const authTarballEnv = findResourceInstance(
state,
"coder_env",
"auth_tarball",
);
expect(authTarballEnv).toBeDefined();
expect(authTarballEnv.name).toBe("AMAZON_Q_AUTH_TARBALL");
// AI prompt is passed through from external source
// The Chat interface functions as required
// The Tasks interface functions as required
// The template can be invoked from GitHub integration as expected
});
// Add more state-based tests as needed
// Test Case 3: Extended Configuration Parameter Validation and File Rendering
// Matches CDES-203 Test Case 3: Extended Configuration
it("Test Case 3: Extended Configuration - Parameter Validation and File Rendering", async () => {
const extendedConfigVars = {
agent_id: "dummy-agent-id",
workdir: "/tmp/test-workdir",
auth_tarball: "dGVzdEF1dGhUYXJiYWxs", // base64 "testAuthTarball"
amazon_q_version: "1.14.1",
q_install_url: "https://desktop-release.q.us-east-1.amazonaws.com",
install_amazon_q: true,
install_agentapi: true,
agentapi_version: "v0.6.0",
trust_all_tools: true,
ai_prompt:
"Help me create a production-grade TypeScript monorepo with testing and deployment",
system_prompt:
"You are a helpful software assistant working in a secure enterprise environment",
pre_install_script: "echo 'Pre-install setup'",
post_install_script: "echo 'Post-install cleanup'",
agent_config: JSON.stringify({
name: "production-agent",
description: "Production Amazon Q agent for enterprise environment",
prompt:
"You are a helpful software assistant working in a secure enterprise environment",
mcpServers: {},
tools: ["fs_read", "fs_write", "execute_bash", "use_aws", "knowledge"],
toolAliases: {},
allowedTools: ["fs_read"],
resources: [
"file://AmazonQ.md",
"file://README.md",
"file://.amazonq/rules/**/*.md",
],
hooks: {},
toolsSettings: {},
useLegacyMcpJson: true,
}),
};
const state = await runTerraformApply(moduleDir, extendedConfigVars);
// All installation steps execute in the correct order
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG");
expect(statusSlugEnv.value).toBe("amazonq");
// auth_tarball is unpacked and used as expected
const authTarballEnv = findResourceInstance(
state,
"coder_env",
"auth_tarball",
);
expect(authTarballEnv).toBeDefined();
expect(authTarballEnv.value).toBe("dGVzdEF1dGhUYXJiYWxs");
// agent_config is rendered correctly, and the name field is used as the agent's name
// The specified ai_prompt and system_prompt are respected by the Q agent
// Tools are trusted globally if trust_all_tools = true
// Files and scripts execute in proper sequence
});
// 1. Basic functionality test (replaces testRequiredVariables)
it("works with required variables", async () => {
const state = await runTerraformApply(moduleDir, requiredVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG");
expect(statusSlugEnv.value).toBe("amazonq");
});
// 2. Environment variables are created correctly
it("creates required environment variables", async () => {
const state = await runTerraformApply(moduleDir, fullConfigVars);
// Check status slug environment variable
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG");
expect(statusSlugEnv.value).toBe("amazonq");
// Check auth tarball environment variable
const authTarballEnv = findResourceInstance(
state,
"coder_env",
"auth_tarball",
);
expect(authTarballEnv).toBeDefined();
expect(authTarballEnv.name).toBe("AMAZON_Q_AUTH_TARBALL");
expect(authTarballEnv.value).toBe("dGVzdEF1dGhUYXJiYWxs");
});
// 3. Empty auth tarball handling
it("handles empty auth tarball correctly", async () => {
const noAuthVars = {
...requiredVars,
auth_tarball: "",
};
const state = await runTerraformApply(moduleDir, noAuthVars);
// Auth tarball environment variable should not be created when empty
const authTarballEnv = state.resources?.find(
(r) => r.type === "coder_env" && r.name === "auth_tarball",
);
expect(authTarballEnv).toBeUndefined();
});
// 4. Status slug is always created
it("creates status slug environment variable", async () => {
const state = await runTerraformApply(moduleDir, requiredVars);
// Status slug should always be configured
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.name).toBe("CODER_MCP_APP_STATUS_SLUG");
expect(statusSlugEnv.value).toBe("amazonq");
});
// 5. Install options configuration
it("respects install option flags", async () => {
const noInstallVars = {
...requiredVars,
install_amazon_q: false,
install_agentapi: false,
};
const state = await runTerraformApply(moduleDir, noInstallVars);
// Status slug should still be configured even when install options are disabled
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.value).toBe("amazonq");
});
// 6. Configurable installation URL
it("uses configurable q_install_url parameter", async () => {
const customUrlVars = {
...requiredVars,
q_install_url: "https://internal-mirror.company.com/amazon-q",
};
const state = await runTerraformApply(moduleDir, customUrlVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 7. Version configuration
it("uses specified versions", async () => {
const versionVars = {
...requiredVars,
amazon_q_version: "1.14.1",
agentapi_version: "v0.6.0",
};
const state = await runTerraformApply(moduleDir, versionVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 8. UI configuration options
it("supports UI customization options", async () => {
const uiCustomVars = {
...requiredVars,
order: 5,
group: "Custom AI Tools",
icon: "/icon/custom-amazon-q-icon.svg",
};
const state = await runTerraformApply(moduleDir, uiCustomVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 9. Pre and post install scripts
it("supports pre and post install scripts", async () => {
const scriptVars = {
...requiredVars,
pre_install_script: "echo 'Pre-install setup'",
post_install_script: "echo 'Post-install cleanup'",
};
const state = await runTerraformApply(moduleDir, scriptVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 10. Valid agent_config JSON with different agent name
it("handles valid agent_config JSON with custom agent name", async () => {
const customAgentConfig = JSON.stringify({
name: "production-agent",
description: "Production Amazon Q agent",
prompt: "You are a production AI assistant.",
mcpServers: {},
tools: ["fs_read", "fs_write"],
toolAliases: {},
allowedTools: ["fs_read"],
resources: ["file://README.md"],
hooks: {},
toolsSettings: {},
useLegacyMcpJson: true,
});
const validAgentConfigVars = {
...requiredVars,
agent_config: customAgentConfig,
};
const state = await runTerraformApply(moduleDir, validAgentConfigVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 11. Air-gapped installation support
it("supports air-gapped installation with custom URL", async () => {
const airGappedVars = {
...requiredVars,
q_install_url: "https://artifacts.internal.corp/amazon-q-releases",
amazon_q_version: "1.14.1",
};
const state = await runTerraformApply(moduleDir, airGappedVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 12. Trust all tools configuration
it("handles trust_all_tools configuration", async () => {
const trustVars = {
...requiredVars,
trust_all_tools: true,
};
const state = await runTerraformApply(moduleDir, trustVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 13. AI prompt configuration
it("handles AI prompt configuration", async () => {
const promptVars = {
...requiredVars,
ai_prompt: "Create a comprehensive test suite for the application",
};
const state = await runTerraformApply(moduleDir, promptVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 14. Agent config with minimal structure
it("handles minimal agent config structure", async () => {
const minimalAgentConfig = JSON.stringify({
name: "minimal-agent",
description: "Minimal agent config",
prompt: "You are a minimal AI assistant.",
mcpServers: {},
tools: ["fs_read", "fs_write", "execute_bash", "use_aws", "knowledge"],
toolAliases: {},
allowedTools: ["fs_read"],
resources: ["file://README.md"],
hooks: {},
toolsSettings: {},
useLegacyMcpJson: true,
});
const minimalVars = {
...requiredVars,
agent_config: minimalAgentConfig,
};
const state = await runTerraformApply(moduleDir, minimalVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
});
// 15. JSON encoding validation for system prompts with newlines
it("handles system prompts with newlines correctly", async () => {
const multilinePromptVars = {
...requiredVars,
system_prompt: "Multi-line\nsystem prompt\nwith newlines",
};
const state = await runTerraformApply(moduleDir, multilinePromptVars);
// Should create the basic resources without JSON parsing errors
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.value).toBe("amazonq");
});
// 16. Agent name extraction from custom config
it("extracts agent name from custom configuration correctly", async () => {
const customNameConfig = JSON.stringify({
name: "enterprise-production-agent",
description: "Enterprise production agent configuration",
prompt: "You are an enterprise production AI assistant.",
mcpServers: {},
tools: ["fs_read", "fs_write", "execute_bash", "use_aws", "knowledge"],
toolAliases: {},
allowedTools: ["fs_read", "fs_write", "execute_bash"],
resources: ["file://README.md", "file://.amazonq/rules/**/*.md"],
hooks: {},
toolsSettings: {},
useLegacyMcpJson: true,
});
const customNameVars = {
...requiredVars,
agent_config: customNameConfig,
};
const state = await runTerraformApply(moduleDir, customNameVars);
// Should create the basic resources
const statusSlugEnv = findResourceInstance(
state,
"coder_env",
"status_slug",
);
expect(statusSlugEnv).toBeDefined();
expect(statusSlugEnv.value).toBe("amazonq");
});
});
+187 -236
View File
@@ -1,10 +1,12 @@
# Improved amazon-q module main.tf
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = ">= 2.5"
version = ">= 2.7"
}
}
}
@@ -15,7 +17,6 @@ variable "agent_id" {
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
variable "order" {
@@ -36,12 +37,67 @@ variable "icon" {
default = "/icon/amazon-q.svg"
}
variable "folder" {
variable "report_tasks" {
type = bool
description = "Whether to enable task reporting to Coder UI via AgentAPI"
default = true
}
variable "cli_app" {
type = bool
description = "Whether to create a CLI app for Amazon Q"
default = false
}
variable "web_app_display_name" {
type = string
description = "Display name for the web app"
default = "AmazonQ"
}
variable "cli_app_display_name" {
type = string
description = "Display name for the CLI app"
default = "AmazonQ CLI"
}
variable "install_agentapi" {
type = bool
description = "Whether to install AgentAPI."
default = true
}
variable "ai_prompt" {
type = string
description = "The initial task prompt to send to Amazon Q."
default = ""
}
variable "pre_install_script" {
type = string
description = "Optional script to run before installing Amazon Q."
default = null
}
variable "post_install_script" {
type = string
description = "Optional script to run after installing Amazon Q."
default = null
}
variable "agentapi_version" {
type = string
description = "The version of AgentAPI to install."
default = "v0.6.1"
}
variable "workdir" {
type = string
description = "The folder to run Amazon Q in."
default = "/home/coder"
}
# ---------------------------------------------
variable "install_amazon_q" {
type = bool
description = "Whether to install Amazon Q."
@@ -51,43 +107,19 @@ variable "install_amazon_q" {
variable "amazon_q_version" {
type = string
description = "The version of Amazon Q to install."
default = "latest"
default = "1.14.1"
}
variable "experiment_use_screen" {
type = bool
description = "Whether to use screen for running Amazon Q in the background."
default = false
}
variable "experiment_use_tmux" {
type = bool
description = "Whether to use tmux instead of screen for running Amazon Q in the background."
default = false
}
variable "experiment_report_tasks" {
type = bool
description = "Whether to enable task reporting."
default = false
}
variable "experiment_pre_install_script" {
variable "q_install_url" {
type = string
description = "Custom script to run before installing Amazon Q."
default = null
description = "Base URL for Amazon Q installation downloads."
default = "https://desktop-release.q.us-east-1.amazonaws.com"
}
variable "experiment_post_install_script" {
type = string
description = "Custom script to run after installing Amazon Q."
default = null
}
variable "experiment_auth_tarball" {
type = string
description = "Base64 encoded, zstd compressed tarball of a pre-authenticated ~/.local/share/amazon-q directory. After running `q login` on another machine, you may generate it with: `cd ~/.local/share/amazon-q && tar -c . | zstd | base64 -w 0`"
default = "tarball"
variable "trust_all_tools" {
type = bool
description = "Whether to trust all tools in Amazon Q."
default = false
}
variable "system_prompt" {
@@ -98,222 +130,141 @@ variable "system_prompt" {
and solve issues the user gives you and test your work, whenever possible.
Avoid shortcuts like mocking tests. When you get stuck, you can ask the user
but opt for autonomy.
YOU MUST REPORT ALL TASKS TO CODER.
When reporting tasks, you MUST follow these EXACT instructions:
- IMMEDIATELY report status after receiving ANY user message.
- Be granular. If you are investigating with multiple steps, report each step to coder.
Task state MUST be one of the following:
- Use "state": "working" when actively processing WITHOUT needing additional user input.
- Use "state": "complete" only when finished with a task.
- Use "state": "failure" when you need ANY user input, lack sufficient details, or encounter blockers.
Task summaries MUST:
- Include specifics about what you're doing.
- Include clear and actionable steps for the user.
- Be less than 160 characters in length.
EOT
}
variable "ai_prompt" {
variable "coder_mcp_instructions" {
type = string
description = "The initial task prompt to send to Amazon Q."
default = "Please help me with my coding tasks. I'll provide specific instructions as needed."
description = "Instructions for the Coder MCP server integration. This defines how the agent should report tasks to Coder."
default = <<-EOT
YOU MUST REPORT ALL TASKS TO CODER.
When reporting tasks you MUST follow these EXACT instructions:
- IMMEDIATELY report status after receiving ANY user message
- Be granular If you are investigating with multiple steps report each step to coder.
Task state MUST be one of the following:
- Use "state": "working" when actively processing WITHOUT needing additional user input
- Use "state": "complete" only when finished with a task
- Use "state": "failure" when you need ANY user input lack sufficient details or encounter blockers.
Task summaries MUST:
- Include specifics about what you're doing
- Include clear and actionable steps for the user
- Be less than 160 characters in length
EOT
}
variable "auth_tarball" {
type = string
description = "Base64 encoded, zstd compressed tarball of a pre-authenticated ~/.local/share/amazon-q directory."
default = ""
sensitive = true
}
variable "agent_config" {
type = string
description = "Optional Agent configuration JSON for Amazon Q."
default = null
}
variable "agentapi_chat_based_path" {
type = bool
description = "Whether to use chat-based path for AgentAPI.Required if CODER_WILDCARD_ACCESS_URL is not defined in coder deployment"
default = false
}
# Expose status slug to the agent environment
resource "coder_env" "status_slug" {
agent_id = var.agent_id
name = "CODER_MCP_APP_STATUS_SLUG"
value = local.app_slug
}
# Expose auth tarball as environment variable for install script
resource "coder_env" "auth_tarball" {
count = var.auth_tarball != "" ? 1 : 0
agent_id = var.agent_id
name = "AMAZON_Q_AUTH_TARBALL"
value = var.auth_tarball
}
locals {
encoded_pre_install_script = var.experiment_pre_install_script != null ? base64encode(var.experiment_pre_install_script) : ""
encoded_post_install_script = var.experiment_post_install_script != null ? base64encode(var.experiment_post_install_script) : ""
full_prompt = <<-EOT
${var.system_prompt}
app_slug = "amazonq"
install_script = file("${path.module}/scripts/install.sh")
start_script = file("${path.module}/scripts/start.sh")
module_dir_name = ".amazonq-module"
system_prompt = jsonencode(replace(var.system_prompt, "/[\r\n]/", ""))
coder_mcp_instructions = jsonencode(replace(var.coder_mcp_instructions, "/[\r\n]/", ""))
Your first task is:
# Create default agent config structure
default_agent_config = templatefile("${path.module}/templates/agent-config.json.tpl", {
system_prompt = local.system_prompt
})
${var.ai_prompt}
EOT
# Choose the JSON string: use var.agent_config if provided, otherwise encode default
agent_config = var.agent_config != null ? var.agent_config : local.default_agent_config
# Extract agent name from the selected config
agent_name = try(jsondecode(local.agent_config).name, "agent")
full_prompt = var.ai_prompt != null ? "${var.ai_prompt}" : ""
server_chat_parameters = var.agentapi_chat_based_path ? "--chat-base-path /@${data.coder_workspace_owner.me.name}/${data.coder_workspace.me.name}.${var.agent_id}/apps/${local.app_slug}/chat" : ""
}
resource "coder_script" "amazon_q" {
agent_id = var.agent_id
display_name = "Amazon Q"
icon = var.icon
script = <<-EOT
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "1.1.1"
agent_id = var.agent_id
web_app_slug = local.app_slug
web_app_order = var.order
web_app_group = var.group
web_app_icon = var.icon
web_app_display_name = var.web_app_display_name
cli_app = var.cli_app
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
module_dir_name = local.module_dir_name
install_agentapi = var.install_agentapi
agentapi_version = var.agentapi_version
pre_install_script = var.pre_install_script
post_install_script = var.post_install_script
start_script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail
command_exists() {
command -v "$1" >/dev/null 2>&1
}
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
chmod +x /tmp/start.sh
ARG_TRUST_ALL_TOOLS='${var.trust_all_tools}' \
ARG_AI_PROMPT='${base64encode(local.full_prompt)}' \
ARG_MODULE_DIR_NAME='${local.module_dir_name}' \
ARG_WORKDIR='${var.workdir}' \
ARG_SERVER_PARAMETERS="${local.server_chat_parameters}" \
ARG_REPORT_TASKS='${var.report_tasks}' \
/tmp/start.sh
EOT
if [ -n "${local.encoded_pre_install_script}" ]; then
echo "Running pre-install script..."
echo "${local.encoded_pre_install_script}" | base64 -d > /tmp/pre_install.sh
chmod +x /tmp/pre_install.sh
/tmp/pre_install.sh
fi
if [ "${var.install_amazon_q}" = "true" ]; then
echo "Installing Amazon Q..."
PREV_DIR="$PWD"
TMP_DIR="$(mktemp -d)"
cd "$TMP_DIR"
ARCH="$(uname -m)"
case "$ARCH" in
"x86_64")
Q_URL="https://desktop-release.q.us-east-1.amazonaws.com/${var.amazon_q_version}/q-x86_64-linux.zip"
;;
"aarch64"|"arm64")
Q_URL="https://desktop-release.codewhisperer.us-east-1.amazonaws.com/${var.amazon_q_version}/q-aarch64-linux.zip"
;;
*)
echo "Error: Unsupported architecture: $ARCH. Amazon Q only supports x86_64 and arm64."
exit 1
;;
esac
echo "Downloading Amazon Q for $ARCH..."
curl --proto '=https' --tlsv1.2 -sSf "$Q_URL" -o "q.zip"
unzip q.zip
./q/install.sh --no-confirm
cd "$PREV_DIR"
export PATH="$PATH:$HOME/.local/bin"
echo "Installed Amazon Q version: $(q --version)"
fi
echo "Extracting auth tarball..."
PREV_DIR="$PWD"
echo "${var.experiment_auth_tarball}" | base64 -d > /tmp/auth.tar.zst
rm -rf ~/.local/share/amazon-q
mkdir -p ~/.local/share/amazon-q
cd ~/.local/share/amazon-q
tar -I zstd -xf /tmp/auth.tar.zst
rm /tmp/auth.tar.zst
cd "$PREV_DIR"
echo "Extracted auth tarball"
if [ "${var.experiment_report_tasks}" = "true" ]; then
echo "Configuring Amazon Q to report tasks via Coder MCP..."
q mcp add --name coder --command "coder" --args "exp,mcp,server,--allowed-tools,coder_report_task" --env "CODER_MCP_APP_STATUS_SLUG=amazon-q" --scope global --force
echo "Added Coder MCP server to Amazon Q configuration"
fi
if [ -n "${local.encoded_post_install_script}" ]; then
echo "Running post-install script..."
echo "${local.encoded_post_install_script}" | base64 -d > /tmp/post_install.sh
chmod +x /tmp/post_install.sh
/tmp/post_install.sh
fi
if [ "${var.experiment_use_tmux}" = "true" ] && [ "${var.experiment_use_screen}" = "true" ]; then
echo "Error: Both experiment_use_tmux and experiment_use_screen cannot be true simultaneously."
echo "Please set only one of them to true."
exit 1
fi
if [ "${var.experiment_use_tmux}" = "true" ]; then
echo "Running Amazon Q in the background with tmux..."
if ! command_exists tmux; then
echo "Error: tmux is not installed. Please install tmux manually."
exit 1
fi
touch "$HOME/.amazon-q.log"
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
tmux new-session -d -s amazon-q -c "${var.folder}" "q chat --trust-all-tools | tee -a "$HOME/.amazon-q.log" && exec bash"
tmux send-keys -t amazon-q "${local.full_prompt}"
sleep 5
tmux send-keys -t amazon-q Enter
fi
if [ "${var.experiment_use_screen}" = "true" ]; then
echo "Running Amazon Q in the background..."
if ! command_exists screen; then
echo "Error: screen is not installed. Please install screen manually."
exit 1
fi
touch "$HOME/.amazon-q.log"
if [ ! -f "$HOME/.screenrc" ]; then
echo "Creating ~/.screenrc and adding multiuser settings..." | tee -a "$HOME/.amazon-q.log"
echo -e "multiuser on\nacladd $(whoami)" > "$HOME/.screenrc"
fi
if ! grep -q "^multiuser on$" "$HOME/.screenrc"; then
echo "Adding 'multiuser on' to ~/.screenrc..." | tee -a "$HOME/.amazon-q.log"
echo "multiuser on" >> "$HOME/.screenrc"
fi
if ! grep -q "^acladd $(whoami)$" "$HOME/.screenrc"; then
echo "Adding 'acladd $(whoami)' to ~/.screenrc..." | tee -a "$HOME/.amazon-q.log"
echo "acladd $(whoami)" >> "$HOME/.screenrc"
fi
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
screen -U -dmS amazon-q bash -c '
cd ${var.folder}
q chat --trust-all-tools | tee -a "$HOME/.amazon-q.log
exec bash
'
# Extremely hacky way to send the prompt to the screen session
# This will be fixed in the future, but `amazon-q` was not sending MCP
# tasks when an initial prompt is provided.
screen -S amazon-q -X stuff "${local.full_prompt}"
sleep 5
screen -S amazon-q -X stuff "^M"
else
if ! command_exists q; then
echo "Error: Amazon Q is not installed. Please enable install_amazon_q or install it manually."
exit 1
fi
fi
EOT
run_on_start = true
}
resource "coder_app" "amazon_q" {
slug = "amazon-q"
display_name = "Amazon Q"
agent_id = var.agent_id
command = <<-EOT
install_script = <<-EOT
#!/bin/bash
set -e
set -o errexit
set -o pipefail
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
if [ "${var.experiment_use_tmux}" = "true" ]; then
if tmux has-session -t amazon-q 2>/dev/null; then
echo "Attaching to existing Amazon Q tmux session." | tee -a "$HOME/.amazon-q.log"
tmux attach-session -t amazon-q
else
echo "Starting a new Amazon Q tmux session." | tee -a "$HOME/.amazon-q.log"
tmux new-session -s amazon-q -c ${var.folder} "q chat --trust-all-tools | tee -a \"$HOME/.amazon-q.log\"; exec bash"
fi
elif [ "${var.experiment_use_screen}" = "true" ]; then
if screen -list | grep -q "amazon-q"; then
echo "Attaching to existing Amazon Q screen session." | tee -a "$HOME/.amazon-q.log"
screen -xRR amazon-q
else
echo "Starting a new Amazon Q screen session." | tee -a "$HOME/.amazon-q.log"
screen -S amazon-q bash -c 'q chat --trust-all-tools | tee -a "$HOME/.amazon-q.log"; exec bash'
fi
else
cd ${var.folder}
q chat --trust-all-tools
fi
EOT
icon = var.icon
order = var.order
group = var.group
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
chmod +x /tmp/install.sh
ARG_INSTALL='${var.install_amazon_q}' \
ARG_VERSION='${var.amazon_q_version}' \
ARG_Q_INSTALL_URL='${var.q_install_url}' \
ARG_AUTH_TARBALL='${var.auth_tarball}' \
ARG_AGENT_CONFIG='${local.agent_config != null ? base64encode(local.agent_config) : ""}' \
ARG_AGENT_NAME='${local.agent_name}' \
ARG_MODULE_DIR_NAME='${local.module_dir_name}' \
ARG_CODER_MCP_APP_STATUS_SLUG='${local.app_slug}' \
ARG_CODER_MCP_INSTRUCTIONS='${base64encode(local.coder_mcp_instructions)}' \
ARG_REPORT_TASKS='${var.report_tasks}' \
/tmp/install.sh
EOT
}
@@ -0,0 +1,152 @@
#!/bin/bash
# Install script for amazon-q module
set -o errexit
set -o pipefail
command_exists() {
command -v "$1" > /dev/null 2>&1
}
# Inputs
ARG_INSTALL=${ARG_INSTALL:-true}
ARG_VERSION=${ARG_VERSION:-latest}
ARG_Q_INSTALL_URL=${ARG_Q_INSTALL_URL:-https://desktop-release.q.us-east-1.amazonaws.com}
ARG_AUTH_TARBALL=${ARG_AUTH_TARBALL:-}
ARG_AGENT_CONFIG=${ARG_AGENT_CONFIG:-}
ARG_AGENT_NAME=${ARG_AGENT_NAME:-default-agent}
ARG_MODULE_DIR_NAME=${ARG_MODULE_DIR_NAME:-.aws/.amazonq}
ARG_CODER_MCP_APP_STATUS_SLUG=${ARG_CODER_MCP_APP_STATUS_SLUG:-}
ARG_CODER_MCP_INSTRUCTIONS=${ARG_CODER_MCP_INSTRUCTIONS:-}
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
mkdir -p "$HOME/$ARG_MODULE_DIR_NAME"
# Decode base64 inputs
ARG_AGENT_CONFIG_DECODED=""
if [ -n "$ARG_AGENT_CONFIG" ]; then
ARG_AGENT_CONFIG_DECODED=$(echo -n "$ARG_AGENT_CONFIG" | base64 -d)
fi
ARG_CODER_MCP_INSTRUCTIONS_DECODED=""
if [ -n "$ARG_CODER_MCP_INSTRUCTIONS" ]; then
ARG_CODER_MCP_INSTRUCTIONS_DECODED=$(echo -n "$ARG_CODER_MCP_INSTRUCTIONS" | base64 -d)
fi
echo "--------------------------------"
echo "install: $ARG_INSTALL"
echo "version: $ARG_VERSION"
echo "q_install_url: $ARG_Q_INSTALL_URL"
echo "agent_name: $ARG_AGENT_NAME"
echo "coder_mcp_app_status_slug: $ARG_CODER_MCP_APP_STATUS_SLUG"
echo "module_dir_name: $ARG_MODULE_DIR_NAME"
echo "auth_tarball_provided: ${ARG_AUTH_TARBALL}"
echo "report_tasks: ${ARG_REPORT_TASKS}"
echo "--------------------------------"
# Install Amazon Q if requested
function install_amazon_q() {
if [ "$ARG_INSTALL" = "true" ]; then
echo "Installing Amazon Q..."
PREV_DIR="$PWD"
TMP_DIR="$(mktemp -d)"
cd "$TMP_DIR"
ARCH="$(uname -m)"
case "$ARCH" in
"x86_64")
Q_URL="${ARG_Q_INSTALL_URL}/${ARG_VERSION}/q-x86_64-linux.zip"
;;
"aarch64" | "arm64")
Q_URL="${ARG_Q_INSTALL_URL}/${ARG_VERSION}/q-aarch64-linux.zip"
;;
*)
echo "Error: Unsupported architecture: $ARCH. Amazon Q only supports x86_64 and arm64."
exit 1
;;
esac
echo "Downloading Amazon Q for $ARCH from $Q_URL..."
curl --proto '=https' --tlsv1.2 -sSf "$Q_URL" -o "q.zip"
unzip q.zip
./q/install.sh --no-confirm
cd "$PREV_DIR"
rm -rf "$TMP_DIR"
# Ensure binaries are discoverable; create stable symlink to q
CANDIDATES=(
"$(command -v q || true)"
"$HOME/.local/bin/q"
)
FOUND_BIN=""
for c in "${CANDIDATES[@]}"; do
if [ -n "$c" ] && [ -x "$c" ]; then
FOUND_BIN="$c"
break
fi
done
export PATH="$PATH:$HOME/.local/bin"
echo "Installed Amazon Q at: $(command -v q || true) (resolved: $FOUND_BIN)"
fi
}
# Extract authentication tarball
function extract_auth_tarball() {
if [ -n "$ARG_AUTH_TARBALL" ]; then
echo "Extracting auth tarball..."
PREV_DIR="$PWD"
echo "$ARG_AUTH_TARBALL" | base64 -d > /tmp/auth.tar.zst
rm -rf ~/.local/share/amazon-q
mkdir -p ~/.local/share/amazon-q
cd ~/.local/share/amazon-q
tar -I zstd -xf /tmp/auth.tar.zst
rm /tmp/auth.tar.zst
cd "$PREV_DIR"
echo "Extracted auth tarball to ~/.local/share/amazon-q"
else
echo "Warning: No auth tarball provided. Amazon Q may require manual authentication."
fi
}
# Configure MCP integration and create agent
function configure_agent() {
# Create Amazon Q agent configuration directory
AGENT_CONFIG_DIR="$HOME/.aws/amazonq/cli-agents"
mkdir -p "$AGENT_CONFIG_DIR"
ALLOWED_TOOLS="coder_get_workspace\,coder_create_workspace\,coder_list_workspaces\,coder_list_templates\,coder_template_version_parameters\,coder_get_authenticated_user\,coder_create_workspace_build\,coder_create_template_version\,coder_get_workspace_agent_logs\,coder_get_workspace_build_logs\,coder_get_template_version_logs\,coder_update_template_active_version\,coder_upload_tar_file\,coder_create_template\,coder_delete_template\,coder_workspace_bash"
if [ -n "$ARG_AGENT_CONFIG_DECODED" ]; then
echo "Applying custom MCP configuration..."
# Use agent name as filename for the configuration
echo "$ARG_AGENT_CONFIG_DECODED" > "$AGENT_CONFIG_DIR/${ARG_AGENT_NAME}.json"
echo "Custom configuration saved to $AGENT_CONFIG_DIR/${ARG_AGENT_NAME}.json"
fi
if [ "$ARG_REPORT_TASKS" = "true" ]; then
echo "Configuring Amazon Q to report tasks via Coder MCP..."
q mcp add --name coder \
--command "coder" \
--agent "$ARG_AGENT_NAME" \
--args "exp,mcp,server,--allowed-tools,coder_report_task,--instructions,'$ARG_CODER_MCP_INSTRUCTIONS_DECODED'" \
--env "CODER_MCP_APP_STATUS_SLUG=${ARG_CODER_MCP_APP_STATUS_SLUG}" \
--env "CODER_MCP_AI_AGENTAPI_URL=http://localhost:3284" \
--env "CODER_AGENT_URL=${CODER_AGENT_URL}" \
--env "CODER_AGENT_TOKEN=${CODER_AGENT_TOKEN}" \
--force || echo "Warning: Failed to add Coder MCP server"
else
q mcp add --name coder \
--command "coder" \
--agent "$ARG_AGENT_NAME" \
--args "exp,mcp,server,--allowed-tools,coder_report_task" \
--env "CODER_AGENT_URL=${CODER_AGENT_URL}" \
--env "CODER_AGENT_TOKEN=${CODER_AGENT_TOKEN}" \
--force || echo "Warning: Failed to add Coder MCP server"
fi
echo "Added Coder MCP server into $ARG_AGENT_NAME in Amazon Q configuration"
q settings chat.defaultAgent "$ARG_AGENT_NAME"
}
# Main execution
install_amazon_q
extract_auth_tarball
configure_agent
echo "Amazon Q installation and configuration complete!"
@@ -0,0 +1,67 @@
#!/bin/bash
# Start script for amazon-q module
set -o errexit
set -o pipefail
command_exists() {
command -v "$1" > /dev/null 2>&1
}
# Decode inputs
ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d)
ARG_TRUST_ALL_TOOLS=${ARG_TRUST_ALL_TOOLS:-true}
ARG_MODULE_DIR_NAME=${ARG_MODULE_DIR_NAME:-.aws/amazonq}
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
ARG_SERVER_PARAMETERS=${ARG_SERVER_PARAMETERS:-""}
echo "--------------------------------"
echo "ai_prompt: $ARG_AI_PROMPT"
echo "trust_all_tools: $ARG_TRUST_ALL_TOOLS"
echo "module_dir_name: $ARG_MODULE_DIR_NAME"
echo "workdir: $ARG_WORKDIR"
echo "report_tasks: ${ARG_REPORT_TASKS}"
echo "--------------------------------"
mkdir -p "$HOME/$ARG_MODULE_DIR_NAME"
# Find Amazon Q CLI
if command_exists q; then
Q_CMD=q
elif [ -x "$HOME/.local/bin/q" ]; then
Q_CMD="$HOME/.local/bin/q"
else
echo "Error: Amazon Q CLI not found. Install it or set install_amazon_q=true."
exit 1
fi
mkdir -p "$ARG_WORKDIR"
cd "$ARG_WORKDIR"
# Set up environment
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
# Build command arguments
ARGS=(chat)
if [ "$ARG_TRUST_ALL_TOOLS" = "true" ]; then
ARGS+=(--trust-all-tools)
fi
# Log and run with agentapi integration
printf "Running: %q %s\n" "$Q_CMD" "$(printf '%q ' "${ARGS[@]}")"
# If we have an AI prompt, we need to handle it specially
if [ -n "$ARG_AI_PROMPT" ]; then
if [ "$ARG_REPORT_TASKS" == "true" ]; then
PROMPT="Every step of the way, report your progress using coder_report_task tool with proper summary and statuses. Your task at hand: $ARG_AI_PROMPT"
else
PROMPT="$ARG_AI_PROMPT"
fi
ARGS+=("$PROMPT")
fi
# Use agentapi to manage the interactive session with initial prompt
agentapi server ${ARG_SERVER_PARAMETERS} --term-width 67 --term-height 1190 -- "$Q_CMD" "${ARGS[@]}"
@@ -0,0 +1,27 @@
{
"name": "agent",
"description": "This is an default agent config",
"prompt": ${system_prompt},
"mcpServers": {},
"tools": [
"fs_read",
"fs_write",
"execute_bash",
"use_aws",
"@coder",
"knowledge"
],
"toolAliases": {},
"allowedTools": [
"fs_read",
"@coder"
],
"resources": [
"file://AmazonQ.md",
"file://README.md",
"file://.amazonq/rules/**/*.md"
],
"hooks": {},
"toolsSettings": {},
"useLegacyMcpJson": true
}