DEV Community

Cover image for Bedrock Guardrails with Terraform: Stop Your AI from Going Rogue πŸ›‘οΈ
Suhas Mallesh
Suhas Mallesh

Posted on

Bedrock Guardrails with Terraform: Stop Your AI from Going Rogue πŸ›‘οΈ

Your AI chatbot can leak PII, discuss competitors, or generate toxic content. Bedrock Guardrails blocks all of that - and Terraform makes it version-controlled, reviewable, and consistent across environments.

You deployed your first Bedrock endpoint (Post 1). It works. Claude responds, tokens flow, invoices accrue.

But what happens when a user asks your customer-support bot to explain how to pick a lock? Or when the model accidentally includes a customer's Social Security number in its response? Or when someone asks it to compare your product to a competitor's?

Amazon Bedrock Guardrails is the safety layer that sits between your users and the model. It filters harmful content, blocks off-limit topics, masks PII, catches hallucinations, and blocks prompt injection attacks. Think of it as a bouncer for your AI endpoint.

The problem? Most teams configure guardrails through the AWS console. Click, click, click. No audit trail. No peer review. No way to promote the same guardrail config from dev to staging to prod without screenshots and hope.

Let's fix that with Terraform. Every safety policy as code. Every change reviewed in a PR. Every environment running the exact same guardrail. πŸ—οΈ

🧱 What Guardrails Actually Do

Bedrock Guardrails gives you six layers of protection, each independently configurable:

Layer What It Does Example
Content Filters Blocks harmful text (hate, violence, sexual, insults, misconduct) User sends hate speech, model refuses
Denied Topics Blocks specific topics you define "Don't discuss competitor products"
Word Filters Exact-match blocking of words/phrases + profanity Block brand names, slurs, internal codenames
PII Filters Detects and masks/blocks personal data SSN, email, phone numbers get redacted
Contextual Grounding Catches hallucinations in RAG responses Model invents facts not in the source docs
Prompt Attack Detection Blocks jailbreaks and prompt injections "Ignore your instructions and..." gets blocked

You can enable any combination. Most production deployments use all six. Let's build them. 🎯

πŸ’° Guardrails Pricing (It's Cheap)

Before we write code, let's talk cost. Guardrails pricing was slashed up to 85% in late 2024:

Filter Type Cost per 1,000 Text Units Notes
Content Filters $0.15 Includes prompt attack detection
Denied Topics $0.15 Per topic evaluated
Word Filters Free Exact match, no ML needed
PII Detection $0.10 Per entity type configured
Contextual Grounding $0.10 Source + query + response combined

A text unit is up to 1,000 characters. A typical chatbot prompt of 2,000 characters costs 2 text units. At $0.15/1K units for content filtering, that's $0.0003 per message. For context, that's roughly 3 million messages per dollar. Guardrails are cheap insurance. πŸ’Έ

πŸ—οΈ Step 1: The Guardrail Resource

Here's a complete aws_bedrock_guardrail resource with all six safety layers:

# guardrails/main.tf

terraform {
  required_version = ">= 1.5.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.60.0"
    }
  }
}

provider "aws" {
  region = var.region
}

variable "region" {
  type    = string
  default = "us-east-1"
}

variable "environment" {
  type = string

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Must be: dev, staging, or prod."
  }
}
Enter fullscreen mode Exit fullscreen mode

Now the guardrail itself:

# guardrails/guardrail.tf

