Skip to content

Enforce Amazon Bedrock Usage Through Tagged Application Inference Profiles Using SCPs

3 minute read
Content level: Advanced
0

This article provides two Service Control Policies (SCPs) that enforce tagged application inference profiles as the only permitted Bedrock invocation path, enabling organizations to achieve complete generative AI cost attribution across teams and projects.

Developers can still call foundation models directly or through system inference profiles, bypassing cost attribution entirely. The two SCPs below take a defense-in-depth approach to close those gaps across your AWS Organization.

The Problem

A common first attempt is to deny access to foundation model ARNs directly:

{
  "Sid": "DenyDirectModelAccess",
  "Effect": "Deny",
  "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
  "Resource": "arn:aws:bedrock:*::foundation-model/*"
}

This breaks application inference profiles too, because they invoke foundation models under the hood. The key is the bedrock:InferenceProfileArn condition key, which distinguishes how a foundation model is being called.

SCP 1: Require Application Inference Profiles

Three statements that close every bypass path:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDirectFoundationModelInvocation",
      "Effect": "Deny",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:Converse",
        "bedrock:ConverseStream"
      ],
      "Resource": "arn:aws:bedrock:*::foundation-model/*",
      "Condition": {
        "Null": {
          "bedrock:InferenceProfileArn": "true"
        }
      }
    },
    {
      "Sid": "DenyFoundationModelViaSystemProfile",
      "Effect": "Deny",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:Converse",
        "bedrock:ConverseStream"
      ],
      "Resource": "arn:aws:bedrock:*::foundation-model/*",
      "Condition": {
        "StringNotLike": {
          "bedrock:InferenceProfileArn": "arn:aws:bedrock:*:*:application-inference-profile/*"
        }
      }
    },
    {
      "Sid": "DenyInvokeOnSystemInferenceProfiles",
      "Effect": "Deny",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:Converse",
        "bedrock:ConverseStream"
      ],
      "Resource": "arn:aws:bedrock:*:*:inference-profile/*"
    }
  ]
}

How it works:

  • DenyDirectFoundationModelInvocation — Blocks calling a model with no inference profile at all (bedrock:InferenceProfileArn is null)
  • DenyFoundationModelViaSystemProfile — Blocks calling a model through a system (AWS-managed) inference profile (ARN doesn't match application-inference-profile/*)
  • DenyInvokeOnSystemInferenceProfiles — Blocks invoking system inference profile ARNs directly (system profiles use inference-profile/*, application profiles use application-inference-profile/*)

The only path left open: invoking a foundation model through an application inference profile.

SCP 2: Require Tags on Application Inference Profiles

Ensures every profile is created with required cost allocation tags and prevents their removal:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RequireCostCenterTag",
      "Effect": "Deny",
      "Action": "bedrock:CreateInferenceProfile",
      "Resource": "arn:aws:bedrock:*:*:application-inference-profile/*",
      "Condition": {
        "Null": {
          "aws:RequestTag/CostCenter": "true"
        }
      }
    },
    {
      "Sid": "RequireProjectTag",
      "Effect": "Deny",
      "Action": "bedrock:CreateInferenceProfile",
      "Resource": "arn:aws:bedrock:*:*:application-inference-profile/*",
      "Condition": {
        "Null": {
          "aws:RequestTag/Project": "true"
        }
      }
    },
    {
      "Sid": "RequireTeamTag",
      "Effect": "Deny",
      "Action": "bedrock:CreateInferenceProfile",
      "Resource": "arn:aws:bedrock:*:*:application-inference-profile/*",
      "Condition": {
        "Null": {
          "aws:RequestTag/Team": "true"
        }
      }
    },
    {
      "Sid": "RequireEnvironmentTag",
      "Effect": "Deny",
      "Action": "bedrock:CreateInferenceProfile",
      "Resource": "arn:aws:bedrock:*:*:application-inference-profile/*",
      "Condition": {
        "Null": {
          "aws:RequestTag/Environment": "true"
        }
      }
    },
    {
      "Sid": "PreventTagRemoval",
      "Effect": "Deny",
      "Action": "bedrock:UntagResource",
      "Resource": "arn:aws:bedrock:*:*:application-inference-profile/*",
      "Condition": {
        "ForAnyValue:StringEquals": {
          "aws:TagKeys": [
            "CostCenter",
            "Project",
            "Team",
            "Environment"
          ]
        }
      }
    }
  ]
}

Replace the four tag names (CostCenter, Project, Team, Environment) with your organization's cost allocation taxonomy.

Considerations

  • Create profiles before enforcing — Attach SCP 1 only after teams have created their application inference profiles. Otherwise, all Bedrock access is blocked immediately.
  • Bedrock Agents and Knowledge Bases — These invoke models internally. Test that they work through application inference profiles before enforcing in accounts that use them.
  • Cross-region inference — Application inference profiles support cross-region inference. If you also use region-deny SCPs, see Bedrock Inference Profile and Cross-Region Inference with Control Tower Restrictions.
  • SCP character limit — Each SCP has a 5,120 character limit. Both policies fit individually. If combining into one, consolidate the tag requirement statements.
  • New API actions — As Bedrock adds invocation APIs, update the Action lists in SCP 1 to maintain coverage.

Related Resources