Give developers a guided experience for deploying AI models with Bedrock
AWS Bedrock is a framework for customizing LLMs based on a variety of foundational models: AWS Titan, Anthropic Claude, AI21 Labs Jambda, and more. While Bedrock is a general-purpose framework, the models that it supports have heterogenous parameter requirements.
In this guide, we'll adapt an AWS Bedrock Terraform example into a Resourcely Blueprint with attached Guardrails. This will give developers a simplified form for deploying AWS Bedrock, along with guidance and rules that help those developers configure their models successfully.
To get started, sign up for a free Resourcely account here.
The Bedrock resource uses foundational models as a baseline, takes training data from an S3 bucket, applies hyperparameter settings to the foundational model, and outputs data to an S3 bucket.
First, we'll adapt the aws_bedrock_custom_model resource into a Blueprint, giving users a form that will generate Terraform and create the resource on their behalf:
AWS Bedrock Resourcely Blueprint
---
constants:
__name: "{{ name }}_{{ __guid }}"
variables:
model_id:
desc: "Select the foundational model to use."
global_value: aws_foundational_models
custom_model_name:
desc: "The name of the custom model."
required: true
suggest: "example-model"
group: Model configuration
job_name:
suggest: "bedrock-example-job-1"
desc: "The name of the job for the custom model."
required: true
group: Model configuration
base_model_identifier:
desc: "The ARN of the base foundation model to customize."
required: true
group: Model configuration
epoch_count:
desc: "Number of epochs for training."
required: true
suggest: "1"
group: Hyperparameters
batch_size:
desc: "Batch size for the training job."
required: true
suggest: "8"
group: Hyperparameters
learning_rate:
desc: "Learning rate for the training job."
required: true
suggest: "0.00001"
group: Hyperparameters
early_stopping_patience:
desc: "Patience parameter for early stopping during training."
required: true
suggest: "6"
group: Hyperparameters
early_stopping_threshold:
desc: "Threshold for early stopping."
required: true
suggest: "0.01"
group: Hyperparameters
eval_percentage:
desc: "Percentage of the data used for evaluation."
required: true
suggest: "20.0"
group: Hyperparameters
output_s3_uri:
desc: "S3 URI for output data."
required: true
links_to: resource.aws_s3_bucket.id
group: Data configuration
training_data_s3_uri:
desc: "S3 URI for training data."
required: true
links_to: resource.aws_s3_bucket.id
group: Data configuration
groups:
Model configuration:
order: 1
desc: "Configuration for the custom model and its job settings."
Hyperparameters:
order: 2
desc: "Configuration for the training hyperparameters."
Data configuration:
order: 3
desc: "Configuration for the S3 locations of training and output data."
---
data "aws_caller_identity" "current" {}
data "aws_bedrock_foundation_model" "{{ __name }}" {
model_id = {{ model_id }}
}
resource "aws_bedrock_custom_model" "{{ __name }}" {
custom_model_name = {{ custom_model_name }}
job_name = {{ job_name }}
base_model_identifier = data.aws_bedrock_foundation_model.{{ __name }}.model_arn
role_arn = {{ role_arn }}
customization_type = "FINE_TUNING"
hyperparameters = {
epochCount = {{ epoch_count }}
batchSize = {{ batch_size }}
learningRate = {{ learning_rate }}
earlyStoppingPatience = {{ early_stopping_patience }}
earlyStoppingThreshold = {{ early_stopping_threshold }}
evalPercentage = {{ eval_percentage }}
}
output_data_config {
s3_uri = "s3://{{ output_s3_uri }}/data/"
}
training_data_config {
s3_uri = "s3://{{ training_data_s3_uri }}/data/train.jsonl"
}
}
Turning Terraform parameters into UI inputs
We have added variables for most AWS Bedrock custom model options:
Model name
Job name
Foundational model
Hyperparameters
Adding these variables immediately gives us input fields in our form. See epoch count, for example
epochCount = {{ epoch_count }}
The {{ epoch_count }} variable references the following frontmatter:
epoch_count:
desc: "Number of epochs for training."
required: true
suggest: "1"
group: Hyperparameters
See the Developer UI tab for a preview of this variable in the UI
Custom model list
The Terraform provider uses a base_model_identifier, which expect the Amazon Resource Name (ARN) of the base model. To make it easy for our developers to simply pick a model (without having to look up its ARN), we create a Global Value list and reference it in the model_id variable:
model_id:
desc: "Select the foundational model to use."
global_value: aws_foundational_models
The aws_foundational_models set of global values looks like this in our resulting form:
Adding complementary resources
We don't want to assume that our developers will know how to properly configure IAM and S3 to use with this Bedrock model. We'll add the following Resourcely Blueprint code:
This code is a good mix of hardcoded Terraform values, and Resourcely variables that take user input. We'll go through each of the added sections:
First, we add an IAM Role that is assumable. This is all hardcoded except for the name of the role, meaning that it will not be impacted by any user input.
We then create a policy and attach it to the role. We hardcode the allowed actions, and automatically link the policy only to the resources that we have created. This creates least privilege, without requiring the developers to know how to create IAM properly.
We also add the following to our full blueprint frontmatter. Notice the links_to statement. This instructs our S3 bucket variables that we created as part of the custom model resource, to automatically surface the S3 buckets that we have created.
output_s3_uri:
desc: "S3 URI for output data."
required: true
links_to: resource.aws_s3_bucket.id
group: Data configuration
training_data_s3_uri:
desc: "S3 URI for training data."
required: true
links_to: resource.aws_s3_bucket.id
group: Data configuration
In practice, this looks like the following in a Resourcely form. Links reduce cognitive load on developers by automatically associating appropriate resources together.
Now we have a fully completed Blueprint that abstracts away complexity, while allowing developers to generate Terraform and deploy the infrastructure they need in a secure-by-default way.
Giving guidance with Guardrails
Now that we have made it easier to deploy AI for our developers, we want to give them more guidance. All of the various models supported by Bedrock have different hyperparameter minimums and maximums. We can build this guidance into Resourcely with Guardrails.
Adding a context question
Let's say that you want to control when this guidance is displayed, through the use of a separate question. You can create Global Context questions, where users choose which AI model they want to use.
Building our Guardrail
Now that we have context, we can customize our Guardrails. This will let us put restrictions on our form that are different, based on the model they are using.
GUARDRAIL "Hyperparameter guidelines for Titan models"
WHEN aws_bedrock_custom_model AND CONTEXT bedrock_model_type = "Titan"
REQUIRE hyperparameters.epochCount MATCHES REGEX "^[1-5]$"
Resulting form differences
This Guardrail requires that epochCount is a whole integer from 1 to 5 when the model type is Titan. In our form for users, a Guardrail lock now appears.
Advanced Guardrail
We can add more restrictions for each of our model-specific Guardrails:
GUARDRAIL "Hyperparameter guidelines for Titan models"
WHEN aws_bedrock_custom_model AND CONTEXT bedrock_model_type = "Titan"
REQUIRE hyperparameters.epochCount MATCHES REGEX "^[1-5]$"
AND hyperparameters.batchSize = "1"
AND hyperparameters.learningRate MATCHES REGEX "^(0\.0000001|\.000001|0\.00001)$" AND hyperparameters.learningRateWarmupSteps MATCHES REGEX "^[0-20]$"
This advanced Guardrail will guide the user to select appropriate values for Titan mode parameters, ensuring that their models don't needlessly fail - wasting infrastructure and time.
Final Blueprint
Putting it all together, here is the final Resourcely Blueprint for streamlining AI deployment:
AWS Bedrock with S3 and IAM Blueprint
---
constants:
__name: "{{ name }}_{{ __guid }}"
variables:
YourIAMUserName:
suggest: "myusername"
model_id:
desc: "Select the foundational model to use."
global_value: aws_foundational_models
custom_model_name:
desc: "The name of the custom model."
required: true
suggest: "example-model"
group: Model configuration
job_name:
suggest: "bedrock-example-job-1"
desc: "The name of the job for the custom model."
required: true
group: Model configuration
base_model_identifier:
desc: "The ARN of the base foundation model to customize."
required: true
group: Model configuration
role_arn:
desc: "The ARN of the IAM role with permissions to manage the custom model."
required: true
links_to: resource.aws_iam_role.arn
group: Model configuration
epoch_count:
desc: "Number of epochs for training."
required: true
suggest: "1"
group: Hyperparameters
batch_size:
desc: "Batch size for the training job."
required: true
suggest: "8"
group: Hyperparameters
learning_rate:
desc: "Learning rate for the training job."
required: true
suggest: "0.00001"
group: Hyperparameters
early_stopping_patience:
desc: "Patience parameter for early stopping during training."
required: true
suggest: "6"
group: Hyperparameters
early_stopping_threshold:
desc: "Threshold for early stopping."
required: true
suggest: "0.01"
group: Hyperparameters
eval_percentage:
desc: "Percentage of the data used for evaluation."
required: true
suggest: "20.0"
group: Hyperparameters
output_s3_uri:
desc: "S3 URI for output data."
required: true
links_to: resource.aws_s3_bucket.id
group: Data configuration
training_data_s3_uri:
desc: "S3 URI for training data."
required: true
links_to: resource.aws_s3_bucket.id
group: Data configuration
bucket:
group: Data configuration
suggest: mybasicbedrockbucket
groups:
Model configuration:
order: 1
desc: "Configuration for the custom model and its job settings."
Hyperparameters:
order: 2
desc: "Configuration for the training hyperparameters."
Data configuration:
order: 3
desc: "Configuration for the S3 locations of training and output data."
Bucket:
order: 4
desc: "The S3 bucket where the training data is stored"
---
data "aws_caller_identity" "current" {}
data "aws_bedrock_foundation_model" "{{ __name }}" {
model_id = {{ model_id }}
}
resource "aws_bedrock_custom_model" "{{ __name }}" {
custom_model_name = {{ custom_model_name }}
job_name = {{ job_name }}
base_model_identifier = data.aws_bedrock_foundation_model.{{ __name }}.model_arn
role_arn = {{ role_arn }}
customization_type = "FINE_TUNING"
hyperparameters = {
epochCount = {{ epoch_count }}
batchSize = {{ batch_size }}
learningRate = {{ learning_rate }}
earlyStoppingPatience = {{ early_stopping_patience }}
earlyStoppingThreshold = {{ early_stopping_threshold }}
evalPercentage = {{ eval_percentage }}
}
output_data_config {
s3_uri = "s3://{{ output_s3_uri }}/data/"
}
training_data_config {
s3_uri = "s3://{{ training_data_s3_uri }}/data/train.jsonl"
}
}
resource "aws_iam_role" "bedrock_{{ __name }}" {
assume_role_policy = jsonencode(
{
"Version" : "2012-10-17",
"Statement" : [
{
"Effect" : "Allow",
"Principal" : { "Service" : "bedrock.amazonaws.com" },
"Action" : "sts:AssumeRole",
}
]
}
)
}
resource "aws_iam_policy" "bedrock_access_policy_{{ __name }}" {
policy = jsonencode(
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:PutObject"
],
"Resource": [
"${ aws_s3_bucket.{{ __name }}.arn }",
"${ aws_s3_bucket.{{ __name }}.arn }/*"
]
},
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:GetFoundationModel",
"bedrock:ListModels",
"bedrock:GetModel",
"bedrock:CreateModelCustomizationJob"
],
"Resource": data.aws_bedrock_foundation_model.{{ __name }}.model_arn
}
]
}
)
}
resource "aws_iam_role_policy_attachment" "attach_bedrock_access_policy_{{ __name }}" {
role = aws_iam_role.bedrock_{{ __name }}.name
policy_arn = aws_iam_policy.bedrock_access_policy_{{ __name }}.arn
}
resource "aws_s3_bucket" "{{ __name }}" {
bucket = "{{ bucket }}"
}
resource "aws_s3_bucket_public_access_block" "{{ __name }}" {
bucket = aws_s3_bucket.{{ __name }}.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Hyperparameter Guardrail
GUARDRAIL "Hyperparameter guidelines for Titan models"
WHEN aws_bedrock_custom_model AND CONTEXT bedrock_model_type = "Titan"
REQUIRE hyperparameters.epochCount MATCHES REGEX "^[1-5]$"
AND hyperparameters.batchSize = "1"
AND hyperparameters.learningRate MATCHES REGEX "^(0\.0000001|\.000001|0\.00001)$" AND hyperparameters.learningRateWarmupSteps MATCHES REGEX "^[0-20]$"
Terraform is created
When this form is completed, properly configured Terraform that meets your requirements is generated and submitted through your CI pipeline. This gives your company the benefits of infrastructure as code, without the steep learning curve or specialized cloud services knowledge.
Get started with Resourcely
If you want to recreate this for yourself, you can do so using Resourcely! Sign up for free here, and find the preloaded Bedrock Guardrails in the Foundry.