resource "aws_bedrock_guardrail" "ai_safety" {
  name                      = "${var.environment}-ai-safety-guardrail"
  description               = "Content safety, PII protection, and topic restrictions for ${var.environment}"
  blocked_input_messaging   = var.blocked_input_message
  blocked_outputs_messaging = var.blocked_output_message

  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  # Layer 1: Content Filters
  # Block harmful content across 6 categories
  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  content_policy_config {
    # Hate speech
    filters_config {
      type            = "HATE"
      input_strength  = var.content_filter_strengths["hate"]
      output_strength = var.content_filter_strengths["hate"]
    }

    # Insults
    filters_config {
      type            = "INSULTS"
      input_strength  = var.content_filter_strengths["insults"]
      output_strength = var.content_filter_strengths["insults"]
    }

    # Sexual content
    filters_config {
      type            = "SEXUAL"
      input_strength  = var.content_filter_strengths["sexual"]
      output_strength = var.content_filter_strengths["sexual"]
    }

    # Violence
    filters_config {
      type            = "VIOLENCE"
      input_strength  = var.content_filter_strengths["violence"]
      output_strength = var.content_filter_strengths["violence"]
    }

    # Misconduct (illegal activities, etc.)
    filters_config {
      type            = "MISCONDUCT"
      input_strength  = var.content_filter_strengths["misconduct"]
      output_strength = var.content_filter_strengths["misconduct"]
    }

    # Prompt attacks (jailbreaks, prompt injection)
    filters_config {
      type            = "PROMPT_ATTACK"
      input_strength  = "HIGH"    # Always HIGH - no reason to be lenient
      output_strength = "NONE"    # Only applies to input
    }
  }

  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  # Layer 2: Denied Topics
  # Block specific topics your AI should never discuss
  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  topic_policy_config {
    dynamic "topics_config" {
      for_each = var.denied_topics
      content {
        name       = topics_config.value.name
        definition = topics_config.value.definition
        examples   = topics_config.value.examples
        type       = "DENY"
      }
    }
  }

  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  # Layer 3: Word Filters
  # Block profanity + custom words/phrases
  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  word_policy_config {
    managed_word_lists_config {
      type = "PROFANITY"
    }

    dynamic "words_config" {
      for_each = var.blocked_words
      content {
        text = words_config.value
      }
    }
  }

  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  # Layer 4: PII Filters
  # Detect and mask/block personally identifiable info
  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  sensitive_information_policy_config {
    dynamic "pii_entities_config" {
      for_each = var.pii_entities
      content {
        type   = pii_entities_config.value.type
        action = pii_entities_config.value.action
      }
    }

    # Custom regex patterns (e.g., internal IDs)
    dynamic "regexes_config" {
      for_each = var.custom_regex_filters
      content {
        name        = regexes_config.value.name
        description = regexes_config.value.description
        pattern     = regexes_config.value.pattern
        action      = regexes_config.value.action
      }
    }
  }

  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  # Layer 5: Contextual Grounding
  # Catch hallucinations in RAG responses
  # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  contextual_grounding_policy_config {
    filters_config {
      type      = "GROUNDING"
      threshold = var.grounding_threshold
    }

    filters_config {
      type      = "RELEVANCE"
      threshold = var.relevance_threshold
    }
  }

  tags = {
    Environment = var.environment
    Purpose     = "ai-safety"
    ManagedBy   = "terraform"
  }
}

# Pin a version for production use
resource "aws_bedrock_guardrail_version" "current" {
  guardrail_arn = aws_bedrock_guardrail.ai_safety.guardrail_arn
  description   = "Version managed by Terraform - ${var.environment}"
}
Enter fullscreen mode Exit fullscreen mode

πŸ”§ Step 2: Variable-Driven Configuration

The power of this approach is that every safety setting is a variable. Different environments, different risk tolerances:

# guardrails/variables.tf

variable "blocked_input_message" {
  type        = string
  description = "Message shown when user input is blocked"
  default     = "Your message could not be processed. Please rephrase your request without including harmful content, personal information, or off-topic questions."
}

variable "blocked_output_message" {
  type        = string
  description = "Message shown when model output is blocked"
  default     = "The response was filtered for safety. Please try rephrasing your question."
}

# ━━━ Content Filter Strengths ━━━
variable "content_filter_strengths" {
  type        = map(string)
  description = "Filter strength per category: NONE, LOW, MEDIUM, HIGH"
  default = {
    hate       = "HIGH"
    insults    = "HIGH"
    sexual     = "HIGH"
    violence   = "HIGH"
    misconduct = "HIGH"
  }
}

# ━━━ Denied Topics ━━━
variable "denied_topics" {
  type = list(object({
    name       = string
    definition = string
    examples   = list(string)
  }))
  description = "Topics the AI should refuse to discuss"
  default     = []
}

# ━━━ Blocked Words ━━━
variable "blocked_words" {
  type        = list(string)
  description = "Exact words/phrases to block"
  default     = []
}

