AWS Public Sector Blog

Implementing per-user token guardrails for Amazon Bedrock in government agencies

Implementing per-user token guardrails for Amazon Bedrock in government agencies

A single developer experimenting with large context windows can consume thousands of dollars in Amazon Bedrock tokens within hours. For federal and state agencies accelerating generative AI adoption, per-user guardrails aren’t optional. They’re essential to maintain governance while enabling innovation at scale.

OMB M-25-21, Accelerating Federal Use of AI through Innovation, Governance, and Public Trust, directs agencies to accelerate AI adoption while maintaining appropriate governance and public trust. Innovation is the primary mandate, but agencies must pursue it responsibly.

Meanwhile, OMB M-22-09, Moving the U.S. Government Toward Zero Trust Cybersecurity Principles, demands that agencies verify every actor and every access attempt. Together, these mandates create a combined expectation: Agencies should maintain visibility and control over how AI resources are consumed not just at the organizational level, but per individual user so adoption can accelerate without uncontrolled risk.

Without per-user guardrails, agencies face two compounding risks: unpredictable cloud spend from unconstrained token consumption, and governance gaps that fail audit requirements. A single automated pipeline with a runaway loop can exhaust thousands of dollars in tokens within hours.

This post presents two complementary patterns for implementing per-user token guardrails on Amazon Bedrock from Amazon Web Services (AWS):

  1. Pattern 1: Claude Code (CLI/developer access) – Using Amazon CloudWatch logging, AWS Lambda, and Amazon DynamoDB to monitor and enforce limits on developer tool usage
  2. Pattern 2: Application-level access – Using a centralized large language model (LLM) invocation class to track and enforce limits within custom applications

Both patterns share a common data model: a default token limit per model applied to all users, with per-user overrides for individuals requiring higher allocations. When a user reaches their daily limit, the system pauses access until the next calendar day.

Architecture overview

Both patterns converge on the same core design principles:

  1. Default limits per model – Every model has a baseline daily token allocation that applies to all users
  2. Per-user overrides – Specific users who require additional capacity receive a higher threshold
  3. Daily enforcement – After a user exhausts their allocation, the system blocks access until the next day
  4. Alerting – Notifications at 80% and 100% thresholds warn users and administrators
  5. Auditability – Every invocation is logged with user identity, model, and token counts

Default and override limit strategy

The default limit per model establishes a baseline allocation that applies across the organization. This approach gives every user immediate access to AI capabilities on the first day without requiring individual provisioning. Agencies should set these defaults conservatively enough for typical daily workflows but low enough to prevent runaway consumption.

Per-user overrides accommodate the reality that different roles have different needs. A data scientist building training pipelines consumes far more tokens than an analyst running occasional summaries. The override mechanism means administrators can grant higher allocations to specific users or service accounts without raising the ceiling for everyone.

When determining initial limits, consider the following factors:

  1. Model pricing tiers – Claude Sonnet tokens cost more than Claude Haiku; set lower token limits for expensive models
  2. Role-based heuristics – Developers using Claude Code typically consume 50,000–200,000 tokens per day; application end users typically consume 5,000–20,000
  3. Budget reverse-engineering – Divide your monthly AI budget by working days and active users to establish a sustainable per-user ceiling

Shared Amazon DynamoDB table design

The following table schema supports both patterns. The UserLimits table stores per-user override allocations, the DefaultLimits table stores organization-wide baselines per model, and the UsageLog table tracks daily consumption:

Table Partition key Sort key Attributes
UserLimits USER#<userId> MODEL#<modelId> dailyTokenLimit, isBlocked, lastReset
DefaultLimits DEFAULT MODEL#<modelId> dailyTokenLimit
UsageLog USER#<userId> DATE#<date>#MODEL#<modelId> inputTokens, outputTokens, totalTokens

Pattern 1: Claude Code guardrails

When developers use Claude Code (Anthropic’s CLI-based coding assistant, accessed through Amazon Bedrock), Amazon CloudWatch captures invocation logs. This pattern uses an Amazon CloudWatch subscription filter to trigger an AWS Lambda function that tracks usage and enforces limits in nearly real time.

How it works

Prerequisites

