|
| 1 | +#!/bin/bash |
| 2 | +set -e |
| 3 | + |
| 4 | +# Accept folder name as parameter (default: aiagent) |
| 5 | +AGENT_NAME="${1:-aiagent}" |
| 6 | +RUNTIME_NAME=$(echo "${AGENT_NAME}" | tr '-' '_') |
| 7 | +VAR_PREFIX=$(echo "${AGENT_NAME}" | tr '[:lower:]-' '[:upper:]_') |
| 8 | + |
| 9 | +echo "==============================================" |
| 10 | +echo "aiagent-runtime.sh - AI Agent Runtime Deployment" |
| 11 | +echo "Agent: ${AGENT_NAME}" |
| 12 | +echo "==============================================" |
| 13 | + |
| 14 | +# Check if .envrc exists |
| 15 | +if [ ! -f ~/environment/.envrc ]; then |
| 16 | + echo "Error: ~/environment/.envrc not found. Run previous scripts first." |
| 17 | + exit 1 |
| 18 | +fi |
| 19 | + |
| 20 | +# Source existing environment |
| 21 | +source ~/environment/.envrc 2>/dev/null || true |
| 22 | + |
| 23 | +# Verify required variables for main agent |
| 24 | +if [ "${AGENT_NAME}" = "aiagent" ]; then |
| 25 | + if [ -z "${AIAGENT_USER_POOL_ID}" ] || [ -z "${AIAGENT_CLIENT_ID}" ] || [ -z "${AIAGENT_DISCOVERY_URL}" ]; then |
| 26 | + echo "Error: Missing Cognito variables. Run 07-aiagent-cognito.sh first." |
| 27 | + exit 1 |
| 28 | + fi |
| 29 | +fi |
| 30 | + |
| 31 | +# Get account ID and region |
| 32 | +ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text --no-cli-pager) |
| 33 | +AWS_REGION=$(aws configure get region) |
| 34 | + |
| 35 | +echo "Account: ${ACCOUNT_ID}" |
| 36 | +echo "Region: ${AWS_REGION}" |
| 37 | + |
| 38 | +## Creating the ECR repository |
| 39 | + |
| 40 | +echo "" |
| 41 | +echo "## Deploying the AI agent to AgentCore Runtime" |
| 42 | +echo "1. Create the ECR repository" |
| 43 | + |
| 44 | +ECR_EXISTS=$(aws ecr describe-repositories --repository-names "${AGENT_NAME}" \ |
| 45 | + --region ${AWS_REGION} --no-cli-pager 2>/dev/null || echo "") |
| 46 | + |
| 47 | +if [ -n "${ECR_EXISTS}" ]; then |
| 48 | + echo "ECR repository already exists: ${AGENT_NAME}" |
| 49 | +else |
| 50 | + echo "Creating ECR repository: ${AGENT_NAME}" |
| 51 | + aws ecr create-repository \ |
| 52 | + --repository-name "${AGENT_NAME}" \ |
| 53 | + --region ${AWS_REGION} \ |
| 54 | + --no-cli-pager |
| 55 | +fi |
| 56 | + |
| 57 | +## Creating the IAM role |
| 58 | + |
| 59 | +echo "" |
| 60 | +echo "2. Create the IAM role" |
| 61 | + |
| 62 | +if aws iam get-role --role-name "aiagent-runtime-role" --no-cli-pager >/dev/null 2>&1; then |
| 63 | + echo "IAM role already exists: aiagent-runtime-role" |
| 64 | +else |
| 65 | + echo "Creating IAM role: aiagent-runtime-role" |
| 66 | + |
| 67 | + cat > /tmp/trust-policy.json << EOF |
| 68 | +{ |
| 69 | + "Version": "2012-10-17", |
| 70 | + "Statement": [{ |
| 71 | + "Effect": "Allow", |
| 72 | + "Principal": {"Service": "bedrock-agentcore.amazonaws.com"}, |
| 73 | + "Action": "sts:AssumeRole", |
| 74 | + "Condition": { |
| 75 | + "StringEquals": {"aws:SourceAccount": "${ACCOUNT_ID}"}, |
| 76 | + "ArnLike": {"aws:SourceArn": "arn:aws:bedrock-agentcore:${AWS_REGION}:${ACCOUNT_ID}:*"} |
| 77 | + } |
| 78 | + }] |
| 79 | +} |
| 80 | +EOF |
| 81 | + |
| 82 | + aws iam create-role \ |
| 83 | + --role-name "aiagent-runtime-role" \ |
| 84 | + --permissions-boundary "arn:aws:iam::${ACCOUNT_ID}:policy/workshop-boundary" \ |
| 85 | + --assume-role-policy-document file:///tmp/trust-policy.json \ |
| 86 | + --no-cli-pager |
| 87 | + |
| 88 | + cat > /tmp/aiagent-policy.json << EOF |
| 89 | +{ |
| 90 | + "Version": "2012-10-17", |
| 91 | + "Statement": [ |
| 92 | + { |
| 93 | + "Effect": "Allow", |
| 94 | + "Action": ["bedrock:*", "bedrock-agentcore:*", "aws-marketplace:*"], |
| 95 | + "Resource": "*" |
| 96 | + }, |
| 97 | + { |
| 98 | + "Effect": "Allow", |
| 99 | + "Action": ["ecr:*", "logs:*", "xray:*", "cloudwatch:*", "s3:*"], |
| 100 | + "Resource": "*" |
| 101 | + } |
| 102 | + ] |
| 103 | +} |
| 104 | +EOF |
| 105 | + |
| 106 | + aws iam put-role-policy \ |
| 107 | + --role-name "aiagent-runtime-role" \ |
| 108 | + --policy-name "AgentCoreExecutionPolicy" \ |
| 109 | + --policy-document file:///tmp/aiagent-policy.json \ |
| 110 | + --no-cli-pager |
| 111 | + |
| 112 | + rm -f /tmp/trust-policy.json /tmp/aiagent-policy.json |
| 113 | +fi |
| 114 | + |
| 115 | +## Building and pushing the container image |
| 116 | + |
| 117 | +echo "" |
| 118 | +echo "3. Build and push the container image" |
| 119 | + |
| 120 | +ECR_URI="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${AGENT_NAME}" |
| 121 | + |
| 122 | +aws ecr get-login-password --region ${AWS_REGION} --no-cli-pager | \ |
| 123 | + docker login --username AWS --password-stdin "${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com" |
| 124 | + |
| 125 | +cd ~/environment/${AGENT_NAME} |
| 126 | + |
| 127 | +echo "Building container image (this may take a few minutes)..." |
| 128 | + |
| 129 | +MVN_PROFILES="" |
| 130 | +if [ "${AGENT_NAME}" = "aiagent" ]; then |
| 131 | + MVN_PROFILES="-Pheadless" |
| 132 | +fi |
| 133 | + |
| 134 | +mvn -ntp spring-boot:build-image \ |
| 135 | + ${MVN_PROFILES} \ |
| 136 | + -DskipTests \ |
| 137 | + -Dspring-boot.build-image.imageName="${ECR_URI}:latest" \ |
| 138 | + -Dspring-boot.build-image.imagePlatform=linux/arm64 |
| 139 | + |
| 140 | +echo "Pushing container image to ECR..." |
| 141 | +docker push "${ECR_URI}:latest" |
| 142 | + |
| 143 | +## Getting VPC configuration |
| 144 | + |
| 145 | +echo "" |
| 146 | +echo "4. Get VPC configuration" |
| 147 | + |
| 148 | +if [ -z "${VPC_ID}" ] || [ -z "${SUBNET_ID}" ] || [ -z "${SG_ID}" ]; then |
| 149 | + VPC_ID=$(aws ec2 describe-vpcs \ |
| 150 | + --filters "Name=tag:Name,Values=workshop-vpc" \ |
| 151 | + --query 'Vpcs[0].VpcId' --output text --no-cli-pager) |
| 152 | + echo "VPC: ${VPC_ID}" |
| 153 | + |
| 154 | + SUBNET_ID=$(aws ec2 describe-subnets \ |
| 155 | + --filters "Name=vpc-id,Values=${VPC_ID}" \ |
| 156 | + "Name=tag:aws-cdk:subnet-type,Values=Private" \ |
| 157 | + "Name=availability-zone-id,Values=use1-az1,use1-az2,use1-az4" \ |
| 158 | + --query 'Subnets[0].SubnetId' --output text --no-cli-pager) |
| 159 | + echo "Subnet: ${SUBNET_ID}" |
| 160 | + |
| 161 | + SG_ID=$(aws ec2 describe-security-groups \ |
| 162 | + --filters "Name=vpc-id,Values=${VPC_ID}" "Name=group-name,Values=default" \ |
| 163 | + --query 'SecurityGroups[0].GroupId' --output text --no-cli-pager) |
| 164 | + echo "Security Group: ${SG_ID}" |
| 165 | + |
| 166 | + for VAR in VPC_ID SUBNET_ID SG_ID; do |
| 167 | + sed -i.bak "/${VAR}=/d" ~/environment/.envrc 2>/dev/null || true |
| 168 | + eval "echo \"export ${VAR}=\${${VAR}}\"" >> ~/environment/.envrc |
| 169 | + done |
| 170 | +else |
| 171 | + echo "Using existing VPC configuration" |
| 172 | + echo "VPC: ${VPC_ID}" |
| 173 | + echo "Subnet: ${SUBNET_ID}" |
| 174 | + echo "Security Group: ${SG_ID}" |
| 175 | +fi |
| 176 | + |
| 177 | +## Creating the AgentCore Runtime |
| 178 | + |
| 179 | +echo "" |
| 180 | +echo "5. Create the AgentCore Runtime" |
| 181 | + |
| 182 | +# JWT authorizer for main agent (has UI), IAM-only for sub-agents |
| 183 | +if [ "${AGENT_NAME}" = "aiagent" ]; then |
| 184 | + AUTH_ARGS="--authorizer-configuration {\"customJWTAuthorizer\":{\"discoveryUrl\":\"${AIAGENT_DISCOVERY_URL}\",\"allowedClients\":[\"${AIAGENT_CLIENT_ID}\"]}} --request-header-configuration {\"requestHeaderAllowlist\":[\"Authorization\"]}" |
| 185 | +else |
| 186 | + AUTH_ARGS="" |
| 187 | +fi |
| 188 | + |
| 189 | +EXISTING_RUNTIME_ID=$(aws bedrock-agentcore-control list-agent-runtimes \ |
| 190 | + --region ${AWS_REGION} --no-cli-pager \ |
| 191 | + --query "agentRuntimes[?agentRuntimeName=='${RUNTIME_NAME}'].agentRuntimeId | [0]" --output text 2>/dev/null || echo "None") |
| 192 | + |
| 193 | +if [ "${EXISTING_RUNTIME_ID}" != "None" ] && [ -n "${EXISTING_RUNTIME_ID}" ]; then |
| 194 | + echo "AgentCore Runtime already exists: ${EXISTING_RUNTIME_ID}" |
| 195 | + echo "Updating runtime with latest configuration..." |
| 196 | + AGENT_RUNTIME_ID="${EXISTING_RUNTIME_ID}" |
| 197 | + |
| 198 | + aws bedrock-agentcore-control update-agent-runtime \ |
| 199 | + --agent-runtime-id "${AGENT_RUNTIME_ID}" \ |
| 200 | + --role-arn "arn:aws:iam::${ACCOUNT_ID}:role/aiagent-runtime-role" \ |
| 201 | + --agent-runtime-artifact "{\"containerConfiguration\":{\"containerUri\":\"${ECR_URI}:latest\"}}" \ |
| 202 | + --network-configuration "{\"networkMode\":\"VPC\",\"networkModeConfig\":{\"subnets\":[\"${SUBNET_ID}\"],\"securityGroups\":[\"${SG_ID}\"]}}" \ |
| 203 | + ${AUTH_ARGS} \ |
| 204 | + --region ${AWS_REGION} \ |
| 205 | + --no-cli-pager |
| 206 | + |
| 207 | + echo -n "Waiting for runtime" |
| 208 | + while [ "$(aws bedrock-agentcore-control get-agent-runtime \ |
| 209 | + --agent-runtime-id "${AGENT_RUNTIME_ID}" --region ${AWS_REGION} \ |
| 210 | + --no-cli-pager --query 'status' --output text)" != "READY" ]; do |
| 211 | + echo -n "."; sleep 5 |
| 212 | + done && echo " READY" |
| 213 | +else |
| 214 | + echo "Creating AgentCore Runtime: ${RUNTIME_NAME}" |
| 215 | + |
| 216 | + AGENT_RUNTIME_ID=$(aws bedrock-agentcore-control create-agent-runtime \ |
| 217 | + --agent-runtime-name "${RUNTIME_NAME}" \ |
| 218 | + --role-arn "arn:aws:iam::${ACCOUNT_ID}:role/aiagent-runtime-role" \ |
| 219 | + --agent-runtime-artifact "{\"containerConfiguration\":{\"containerUri\":\"${ECR_URI}:latest\"}}" \ |
| 220 | + --network-configuration "{\"networkMode\":\"VPC\",\"networkModeConfig\":{\"subnets\":[\"${SUBNET_ID}\"],\"securityGroups\":[\"${SG_ID}\"]}}" \ |
| 221 | + ${AUTH_ARGS} \ |
| 222 | + --region ${AWS_REGION} \ |
| 223 | + --no-cli-pager \ |
| 224 | + --query 'agentRuntimeId' --output text) |
| 225 | + |
| 226 | + echo -n "Waiting for runtime" |
| 227 | + while [ "$(aws bedrock-agentcore-control get-agent-runtime \ |
| 228 | + --agent-runtime-id "${AGENT_RUNTIME_ID}" --region ${AWS_REGION} \ |
| 229 | + --no-cli-pager --query 'status' --output text)" != "READY" ]; do |
| 230 | + echo -n "."; sleep 5 |
| 231 | + done && echo " READY" |
| 232 | +fi |
| 233 | + |
| 234 | +# Save runtime ID to environment |
| 235 | +RUNTIME_ID_VAR="${VAR_PREFIX}_RUNTIME_ID" |
| 236 | +if ! grep -q "${RUNTIME_ID_VAR}=${AGENT_RUNTIME_ID}" ~/environment/.envrc 2>/dev/null; then |
| 237 | + sed -i.bak "/${RUNTIME_ID_VAR}=/d" ~/environment/.envrc 2>/dev/null || true |
| 238 | + echo "export ${RUNTIME_ID_VAR}=${AGENT_RUNTIME_ID}" >> ~/environment/.envrc |
| 239 | +fi |
| 240 | + |
| 241 | +## Saving endpoint |
| 242 | + |
| 243 | +echo "" |
| 244 | +echo "6. Save endpoint" |
| 245 | + |
| 246 | +RUNTIME_ARN="arn:aws:bedrock-agentcore:${AWS_REGION}:${ACCOUNT_ID}:runtime/${AGENT_RUNTIME_ID}" |
| 247 | +AGENT_ENDPOINT="https://bedrock-agentcore.${AWS_REGION}.amazonaws.com/runtimes/$(echo -n "${RUNTIME_ARN}" | jq -sRr @uri)/invocations?qualifier=DEFAULT" |
| 248 | + |
| 249 | +RUNTIME_ARN_VAR="${VAR_PREFIX}_RUNTIME_ARN" |
| 250 | +sed -i.bak "/${RUNTIME_ARN_VAR}=/d" ~/environment/.envrc 2>/dev/null || true |
| 251 | +echo "export ${RUNTIME_ARN_VAR}=${RUNTIME_ARN}" >> ~/environment/.envrc |
| 252 | + |
| 253 | +ENDPOINT_VAR="${VAR_PREFIX}_ENDPOINT" |
| 254 | +sed -i.bak "/${ENDPOINT_VAR}=/d" ~/environment/.envrc 2>/dev/null || true |
| 255 | +echo "export ${ENDPOINT_VAR}=${AGENT_ENDPOINT}" >> ~/environment/.envrc |
| 256 | + |
| 257 | +rm -f ~/environment/.envrc.bak |
| 258 | + |
| 259 | +echo "" |
| 260 | +echo "==============================================" |
| 261 | +echo "AI Agent Runtime deployment complete!" |
| 262 | +echo "==============================================" |
| 263 | +echo "" |
| 264 | +echo "Environment variables saved to ~/environment/.envrc:" |
| 265 | +echo " ${RUNTIME_ID_VAR}=${AGENT_RUNTIME_ID}" |
| 266 | +echo " ${RUNTIME_ARN_VAR}=${RUNTIME_ARN}" |
| 267 | +echo " ${ENDPOINT_VAR}=${AGENT_ENDPOINT}" |
0 commit comments