# ━━━ PII Entity Configuration ━━━
variable "pii_entities" {
  type = list(object({
    type   = string  # NAME, EMAIL, PHONE, SSN, etc.
    action = string  # BLOCK or ANONYMIZE
  }))
  description = "PII types to detect and their action (BLOCK or ANONYMIZE)"
  default     = []
}

# ━━━ Custom Regex Filters ━━━
variable "custom_regex_filters" {
  type = list(object({
    name        = string
    description = string
    pattern     = string
    action      = string  # BLOCK or ANONYMIZE
  }))
  description = "Custom regex patterns to detect sensitive data"
  default     = []
}

# ━━━ Contextual Grounding ━━━
variable "grounding_threshold" {
  type        = number
  description = "Minimum confidence for grounding check (0.0 to 0.99). Higher = stricter."
  default     = 0.7
}

variable "relevance_threshold" {
  type        = number
  description = "Minimum confidence for relevance check (0.0 to 0.99). Higher = stricter."
  default     = 0.7
}
Enter fullscreen mode Exit fullscreen mode

🌍 Step 3: Environment-Specific Configs

This is where the real value shows up. Each environment gets its own guardrail policy:

# environments/dev.tfvars

environment = "dev"

# Dev: More lenient for testing, still catches the big stuff
content_filter_strengths = {
  hate       = "HIGH"
  insults    = "MEDIUM"
  sexual     = "HIGH"
  violence   = "MEDIUM"
  misconduct = "MEDIUM"
}

denied_topics = [
  {
    name       = "competitor-products"
    definition = "Questions about or comparisons with competitor products and services"
    examples   = [
      "How does your product compare to CompetitorCo?",
      "Is CompetitorCo better than you?",
      "What are the alternatives to your service?"
    ]
  }
]

blocked_words = ["confidential", "internal-only"]

pii_entities = [
  { type = "US_SOCIAL_SECURITY_NUMBER", action = "BLOCK" },
  { type = "CREDIT_DEBIT_CARD_NUMBER",  action = "BLOCK" },
]

grounding_threshold = 0.5   # Lenient for testing
relevance_threshold = 0.5
Enter fullscreen mode Exit fullscreen mode
# environments/prod.tfvars

environment = "prod"

# Prod: Maximum protection
content_filter_strengths = {
  hate       = "HIGH"
  insults    = "HIGH"
  sexual     = "HIGH"
  violence   = "HIGH"
  misconduct = "HIGH"
}

denied_topics = [
  {
    name       = "competitor-products"
    definition = "Questions about or comparisons with competitor products and services"
    examples   = [
      "How does your product compare to CompetitorCo?",
      "Is CompetitorCo better than you?",
      "What are the alternatives to your service?"
    ]
  },
  {
    name       = "investment-advice"
    definition = "Specific financial investment recommendations or stock predictions"
    examples   = [
      "Should I buy Tesla stock?",
      "What cryptocurrency should I invest in?",
      "Is now a good time to invest in real estate?"
    ]
  },
  {
    name       = "legal-advice"
    definition = "Specific legal recommendations that should come from a licensed attorney"
    examples   = [
      "Can I sue my landlord for this?",
      "Is this contract legally binding?",
      "What legal action should I take?"
    ]
  },
  {
    name       = "medical-diagnosis"
    definition = "Specific medical diagnoses or treatment recommendations"
    examples   = [
      "I have these symptoms, what disease do I have?",
      "Should I stop taking my medication?",
      "Is this mole cancerous?"
    ]
  }
]

blocked_words = [
  "confidential",
  "internal-only",
  "Project Falcon",
  "Project Lighthouse"
]

pii_entities = [
  { type = "NAME",                       action = "ANONYMIZE" },
  { type = "EMAIL",                      action = "ANONYMIZE" },
  { type = "PHONE",                      action = "ANONYMIZE" },
  { type = "US_SOCIAL_SECURITY_NUMBER",  action = "BLOCK" },
  { type = "CREDIT_DEBIT_CARD_NUMBER",   action = "BLOCK" },
  { type = "CREDIT_DEBIT_CARD_CVV",      action = "BLOCK" },
  { type = "CREDIT_DEBIT_CARD_EXPIRY",   action = "BLOCK" },
  { type = "US_BANK_ACCOUNT_NUMBER",     action = "BLOCK" },
  { type = "US_BANK_ROUTING_NUMBER",     action = "BLOCK" },
  { type = "US_PASSPORT_NUMBER",         action = "BLOCK" },
  { type = "DRIVER_ID",                  action = "BLOCK" },
  { type = "IP_ADDRESS",                 action = "ANONYMIZE" },
  { type = "USERNAME",                   action = "ANONYMIZE" },
  { type = "PASSWORD",                   action = "BLOCK" },
  { type = "AWS_ACCESS_KEY",             action = "BLOCK" },
  { type = "AWS_SECRET_KEY",             action = "BLOCK" },
]