You must enable Amazon Bedrock model invocation logging (it’s disabled by default). After it’s enabled, each log entry automatically includes the identity.arn field (the IAM principal that made the request) alongside input.inputTokenCount and output.outputTokenCount. No custom instrumentation is required to attribute token usage to individual users.

  1. Amazon Bedrock model invocation logging sends events to Amazon CloudWatch Logs
  2. An Amazon CloudWatch subscription filter triggers a Lambda function on each invocation event
  3. The Lambda function parses the log event to extract the user identity (IAM principal), model ID, and token counts (input + output)
  4. Lambda queries Amazon DynamoDB for the user’s current daily usage and their limit (user-specific override or model default)
  5. If the cumulative usage exceeds the limit, the function triggers enforcement
  6. A scheduled daily Lambda function (or DynamoDB TTL) resets usage counters at midnight UTC

Because model invocation logs are delivered to Amazon CloudWatch asynchronously (typically within seconds), this pattern provides close to real-time enforcement. A user might complete one or two additional invocations between exceeding their limit and the deny policy taking effect. For applications requiring strictly synchronous enforcement, see Pattern 2, which checks limits inline before each Amazon Bedrock call.

Enforcement option A: IAM deny policy (hard block)

The Lambda function attaches an explicit deny policy to the user’s IAM role or user, preventing any further Amazon Bedrock InvokeModel calls. This is the strongest enforcement mechanism: The user receives an AccessDenied error on their next attempt.

Node.js Lambda handler (Option A)

const { DynamoDBClient, GetItemCommand, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
const { IAMClient, PutUserPolicyCommand, DeleteUserPolicyCommand } = require('@aws-sdk/client-iam');
const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');

const ddb = new DynamoDBClient({});
const iam = new IAMClient({});
const sns = new SNSClient({});

exports.handler = async (event) => {
  const logData = parseCloudWatchEvent(event);
  const { userId, modelId, inputTokens, outputTokens } = logData;
  const totalTokens = inputTokens + outputTokens;
  const today = new Date().toISOString().split('T')[0];

  // Get user limit (override or default)
  const limit = await getUserLimit(userId, modelId);

  // Increment daily usage
  const newTotal = await incrementUsage(userId, modelId, today, totalTokens);

  // Check thresholds
  if (newTotal >= limit) {
    await blockUser(userId);
    await notify(userId, modelId, 'BLOCKED', newTotal, limit);
  } else if (newTotal >= limit * 0.8) {
    await notify(userId, modelId, 'WARNING_80PCT', newTotal, limit);
  }
};

async function getUserLimit(userId, modelId) {
  // Check for user-specific override first
  const override = await ddb.send(new GetItemCommand({
    TableName: 'UserLimits',
    Key: { PK: { S: `USER#${userId}` }, SK: { S: `MODEL#${modelId}` } }
  }));
  if (override.Item?.dailyTokenLimit) return Number(override.Item.dailyTokenLimit.N);

  // Fall back to default limit for this model
  const defaults = await ddb.send(new GetItemCommand({
    TableName: 'UserLimits',
    Key: { PK: { S: 'DEFAULT' }, SK: { S: `MODEL#${modelId}` } }
  }));
  return Number(defaults.Item?.dailyTokenLimit?.N || 100000);
}

async function blockUser(userId) {
  const denyPolicy = JSON.stringify({
    Version: '2012-10-17',
    Statement: [{ Effect: 'Deny', Action: 'bedrock:InvokeModel*', Resource: '*' }]
  });
  await iam.send(new PutUserPolicyCommand({
    UserName: userId,
    PolicyName: 'BedrockTokenLimitDeny',
    PolicyDocument: denyPolicy
  }));
}

.NET Lambda handler (Option A)

public class TokenGuardrailFunction
{
    private readonly IAmazonDynamoDB _dynamoDb;
    private readonly IAmazonIdentityManagementService _iam;
    private readonly IAmazonSimpleNotificationService _sns;

    public async Task FunctionHandler(CloudWatchLogsEvent input, ILambdaContext ctx)
    {
        var logData = ParseCloudWatchEvent(input);
        var (userId, modelId, inputTokens, outputTokens) = logData;
        var totalTokens = inputTokens + outputTokens;
        var today = DateTime.UtcNow.ToString("yyyy-MM-dd");

        var limit = await GetUserLimit(userId, modelId);
        var newTotal = await IncrementUsage(userId, modelId, today, totalTokens);

        if (newTotal >= limit)
        {
            await BlockUser(userId);
            await Notify(userId, modelId, "BLOCKED", newTotal, limit);
        }
        else if (newTotal >= limit * 0.8)
            await Notify(userId, modelId, "WARNING_80PCT", newTotal, limit);
    }

    private async Task<long> GetUserLimit(string userId, string modelId)
    {
        var overrideReq = new GetItemRequest {
            TableName = "UserLimits",
            Key = new Dictionary<string, AttributeValue> {
                ["PK"] = new AttributeValue { S = $"USER#{userId}" },
                ["SK"] = new AttributeValue { S = $"MODEL#{modelId}" }
            }
        };
        var result = await _dynamoDb.GetItemAsync(overrideReq);
        if (result.Item.ContainsKey("dailyTokenLimit"))
            return long.Parse(result.Item["dailyTokenLimit"].N);

        // Fall back to default
        overrideReq.Key["PK"] = new AttributeValue { S = "DEFAULT" };
        var defaults = await _dynamoDb.GetItemAsync(overrideReq);
        return long.Parse(defaults.Item["dailyTokenLimit"].N);
    }

    private async Task BlockUser(string userId)
    {
        var denyPolicy = @"{ ""Version"":""2012-10-17"",
            ""Statement"":[{""Effect"":""Deny"",""Action"":""bedrock:InvokeModel*"",""Resource"":""*""}] }";
        await _iam.PutUserPolicyAsync(new PutUserPolicyRequest {
            UserName = userId,
            PolicyName = "BedrockTokenLimitDeny",
            PolicyDocument = denyPolicy
        });
    }
}

