Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,33 @@ No modules.
| Name | Type |
| ---- | ---- |
| [aws_iam_access_key.admin_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
| [aws_iam_access_key.bedrock_opencode](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
| [aws_iam_access_key.sops_secrets_operator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
| [aws_iam_openid_connect_provider.github_actions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_openid_connect_provider) | resource |
| [aws_iam_role.bedrock_batch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.github_actions_sops_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.bedrock_batch_invoke](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy.bedrock_batch_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy.github_actions_sops_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_user.admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource |
| [aws_iam_user.bedrock_opencode](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource |
| [aws_iam_user.sops_secrets_operator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource |
| [aws_iam_user_policy.bedrock_opencode](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy) | resource |
| [aws_iam_user_policy.sops_secrets_operator_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy) | resource |
| [aws_iam_user_policy_attachment.admin_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy_attachment) | resource |
| [aws_kms_alias.sops](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_key.sops](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_s3_bucket.bedrock_batch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket.web](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_policy.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_policy.web](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.bedrock_batch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_public_access_block.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_public_access_block.web](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_website_configuration.web](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [sops_file.secret_vars](https://registry.terraform.io/providers/carlpett/sops/latest/docs/data-sources/file) | data source |

## Inputs
Expand All @@ -51,6 +60,10 @@ No inputs.
| Name | Description |
| ---- | ----------- |
| <a name="output_admin_access_keys"></a> [admin\_access\_keys](#output\_admin\_access\_keys) | Admin IAM user access keys |
| <a name="output_bedrock_batch_bucket_name"></a> [bedrock\_batch\_bucket\_name](#output\_bedrock\_batch\_bucket\_name) | S3 bucket for Bedrock batch inference input and output data |
| <a name="output_bedrock_batch_service_role_arn"></a> [bedrock\_batch\_service\_role\_arn](#output\_bedrock\_batch\_service\_role\_arn) | Service role ARN to pass as roleArn when creating Bedrock batch inference jobs |
| <a name="output_bedrock_opencode_access_key"></a> [bedrock\_opencode\_access\_key](#output\_bedrock\_opencode\_access\_key) | Access key for OpenCode to invoke Anthropic models via AWS Bedrock |
| <a name="output_bedrock_opencode_iam_user_arn"></a> [bedrock\_opencode\_iam\_user\_arn](#output\_bedrock\_opencode\_iam\_user\_arn) | IAM user ARN for OpenCode Bedrock access |
| <a name="output_github_actions_sops_kms_role_arn"></a> [github\_actions\_sops\_kms\_role\_arn](#output\_github\_actions\_sops\_kms\_role\_arn) | IAM role ARN for GitHub Actions SOPS KMS access |
| <a name="output_sops_kms_key_arn"></a> [sops\_kms\_key\_arn](#output\_sops\_kms\_key\_arn) | KMS key ARN for future SOPS AWS KMS recipients |
| <a name="output_sops_secrets_operator_access_key"></a> [sops\_secrets\_operator\_access\_key](#output\_sops\_secrets\_operator\_access\_key) | Access key for the k3s sops-secrets-operator to decrypt SOPS AWS KMS secrets |
Expand Down
196 changes: 196 additions & 0 deletions aws-bedrock.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
data "aws_caller_identity" "current" {}

# Batch inference (50% discount vs on-demand) requires a dedicated service
# role: CreateModelInvocationJob takes a roleArn that Bedrock assumes for S3
# input/output access. The opencode user below gets iam:PassRole on it.
resource "aws_s3_bucket" "bedrock_batch" {
bucket = "mitw-bedrock-batch"

tags = {
ManagedBy = "Terraform"
Purpose = "bedrock-batch-inference"
}

lifecycle {
prevent_destroy = true
}
}

resource "aws_s3_bucket_public_access_block" "bedrock_batch" {
bucket = aws_s3_bucket.bedrock_batch.bucket
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_iam_role" "bedrock_batch" {
name = "bedrock-batch-inference"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "bedrock.amazonaws.com"
}
Action = "sts:AssumeRole"
Condition = {
StringEquals = {
"aws:SourceAccount" = data.aws_caller_identity.current.account_id
}
ArnEquals = {
"aws:SourceArn" = "arn:aws:bedrock:*:${data.aws_caller_identity.current.account_id}:model-invocation-job/*"
}
}
}
]
})

tags = {
ManagedBy = "Terraform"
Purpose = "bedrock-batch-inference"
}
}

resource "aws_iam_role_policy" "bedrock_batch_s3" {
name = "bedrock-batch-s3"
role = aws_iam_role.bedrock_batch.name

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "S3Access"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
]
Resource = [
aws_s3_bucket.bedrock_batch.arn,
"${aws_s3_bucket.bedrock_batch.arn}/*"
]
Condition = {
StringEquals = {
"aws:ResourceAccount" = data.aws_caller_identity.current.account_id
}
}
}
]
})
}

# Newer Anthropic models (Opus 4.x) reject base model IDs and must be called
# through inference profiles, so both resource types are granted everywhere.
resource "aws_iam_role_policy" "bedrock_batch_invoke" {
name = "bedrock-batch-invoke"
role = aws_iam_role.bedrock_batch.name

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "InvokeAnthropicModels"
Effect = "Allow"
Action = [
"bedrock:InvokeModel"
]
Resource = [
"arn:aws:bedrock:*::foundation-model/anthropic.*",
"arn:aws:bedrock:*:${data.aws_caller_identity.current.account_id}:inference-profile/*.anthropic.*"
]
}
]
})
}

resource "aws_iam_user" "bedrock_opencode" {
name = "opencode-bedrock"
force_destroy = false

tags = {
ManagedBy = "Terraform"
Purpose = "opencode-bedrock"
}
}

resource "aws_iam_user_policy" "bedrock_opencode" {
name = "bedrock-invoke"
user = aws_iam_user.bedrock_opencode.name

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "InvokeAnthropicModels"
Effect = "Allow"
Action = [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
]
Resource = [
"arn:aws:bedrock:*::foundation-model/anthropic.*",
"arn:aws:bedrock:*:${data.aws_caller_identity.current.account_id}:inference-profile/*.anthropic.*"
]
},
{
Sid = "DiscoverInferenceProfiles"
Effect = "Allow"
Action = ["bedrock:ListInferenceProfiles"]
Resource = "*"
},
{
Sid = "ManageBatchJobs"
Effect = "Allow"
Action = [
"bedrock:CreateModelInvocationJob",
"bedrock:GetModelInvocationJob",
"bedrock:StopModelInvocationJob",
"bedrock:TagResource"
]
Resource = [
"arn:aws:bedrock:*::foundation-model/anthropic.*",
"arn:aws:bedrock:*:${data.aws_caller_identity.current.account_id}:inference-profile/*.anthropic.*",
"arn:aws:bedrock:*:${data.aws_caller_identity.current.account_id}:model-invocation-job/*"
]
},
{
Sid = "ListBatchJobs"
Effect = "Allow"
Action = ["bedrock:ListModelInvocationJobs"]
Resource = "*"
},
{
Sid = "BatchDataS3Access"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
]
Resource = [
aws_s3_bucket.bedrock_batch.arn,
"${aws_s3_bucket.bedrock_batch.arn}/*"
]
},
{
Sid = "PassBatchServiceRole"
Effect = "Allow"
Action = ["iam:PassRole"]
Resource = aws_iam_role.bedrock_batch.arn
Condition = {
StringEquals = {
"iam:PassedToService" = "bedrock.amazonaws.com"
}
}
}
]
})
}

resource "aws_iam_access_key" "bedrock_opencode" {
user = aws_iam_user.bedrock_opencode.name
}
24 changes: 24 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,27 @@ output "sops_secrets_operator_iam_user_arn" {
description = "IAM user ARN for the k3s sops-secrets-operator"
value = aws_iam_user.sops_secrets_operator.arn
}

output "bedrock_opencode_access_key" {
description = "Access key for OpenCode to invoke Anthropic models via AWS Bedrock"
value = {
access_key_id = aws_iam_access_key.bedrock_opencode.id
secret_access_key = aws_iam_access_key.bedrock_opencode.secret
}
sensitive = true
}

output "bedrock_opencode_iam_user_arn" {
description = "IAM user ARN for OpenCode Bedrock access"
value = aws_iam_user.bedrock_opencode.arn
}

output "bedrock_batch_service_role_arn" {
description = "Service role ARN to pass as roleArn when creating Bedrock batch inference jobs"
value = aws_iam_role.bedrock_batch.arn
}

output "bedrock_batch_bucket_name" {
description = "S3 bucket for Bedrock batch inference input and output data"
value = aws_s3_bucket.bedrock_batch.bucket
}
Loading