resource "aws_iam_user" "langsmith_export" { name = local.export_user_name tags = { Name = "sh-openswe-langsmith-export" purpose = "langsmith-bulk-export-writer" } } resource "aws_iam_user_policy" "langsmith_export_put" { name = "langsmith-export-put" user = aws_iam_user.langsmith_export.name # Bucket-wide PutObject (not prefix-scoped): LangSmith destination validation # writes a test object whose key is not guaranteed under export_prefix. # Deliberately NO s3:GetObject / s3:DeleteObject — write-only. policy = jsonencode({ Version = "2012-10-17" Statement = [ { Sid = "PutExportObjects" Effect = "Allow" Action = [ "s3:PutObject", "s3:AbortMultipartUpload", ] Resource = "${aws_s3_bucket.traces.arn}/*" }, { Sid = "EncryptWithBucketKey" Effect = "Allow" Action = [ "kms:GenerateDataKey", "kms:Encrypt", # Required by S3 at CompleteMultipartUpload for SSE-KMS. Safe: the # writer has no s3:GetObject, so there is no object to decrypt/exfil. "kms:Decrypt", ] Resource = aws_kms_key.traces.arn Condition = { StringEquals = { "kms:ViaService" = "s3.${var.aws_region}.amazonaws.com" } } } ] }) }