Deploy Your First Bedrock Agent with Terraform: Model-Agnostic and Future-Proof šŸ¤–

Published: (March 13, 2026 at 03:00 AM EDT)
8 min read
Source: Dev.to

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?

CapabilityDirect Endpoint (Series 1)Bedrock Agent
InteractionSingle request/responseMulti‑turn conversation
StateStatelessSession memory (configurable TTL)
Tool useManual implementationBuilt‑in action groups
ReasoningWhatever the model doesReAct‑style orchestration loop
VersioningModel version onlyAgent alias + version snapshots
GuardrailsSeparate API callAttach 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 = true is crucial. After creation or an update, a Bedrock agent must be prepared before it can serve requests.


TL;DR

  1. Define the model as a variable (agent_model).
  2. Reference the variable everywhere (validation data source, IAM policy, agent resource).
  3. Update the model ID in a single .tfvars file when a newer model is released.
  4. 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: sessionId enables multi‑turn conversation. The agent remembers context within a session up to the idle_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:

  1. Update .tfvars – modify agent_model.id and agent_model.display.
  2. Run terraform plan – review the changes (agent updated, IAM policy updated, tags updated).
  3. 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. The data.aws_bedrock_foundation_model data source catches this at plan time.

  • Agent preparation is asynchronous
    When prepare_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 a null_resource with a local‑exec provisioner 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 run apply, 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.

PostTitle
Post 1Deploy First Bedrock Agent (you are here) šŸ¤–
Post 2Action Groups – Connect Your Agent to APIs
Post 3Multi‑Agent Orchestration
Post 4Agent + 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! šŸ’¬

0 views
Back to Blog

Related posts

Read more Ā»

AnswerThis (YC F25) Is Hiring

Who we are Trillions of dollars flow into global R&D every year, and a massive share of it goes to researchers manually reading papers, writing literature revi...