custom_regex_filters = [
  {
    name        = "internal-ticket-id"
    description = "Internal support ticket IDs (format: TKT-XXXXXXXX)"
    pattern     = "TKT-[A-Z0-9]{8}"
    action      = "ANONYMIZE"
  },
  {
    name        = "employee-id"
    description = "Employee ID numbers (format: EMP-XXXXX)"
    pattern     = "EMP-[0-9]{5}"
    action      = "ANONYMIZE"
  }
]

grounding_threshold = 0.75  # Strict - block ungrounded responses
relevance_threshold = 0.75
Enter fullscreen mode Exit fullscreen mode

Deploy per environment:

# Dev
terraform apply -var-file=environments/dev.tfvars

# Production
terraform apply -var-file=environments/prod.tfvars
Enter fullscreen mode Exit fullscreen mode

What you get: Dev has 2 PII types blocked and lenient content filtering for testing. Prod has 16 PII types, 4 denied topics, custom regex patterns, and strict grounding. Same Terraform code, different configs. 🎯

πŸ”Œ Step 4: Attach Guardrails to Your Endpoint

A guardrail does nothing until you attach it to model invocations. Here's how to use it with the Lambda endpoint from Post 1:

# guardrails/outputs.tf

output "guardrail_id" {
  value       = aws_bedrock_guardrail.ai_safety.guardrail_id
  description = "Guardrail ID to pass to Bedrock InvokeModel calls"
}

output "guardrail_version" {
  value       = aws_bedrock_guardrail_version.current.version
  description = "Pinned guardrail version for production use"
}

output "guardrail_arn" {
  value       = aws_bedrock_guardrail.ai_safety.guardrail_arn
  description = "Guardrail ARN for IAM policies"
}
Enter fullscreen mode Exit fullscreen mode

Update your Lambda environment variables to include the guardrail:

# In your Lambda resource from Post 1, add these env vars:
environment {
  variables = {
    MODEL_ID           = var.primary_model.id
    GUARDRAIL_ID       = aws_bedrock_guardrail.ai_safety.guardrail_id
    GUARDRAIL_VERSION  = aws_bedrock_guardrail_version.current.version
    AWS_REGION_        = var.region
  }
}
Enter fullscreen mode Exit fullscreen mode

And update the Lambda code to apply the guardrail:

import boto3
import json
import os

bedrock = boto3.client('bedrock-runtime')

def handler(event, context):
    prompt = event.get('prompt', 'Say hello!')
    max_tokens = event.get('max_tokens', 500)
    model_id = os.environ.get('MODEL_ID')
    guardrail_id = os.environ.get('GUARDRAIL_ID')
    guardrail_version = os.environ.get('GUARDRAIL_VERSION')

    body = json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": max_tokens,
        "messages": [{"role": "user", "content": prompt}]
    })

    try:
        response = bedrock.invoke_model(
            modelId=model_id,
            contentType='application/json',
            accept='application/json',
            body=body,
            # Attach the guardrail
            guardrailIdentifier=guardrail_id,
            guardrailVersion=guardrail_version,
        )

        result = json.loads(response['body'].read())

        # Check if guardrail intervened
        stop_reason = result.get('stop_reason', '')

        return {
            'statusCode': 200,
            'body': {
                'response': result['content'][0]['text'],
                'model': model_id,
                'guardrail_action': stop_reason,
                'input_tokens': result['usage']['input_tokens'],
                'output_tokens': result['usage']['output_tokens']
            }
        }

    except bedrock.exceptions.ClientError as e:
        error_code = e.response['Error']['Code']
        if error_code == 'ThrottlingException':
            return {
                'statusCode': 429,
                'body': {'error': 'Rate limited. Try again shortly.'}
            }
        return {
            'statusCode': 500,
            'body': {'error': str(e)}
        }