Enforcement option B: Gateway check (soft block)

For agencies preferring a less disruptive approach, the Lambda function sets a “blocked” flag in Amazon DynamoDB. A custom Amazon API Gateway authorizer or proxy layer checks this flag before forwarding requests to Amazon Bedrock. This provides a friendlier user experience with custom error messages and is more readily reversible.

Node.js gateway authorizer

const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb');
const ddb = new DynamoDBClient({});

exports.handler = async (event) => {
  const userId = event.requestContext.authorizer.claims.sub;
  const modelId = event.pathParameters.modelId;

  const result = await ddb.send(new GetItemCommand({
    TableName: 'UserLimits',
    Key: { PK: { S: `USER#${userId}` }, SK: { S: `MODEL#${modelId}` } }
  }));

  if (result.Item?.isBlocked?.BOOL) {
    return {
      statusCode: 429,
      body: JSON.stringify({
        error: 'TokenLimitExceeded',
        message: 'Daily token limit reached. Access resumes tomorrow.',
        resetTime: getNextMidnightUTC()
      })
    };
  }
  return { statusCode: 200, body: 'authorized' };
};

Daily reset function

A scheduled Lambda function (triggered using Amazon EventBridge at 00:00 UTC daily) resets blocked users:

exports.resetHandler = async () => {
  const blocked = await scanBlockedUsers();
  for (const user of blocked) {
    await iam.send(new DeleteUserPolicyCommand({
      UserName: user.userId, PolicyName: 'BedrockTokenLimitDeny'
    }));
    await resetUsageCounters(user.userId);
  }
  console.log(`Reset ${blocked.length} users`);
};

Comparing enforcement options

Both enforcement approaches are valid. The right choice depends on your agency’s operational requirements, risk tolerance, and user experience priorities.

Criteria Option A: IAM deny policy Option B: Gateway check
Enforcement strength Hard block. AWS-level denial, can’t be bypassed by application code Soft block. Enforced at the proxy layer; direct SDK calls bypass it
User experience Generic AccessDenied error; requires user to understand IAM Custom error message with reset time and remaining context
Reversibility Requires IAM policy deletion. Slightly slower to unblock Flip a DynamoDB flag. Instant unblock by admin
Latency impact None. Enforcement is async after invocation Adds approximately 10–50ms per request for the DynamoDB lookup
IAM permissions required Lambda needs iam:PutUserPolicy and iam:DeleteUserPolicy Lambda only needs dynamodb:UpdateItem
Coverage scope Blocks all Amazon Bedrock access (CLI, SDK, console) Only blocks requests routed through the gateway
Race condition risk User might complete one to two additional calls before deny propagates Minimal. Check happens synchronously before each call
Best suited for High-security environments; compliance-driven agencies; Claude Code CLI users User-facing applications; environments needing graceful degradation

