Deploy Your First Bedrock Agent with Terraform: Model-Agnostic and Future-Proof š¤
Source: Dev.to
Sure! Iām happy to help tidy up the Markdown for you. Could you please paste the specific Markdown section youād like cleaned up? Once I have the content, Iāll format it properly while preserving all headings, links, code blocks, lists, tables, etc.
Bedrock Agents ā Reasoning, Planning, and Tool Use on Top of Foundation Models
Deploy an agent with Terraform so that upgrading to the next Claude or Nova model is just a single variable change.
SeriesāÆ1 ā We previously deployed a Bedrock endpoint and called a foundation model directly (singleāturn, stateless, no tools).
Your AWS account is AIāready ā Deploy your first AI endpoint with Terraform in 10āÆminutes
Why Agents Are Different
An agent wraps a foundation model with an orchestration layer that can:
- Reason through multiāstep tasks
- Maintain conversation state across turns
- Decide when to use tools and loop until a satisfactory answer is produced
The model is the brain; the agent is the decisionāmaking layer.
š§ Endpoint vs. Agent: Whatās Different?
| Capability | Direct Endpoint (SeriesāÆ1) | Bedrock Agent |
|---|---|---|
| Interaction | Single request/response | Multiāturn conversation |
| State | Stateless | Session memory (configurable TTL) |
| Tool use | Manual implementation | Builtāin action groups |
| Reasoning | Whatever the model does | ReActāstyle orchestration loop |
| Versioning | Model version only | Agent aliasāÆ+āÆversion snapshots |
| Guardrails | Separate API call | Attach directly to agent |
The agent adds autonomy: instead of you deciding which API to call, the agent decides based on the userās request and the available tools.
š§ Model Configuration: The FutureāProof Layer
Separate model selection from agent logic by defining the model as a variable with sensible defaults.
# variables.tf
variable "agent_model" {
description = "Foundation model ID for the agent. Change this to upgrade models."
type = object({
id = string # Bedrock model ID
display = string # Humanāreadable name for tags/logs
})
default = {
id = "anthropic.claude-sonnet-4-20250514-v1:0"
display = "Claude Sonnet 4"
}
}
variable "agent_name" {
description = "Name of the Bedrock agent"
type = string
default = "assistant"
}
variable "agent_instruction" {
description = "System instruction for the agent. Defines its behavior and persona."
type = string
}
variable "idle_session_ttl" {
description = "How long agent sessions stay open (seconds). Max 3600."
type = number
default = 600
}Updating the Model
When a new model is released, edit one place in your .tfvars files:
# environments/dev.tfvars
agent_model = {
id = "anthropic.claude-sonnet-4-20250514-v1:0"
display = "Claude Sonnet 4"
}
# environments/prod.tfvars
agent_model = {
id = "anthropic.claude-sonnet-4-20250514-v1:0"
display = "Claude Sonnet 4"
}When a newer model becomes available, update the block in both files (or in a shared .tfvars file) to:
# Example update
agent_model = {
id = "anthropic.claude-next-model-v1:0"
display = "Claude Next"
}Run terraform apply and the agent automatically uses the new modelāno code changes, no redeployment of application logic.
š§ Terraform: Deploy the Agent
Data Source ā Model Validation
# agent/main.tf
data "aws_bedrock_foundation_model" "agent" {
model_id = var.agent_model.id
}The data source validates that the model ID exists and is available in the selected region before any resources are created. A typo or an unavailable model will cause terraform plan to fail early.
IAM Role
# agent/iam.tf
resource "aws_iam_role" "agent" {
name = "${var.environment}-${var.agent_name}-agent-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "bedrock.amazonaws.com" }
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
}]
})
}
resource "aws_iam_role_policy" "agent_invoke" {
name = "bedrock-invoke"
role = aws_iam_role.agent.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "bedrock:InvokeModel"
Resource = "arn:aws:bedrock:${var.region}::foundation-model/${var.agent_model.id}"
}]
})
}The policy references var.agent_model.id, so swapping models automatically updates the required permissions.
The Agent Resource
# agent/agent.tf
resource "aws_bedrockagent_agent" "this" {
agent_name = "${var.environment}-${var.agent_name}"
agent_resource_role_arn = aws_iam_role.agent.arn
description = "Agent: ${var.agent_model.display} | Env: ${var.environment}"
foundation_model = data.aws_bedrock_foundation_model.agent.model_id
instruction = var.agent_instruction
idle_session_ttl_in_seconds = var.idle_session_ttl
prepare_agent = true
tags = {
Environment = var.environment
Model = var.agent_model.display
ManagedBy = "terraform"
}
}Note:
prepare_agent = trueis crucial. After creation or an update, a Bedrock agent must be prepared before it can serve requests.
TL;DR
- Define the model as a variable (
agent_model). - Reference the variable everywhere (validation data source, IAM policy, agent resource).
- Update the model ID in a single
.tfvarsfile when a newer model is released. - Run
terraform applyā the agent is rebuilt with the new model automatically.
Your Bedrock agent is now futureāproof, versionāagnostic, and fully managed through Terraform. šÆ
Setting sts to true triggers preparation automatically on create and update
Agent Alias (Deployable Endpoint)
# agent/alias.tf
resource "aws_bedrockagent_agent_alias" "live" {
agent_alias_name = "${var.environment}-live"
agent_id = aws_bedrockagent_agent.this.agent_id
description = "Live alias for ${var.environment}"
tags = {
Environment = var.environment
Model = var.agent_model.display
}
}The alias is the name your application uses to call the agentāthink of it as a DNS record that points to a specific agent version.
- When you update the agent (e.g., change the model or instructions), a new version is created.
- The alias is then repointed to this new version, so your application code never needs to change; it always references the alias, not a concrete version.
š§Ŗ Invoke the Agent
Your application code uses the alias ARN, which stays stable across model upgrades:
import boto3
client = boto3.client("bedrock-agent-runtime")
response = client.invoke_agent(
agentId="YOUR_AGENT_ID",
agentAliasId="YOUR_ALIAS_ID",
sessionId="user-session-123",
inputText="What were our Q3 revenue numbers?"
)
# Stream the response
for event in response["completion"]:
if "chunk" in event:
print(event["chunk"]["bytes"].decode(), end="")Note:
sessionIdenables multiāturn conversation. The agent remembers context within a session up to theidle_session_ttl. No additional sessionāmanagement code is required on your side.
š The Upgrade Workflow
When a new model launches, the upgrade consists of three steps:
- Update
.tfvarsā modifyagent_model.idandagent_model.display. - Run
terraform planā review the changes (agent updated, IAM policy updated, tags updated). - Run
terraform applyā the agent is reāprepared with the new model and the alias is updated.
Note: Your application code doesnāt change. The alias ID stays the same; only the agentās intelligence improves.
Why This Works
- Variableādriven: All mutable items (model ID, instructions, session TTL) are externalized into variables.
- Stable infrastructure: Fixed resources (alias ID, agent ID, integration points) remain untouched.
This separation lets you upgrade models safely and repeatedly without touching your application code.
š”ļø Attaching Guardrails (Optional)
If you deployed guardrails in SeriesāÆ1, attach them to the agent:
resource "aws_bedrockagent_agent" "this" {
# ... all previous config ...
guardrail_configuration {
guardrail_identifier = var.guardrail_id
guardrail_version = var.guardrail_version
}
}The guardrails apply to every agent interaction automaticallyāno perārequest configuration needed.
ā ļø Gotchas and Tips
Model availability varies by region
Not every model is available in every AWS region. Thedata.aws_bedrock_foundation_modeldata source catches this at plan time.Agent preparation is asynchronous
Whenprepare_agent = true, Terraform waits for preparation to complete. If youāre adding action groups (covered in the next post), remember that changes to action groups also require reāpreparation. You may need anull_resourcewith alocalāexecprovisioner to trigger the reāpreparation.Session TTL costs
Longer sessions consume more memory. For development, a TTL of 600āÆseconds (10āÆminutes) is sufficient. For production chatābots, consider 1800ā3600āÆseconds. Sessions expire silentlyā the next message starts a new session.Alias versioning
Each time you update the agent and runapply, a new version is created. The alias always points to the latest version. If you need rollback capability, create a separate alias that points to a specific version number.
āļø Whatās Next
This is PostāÆ1 of the AI Agents with Terraform series.
| Post | Title |
|---|---|
| PostāÆ1 | Deploy First Bedrock Agent (you are here) š¤ |
| PostāÆ2 | Action Groups ā Connect Your Agent to APIs |
| PostāÆ3 | MultiāAgent Orchestration |
| PostāÆ4 | Agent + Knowledge Base Grounding |
Your first AI agent is deployed. It reasons, it remembers, and when the next model launches, upgrading is a oneāline change. The foundation model is a variable, not a commitment. š¤
Found this helpful? Follow for the full AI Agents with Terraform series! š¬