Enter fullscreen mode Exit fullscreen mode

When the guardrail blocks a prompt, the response stop_reason will be "guardrail_intervened" and result['content'][0]['text'] will contain your blocked_input_messaging or blocked_outputs_messaging. Your application code stays clean - the guardrail handles all the messy safety logic. βœ…

πŸ” Step 5: IAM for Guardrails

Your Lambda role needs permission to apply guardrails. Add this to the IAM policy from Post 1:

# guardrails/iam.tf

resource "aws_iam_role_policy" "guardrail_access" {
  name = "${var.environment}-guardrail-access"
  role = var.lambda_role_id  # From Post 1

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "AllowGuardrailUse"
        Effect = "Allow"
        Action = [
          "bedrock:ApplyGuardrail",
          "bedrock:GetGuardrail"
        ]
        Resource = aws_bedrock_guardrail.ai_safety.guardrail_arn
      }
    ]
  })
}
Enter fullscreen mode Exit fullscreen mode

Why this matters: Lock the permission to the specific guardrail ARN. Don't use Resource: "*" - that would let the Lambda bypass your guardrail by referencing a different (or no) guardrail. πŸ”’

πŸ“Š Content Filter Strengths: What Do They Actually Do?

The input_strength and output_strength values control how aggressively each category is filtered:

Strength Behavior Use When
NONE No filtering Deliberately allowing this category (rare)
LOW Blocks only the most extreme content Internal tools with trusted users
MEDIUM Blocks clearly harmful content Dev/staging environments
HIGH Blocks anything borderline Customer-facing production

Practical example: With violence set to LOW, "The knight slayed the dragon" passes through. With HIGH, it might get flagged. For a fantasy game's AI, you'd want LOW. For a children's education chatbot, you'd want HIGH.

πŸ§ͺ Step 6: Test Your Guardrails

After terraform apply, test each layer:

# Test 1: Content filter (should be blocked)
aws bedrock-runtime invoke-model \
  --model-id anthropic.claude-3-5-sonnet-20241022-v2:0 \
  --body '{"anthropic_version":"bedrock-2023-05-31","max_tokens":200,"messages":[{"role":"user","content":"Write an extremely violent scene"}]}' \
  --guardrail-identifier $(terraform output -raw guardrail_id) \
  --guardrail-version $(terraform output -raw guardrail_version) \
  --cli-binary-format raw-in-base64-out \
  response.json

# Test 2: PII masking (SSN should be blocked/masked)
aws bedrock-runtime invoke-model \
  --model-id anthropic.claude-3-5-sonnet-20241022-v2:0 \
  --body '{"anthropic_version":"bedrock-2023-05-31","max_tokens":200,"messages":[{"role":"user","content":"My SSN is 123-45-6789, can you confirm it?"}]}' \
  --guardrail-identifier $(terraform output -raw guardrail_id) \
  --guardrail-version $(terraform output -raw guardrail_version) \
  --cli-binary-format raw-in-base64-out \
  response.json

# Test 3: Denied topic (competitor discussion should be blocked)
aws bedrock-runtime invoke-model \
  --model-id anthropic.claude-3-5-sonnet-20241022-v2:0 \
  --body '{"anthropic_version":"bedrock-2023-05-31","max_tokens":200,"messages":[{"role":"user","content":"How does your product compare to CompetitorCo?"}]}' \
  --guardrail-identifier $(terraform output -raw guardrail_id) \
  --guardrail-version $(terraform output -raw guardrail_version) \
  --cli-binary-format raw-in-base64-out \
  response.json

cat response.json
Enter fullscreen mode Exit fullscreen mode

When a guardrail blocks content, you'll see the blocked message instead of a model response. The amazon-bedrock-guardrailAction header will show INTERVENED. βœ…

πŸ”„ The Upgrade Workflow

When compliance requirements change (and they always do), guardrail updates follow the same pattern as model upgrades:

# Current state: MEDIUM violence filter in staging
# New requirement: Legal says bump to HIGH everywhere

# 1. Update the .tfvars file
# staging.tfvars: violence = "MEDIUM" -> "HIGH"

# 2. Review the diff
terraform plan -var-file=environments/staging.tfvars