For Claude Code (CLI) usage, option A provides more rigorous enforcement because developers have direct SDK access that could bypass a gateway. For application-level access (Pattern 2), a gateway check or in-application enforcement is typically sufficient and delivers a better end-user experience. Many agencies implement both: Option A as a backstop for developer tools and option B for user-facing applications.

Pattern 2: Application-level guardrails

For custom applications built on Amazon Bedrock (chatbots, document processors, code assistants), you implement enforcement within the application’s LLM service layer. A centralized invocation class wraps all Amazon Bedrock calls, performing pre-invocation limit checks and post-invocation usage recording.

How it works

The following steps describe the request lifecycle from user authentication through quota enforcement:

  1. Application users authenticate using Amazon Cognito or your agency identity provider (IdP)
  2. All Amazon Bedrock calls route through a centralized service class (never direct SDK calls from controllers)
  3. Before each invocation, the service checks the user’s daily usage against their limit
  4. If under the limit, the service invokes Amazon Bedrock and captures token counts from the response metadata
  5. After invocation, the service records the interaction (user, model, tokens, timestamp) in the database
  6. If over the limit, the service returns a structured quota-exceeded response to the frontend

Node.js — BedrockGuardrailService class

const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');
const { DynamoDBClient, GetItemCommand, UpdateItemCommand } = require('@aws-sdk/client-dynamodb');
const { SNSClient, PublishCommand } = require('@aws-sdk/client-sns');

class BedrockGuardrailService {
  constructor() {
    this.bedrock = new BedrockRuntimeClient({});
    this.ddb = new DynamoDBClient({});
    this.sns = new SNSClient({});
  }

  async invoke(userId, modelId, payload) {
    const today = new Date().toISOString().split('T')[0];

    // Pre-invocation: check current usage against limit
    const { currentUsage, limit } = await this.getUsageAndLimit(userId, modelId, today);
    if (currentUsage >= limit) {
      return {
        success: false, error: 'TOKEN_LIMIT_EXCEEDED',
        message: 'Daily token limit reached. Access will resume tomorrow.',
        currentUsage, limit
      };
    }

    // Invoke Amazon Bedrock
    const response = await this.bedrock.send(new InvokeModelCommand({
      modelId, body: JSON.stringify(payload), contentType: 'application/json'
    }));
    const result = JSON.parse(new TextDecoder().decode(response.body));

    // Post-invocation: record usage
    const tokensUsed = result.usage.input_tokens + result.usage.output_tokens;
    const newTotal = await this.recordUsage(userId, modelId, today, tokensUsed);

    // Check alert thresholds
    if (newTotal >= limit * 0.8 && currentUsage < limit * 0.8) {
      await this.sendAlert(userId, modelId, newTotal, limit, 'WARNING_80PCT');
    }

    return { success: true, result, tokensUsed, remainingTokens: limit - newTotal };
  }

  async getUsageAndLimit(userId, modelId, date) {
    const [usage, userLimit, defaultLimit] = await Promise.all([
      this.ddb.send(new GetItemCommand({
        TableName: 'UsageLog',
        Key: { PK: { S: `USER#${userId}` }, SK: { S: `DATE#${date}#MODEL#${modelId}` } }
      })),
      this.ddb.send(new GetItemCommand({
        TableName: 'UserLimits',
        Key: { PK: { S: `USER#${userId}` }, SK: { S: `MODEL#${modelId}` } }
      })),
      this.ddb.send(new GetItemCommand({
        TableName: 'UserLimits',
        Key: { PK: { S: 'DEFAULT' }, SK: { S: `MODEL#${modelId}` } }
      }))
    ]);
    return {
      currentUsage: Number(usage.Item?.totalTokens?.N || 0),
      limit: Number(userLimit.Item?.dailyTokenLimit?.N || defaultLimit.Item?.dailyTokenLimit?.N || 100000)
    };
  }

  async recordUsage(userId, modelId, date, tokens) {
    const result = await this.ddb.send(new UpdateItemCommand({
      TableName: 'UsageLog',
      Key: { PK: { S: `USER#${userId}` }, SK: { S: `DATE#${date}#MODEL#${modelId}` } },
      UpdateExpression: 'ADD totalTokens :t',
      ExpressionAttributeValues: { ':t': { N: String(tokens) } },
      ReturnValues: 'ALL_NEW'
    }));
    return Number(result.Attributes.totalTokens.N);
  }
}

module.exports = { BedrockGuardrailService };

.NET — BedrockGuardrailClient class

public class BedrockGuardrailClient
{
    private readonly IAmazonBedrockRuntime _bedrock;
    private readonly IAmazonDynamoDB _dynamoDb;
    private readonly IAmazonSimpleNotificationService _sns;
    private readonly ILogger<BedrockGuardrailClient> _logger;

    public BedrockGuardrailClient(
        IAmazonBedrockRuntime bedrock,
        IAmazonDynamoDB dynamoDb,
        IAmazonSimpleNotificationService sns,
        ILogger<BedrockGuardrailClient> logger)
    {
        _bedrock = bedrock; _dynamoDb = dynamoDb;
        _sns = sns; _logger = logger;
    }

    public async Task<InvokeResult> InvokeWithGuardrails(
        string userId, string modelId, string payload)
    {
        var today = DateTime.UtcNow.ToString("yyyy-MM-dd");
        var (currentUsage, limit) = await GetUsageAndLimit(userId, modelId, today);

        if (currentUsage >= limit)
            return InvokeResult.LimitExceeded(currentUsage, limit);

        var response = await _bedrock.InvokeModelAsync(new InvokeModelRequest
        {
            ModelId = modelId,
            Body = new MemoryStream(Encoding.UTF8.GetBytes(payload)),
            ContentType = "application/json"
        });

        var result = JsonSerializer.Deserialize<BedrockResponse>(response.Body);
        var tokensUsed = result.Usage.InputTokens + result.Usage.OutputTokens;
        var newTotal = await RecordUsage(userId, modelId, today, tokensUsed);

        if (newTotal >= limit * 0.8 && currentUsage < limit * 0.8)
            await SendAlert(userId, modelId, newTotal, limit);

        return InvokeResult.Success(result, tokensUsed, limit - newTotal);
    }
}

.NET dependency injection registration

// Program.cs or Startup.cs
builder.Services.AddSingleton<IAmazonBedrockRuntime>(new AmazonBedrockRuntimeClient());
builder.Services.AddSingleton<IAmazonDynamoDB>(new AmazonDynamoDBClient());
builder.Services.AddSingleton<IAmazonSimpleNotificationService>(
    new AmazonSimpleNotificationServiceClient());
builder.Services.AddSingleton<BedrockGuardrailClient>();

Usage alerts and notifications

Both patterns include a notification layer that alerts users and administrators when usage thresholds are approached or exceeded. Amazon Simple Notification Service (Amazon SNS) delivers alerts to email, SMS, or agency messaging platforms.

Alert thresholds

The system supports configurable alert levels that trigger notifications at key consumption milestones:

  1. 80% threshold – Warning notification sent to the user: “You have consumed 80% of your daily token allocation for [model]. Consider conserving usage for critical tasks.”
  2. 100% threshold – Block notification sent to the user and their administrator: “Daily token limit reached. Access to [model] is paused until [reset time].”
  3. Custom thresholds – Agencies can define additional intermediate alerts (for example, 50% for high-visibility projects)

Node.js alert function

async function sendAlert(userId, modelId, currentUsage, limit, alertType) {
  const percentage = Math.round((currentUsage / limit) * 100);
  const message = alertType === 'BLOCKED'
    ? `User ${userId} has reached their daily limit for ${modelId}. Access paused.`
    : `User ${userId} has consumed ${percentage}% of their daily limit for ${modelId}.`;

  await sns.send(new PublishCommand({
    TopicArn: process.env.ALERT_TOPIC_ARN,
    Subject: `Bedrock Token Alert: ${alertType}`,
    Message: JSON.stringify({ userId, modelId, currentUsage, limit, percentage, alertType }),
    MessageAttributes: {
      alertType: { DataType: 'String', StringValue: alertType },
      userId: { DataType: 'String', StringValue: userId }
    }
  }));
}