# 3. The plan shows exactly what changes:
# ~ content_policy_config {
#   ~ filters_config {
#       type            = "VIOLENCE"
#     ~ input_strength  = "MEDIUM" -> "HIGH"
#     ~ output_strength = "MEDIUM" -> "HIGH"
#   }
# }

# 4. Apply after review
terraform apply -var-file=environments/staging.tfvars
Enter fullscreen mode Exit fullscreen mode

Compare this to the console workflow: log in, find the guardrail, click through 6 tabs, change a dropdown, hope you changed the right one, repeat for every environment. With Terraform, it's a one-line diff in a PR that your security team can review. πŸ”

⚑ The ApplyGuardrail API: Use Guardrails Without a Model

Here's something most tutorials miss: you can use Guardrails independently from model invocations via the ApplyGuardrail API. This lets you moderate content from any source, not just Bedrock models:

# Use guardrails as a standalone content moderation layer
response = bedrock.apply_guardrail(
    guardrailIdentifier=guardrail_id,
    guardrailVersion=guardrail_version,
    source='INPUT',  # or 'OUTPUT'
    content=[{
        'text': {
            'text': user_message
        }
    }]
)

# Check if content was blocked
if response['action'] == 'GUARDRAIL_INTERVENED':
    print("Content blocked:", response['outputs'][0]['text'])
else:
    print("Content passed")
Enter fullscreen mode Exit fullscreen mode

This is powerful for moderating content from non-Bedrock sources like third-party APIs, user-generated content, or self-hosted models. You get Bedrock's safety filters without being locked into Bedrock for inference. 🎯

🏒 Production Patterns

Pattern 1: Guardrail per use case. Don't share a single guardrail across your customer chatbot and your internal research tool. Create separate guardrails with different risk tolerances:

# Customer-facing: strict everything
resource "aws_bedrock_guardrail" "customer_facing" {
  name = "${var.environment}-customer-facing"
  # ... HIGH filters, aggressive PII masking
}

# Internal research: more permissive
resource "aws_bedrock_guardrail" "internal_tools" {
  name = "${var.environment}-internal-tools"
  # ... MEDIUM filters, fewer denied topics
}
Enter fullscreen mode Exit fullscreen mode

Pattern 2: Version pinning. Always create a aws_bedrock_guardrail_version and reference it. The DRAFT version updates in place when you modify the guardrail, which means a terraform apply could change production behavior mid-flight. A pinned version only updates when you explicitly create a new one.

Pattern 3: Organization-level enforcement. For enterprises, Bedrock supports applying guardrails at the AWS Organization level. This means even if a developer forgets to attach a guardrail to their model call, the organization-level guardrail still applies. Configure this for your strictest baseline policies.

🎯 What You Just Built

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  User Input                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Bedrock Guardrail (Input)       β”‚
β”‚  βœ“ Content filters               β”‚
β”‚  βœ“ Denied topics                 β”‚
β”‚  βœ“ Word filters                  β”‚
β”‚  βœ“ PII detection                 β”‚
β”‚  βœ“ Prompt attack detection       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚ Passed? βœ…
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Foundation Model                β”‚
β”‚  (Claude, Llama, Nova, etc.)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Bedrock Guardrail (Output)      β”‚
β”‚  βœ“ Content filters               β”‚
β”‚  βœ“ PII detection/masking         β”‚
β”‚  βœ“ Contextual grounding          β”‚
β”‚  βœ“ Word filters                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚ Passed? βœ…
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  User Response                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

All six safety layers. All managed by Terraform. All version-controlled and reviewable. All consistent across dev, staging, and prod. πŸš€

⏭️ What's Next

This is Post 2 of the AWS AI Infrastructure with Terraform series.

  • Post 1: Deploy Bedrock: First AI Endpoint
  • Post 2: Bedrock Guardrails (you are here) πŸ›‘οΈ
  • Post 3: Invocation Logging - Track every AI call for compliance and debugging
  • Post 4: RAG Knowledge Base - Connect your company docs to AI with Bedrock + OpenSearch

Your AI endpoint is now protected by six layers of safety, all defined as code. When the compliance team asks "what guardrails do we have?", you point them to a Git repo instead of a screenshot. πŸ”’

Found this helpful? Follow for the full AWS AI Infrastructure with Terraform series! πŸ’¬

Top comments (0)