Admin visibility and dashboard

Governance requires not just enforcement but transparency. Administrators need a real-time view of token consumption across their agency by user, model, and time period.

Key dashboard views

A governance dashboard should provide the following views to support cost management and usage oversight:

  1. Agency-wide daily consumption – Total tokens consumed across all models, trended over time
  2. Per-user leaderboard – Top consumers ranked by daily, weekly, and monthly usage
  3. Model breakdown – Usage distribution across Claude Sonnet, Claude Haiku, and other models
  4. Limit utilization – How close each user is to their daily cap (highlighting those at 80% or above)
  5. Cost projection – Estimated monthly cost based on current consumption patterns

Amazon DynamoDB query patterns

The UsageLog table supports efficient queries for dashboard data:

// Query all usage for a specific date (using a GSI on date)
const params = {
  TableName: 'UsageLog',
  IndexName: 'DateIndex',
  KeyConditionExpression: '#d = :date',
  ExpressionAttributeNames: { '#d': 'usageDate' },
  ExpressionAttributeValues: { ':date': { S: '2026-06-19' } } // Example date
};

// Query a specific user's usage history
const userHistory = {
  TableName: 'UsageLog',
  KeyConditionExpression: 'PK = :pk AND begins_with(SK, :prefix)',
  ExpressionAttributeValues: {
    ':pk': { S: 'USER#jsmith' },
    ':prefix': { S: 'DATE#2026-06' }  // All of June 2026
  }
};

For production dashboards, consider streaming Amazon DynamoDB data to Amazon QuickSight using Amazon DynamoDB Streams and Amazon Data Firehose, or using Amazon Athena to query exported data in Amazon Simple Storage Service (Amazon S3).

Security considerations

When implementing token guardrails, agencies should address the following security dimensions to maintain alignment with their authorization to operate and Federal Risk and Authorization Management Program (FedRAMP) requirements:

  1. Least privilege – The Lambda enforcement function requires IAM permissions to attach deny policies and write to Amazon DynamoDB, but shouldn’t have broader administrative access. Scope its execution role tightly to the specific resources it manages.
  2. Tamper resistance – Users shouldn’t be able to modify their own limit records in Amazon DynamoDB. Use IAM resource-based policies to restrict write access to the limits table exclusively to the enforcement Lambda function and authorized administrators.
  3. Audit trail – Enable Amazon DynamoDB Streams on the UserLimits table to capture all modifications to limit values. Stream these changes to Amazon CloudWatch Logs or AWS CloudTrail Lake for compliance auditing.
  4. Encryption – Enable encryption at rest on all Amazon DynamoDB tables using AWS Key Management Service (AWS KMS) customer-managed keys, and enforce TLS 1.2+ for all API communications.
  5. Multi-account strategy – In organizations using AWS Organizations with multiple accounts, deploy the enforcement infrastructure in a centralized security account and use cross-account IAM roles to apply deny policies in workload accounts.

Conclusion

Implementing per-user token guardrails for Amazon Bedrock addresses both the governance mandates from OMB and the practical need for predictable AI spend. The two patterns presented here cover the full spectrum of agency AI usage:

  1. Pattern 1 (Claude Code) – Monitors developer CLI usage using Amazon CloudWatch and enforces limits through IAM deny policies or gateway checks
  2. Pattern 2 (Application-level) – Embeds enforcement directly in the application’s LLM service layer, providing immediate feedback to end users

Both approaches share a common Amazon DynamoDB data model with default limits per model and per-user overrides, making administration straightforward. Combined with proactive alerting and administrative dashboards, agencies gain complete visibility and control over their generative AI consumption.

By implementing these guardrails, agencies demonstrate compliance with OMB AI governance requirements while making certain that AI adoption remains sustainable and fiscally responsible. The patterns are modular: Start with one, add the other as your AI footprint grows.

Phillip Spies

Phillip Spies

Phillip Spies is a senior solutions architect on the AWS Federal Civilian team with 20 years of development and solution architecture experience. He specializes in cloud infrastructure design, container technologies, and generative AI adoption, holding multiple AWS certifications. Phillip builds production-grade generative AI prototypes with government agencies, helping organizations accelerate from concept to deployed capability through cloud migrations and application modernization.