aws / aws/aws-cdk

codebuild: Race condition between CfnProject and IAM Policy for CODECONNECTIONS source authentication causes OAuthProviderException

Open
#38,504 1 comment 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-codebuild bug effort/medium p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the bug

When creating a new `aws_codebuild.Project` with `Source.gitHub(...)` configured to use **AWS CodeConnections** (`auth: { type: 'CODECONNECTIONS', resource: connectionArn }`), CloudFormation deployment fails during `AWS::CodeBuild::Project` resource creation (`CREATE`) with the following error:

```text
Resource handler returned message: "User is not authorized to access connection arn:aws:codeconnections:us-east-1:123456789012:connection/84941bd4-6795-4871-99f6-e2e4697138da (Service: AWSCodeBuild; Status Code: 400; Error Code: OAuthProviderException; Request ID: c956eb0c-3654-499e-ac75-188f8a0508ea; Proxy: null)"
```

#### Root Cause Analysis:
1. When CloudFormation provisions an `AWS::CodeBuild::Project` resource using `CODECONNECTIONS` authentication, the AWS CodeBuild service handler validates access to the CodeConnections connection ARN **at the moment the `CreateProject` API call is executed**.
2. This validation call uses the project's Service Role (`AWS::IAM::Role`). Therefore, the Service Role must have `codeconnections:UseConnection`, `codeconnections:GetConnection`, and `codeconnections:GetConnectionToken` policies **fully attached and propagated before `AWS::CodeBuild::Project` creation starts**.
3. However, `aws-cdk-lib/aws_codebuild` creates IAM policies (such as `RoleDefaultPolicy` or custom policies attached via `addToRolePolicy`) **without generating a CloudFormation `DependsOn` dependency** on the `AWS::CodeBuild::Project` resource. In fact, if CloudWatch log group ARNs reference the `Project` resource (`Fn::Join` with `Ref: Project`), CDK introduces a dependency direction where `AWS::IAM::Policy` depends on `AWS::CodeBuild::Project`, forcing CloudFormation to attempt `CreateProject` **before** the IAM Policy is attached to the role.
4. As a result, when CloudFormation executes `CreateProject`, the IAM policy granting `codeconnections:UseConnection` is not yet active on the role. CodeBuild receives an authorization error when accessing CodeConnections and translates it into an `OAuthProviderException`.

### Regression Issue

- [ ] Select this option if this issue appears to be a regression.

### Last Known Working CDK Library Version

_No response_

### Expected Behavior

`aws-cdk-lib/aws_codebuild` should automatically establish a CloudFormation resource dependency (`DependsOn`) between `AWS::CodeBuild::Project` and any `AWS::IAM::Policy` that grants CodeConnections permissions (`codeconnections:UseConnection`, `codeconnections:GetConnectionToken`, etc.) to the project's Service Role.

CloudFormation should wait for the IAM Policy granting CodeConnections permissions to finish attaching to the Service Role BEFORE initiating creation of `AWS::CodeBuild::Project`.

### Current Behavior

CloudFormation attempts to create `AWS::CodeBuild::Project` concurrently with or before the `AWS::IAM::Policy` granting `codeconnections:UseConnection` is attached to the CodeBuild Service Role.

This results in the following deployment failure:

```text
MyStack/BuildProject/Resource (AWS::CodeBuild::Project BuildProject097C5DB7)
Resource handler returned message: "User is not authorized to access connection arn:aws:codeconnections:us-east-1:123456789012:connection/84941bd4-6795-4871-99f6-e2e4697138da (Service: AWSCodeBuild; Status Code: 400; Error Code: OAuthProviderException)"
```

### Reproduction Steps

#### ❌Bug Reproduction Code (Fails with OAuthProviderException)

In standard CDK usage, calling `project.addToRolePolicy(...)` attaches an IAM policy to the role, but CDK does not synthesize a CloudFormation `DependsOn` dependency from `AWS::CodeBuild::Project` to the `AWS::IAM::Policy`.

```typescript
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import * as iam from 'aws-cdk-lib/aws-iam';

export class CodeBuildBugReproStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const connectionArn = 'arn:aws:codeconnections:us-east-1:123456789012:connection/84941bd4-6795-4871-99f6-e2e4697138da';

const gitHubSource = codebuild.Source.gitHub({
owner: 'my-org',
repo: 'my-repo',
webhook: false,
});

const project = new codebuild.Project(this, 'BuildProject', {
projectName: 'my-repo-build',
source: gitHubSource,
environment: {
buildImage: codebuild.LinuxArmBuildImage.AMAZON_LINUX_2023_STANDARD_3_0,
computeType: codebuild.ComputeType.SMALL,
},
});

// Override Source to use CodeConnections
const cfnProject = project.node.defaultChild as codebuild.CfnProject;
const currentSource = cfnProject.source as codebuild.CfnProject.SourceProperty;
cfnProject.source = {
...currentSource,
type: 'GITHUB',
location: 'https://github.com/my-org/my-repo.git',
auth: {
type: 'CODECONNECTIONS',
resource: connectionArn,
},
};
cfnProject.triggers = undefined;

// Attach permissions via addToRolePolicy (CDK does NOT generate DependsOn on AWS::CodeBuild::Project)
project.addToRolePolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'codeconnections:UseConnection',
'codeconnections:GetConnection',
'codeconnections:GetConnectionToken',
'codestar-connections:UseConnection',
'codestar-connections:GetConnection',
'codestar-connections:GetConnectionToken',
],
resources: [connectionArn],
})
);
}
}
```

**Synthesized CloudFormation Output (Bug):**
In the synthesized CloudFormation template, `AWS::CodeBuild::Project` has **no `DependsOn` entry** pointing to the `AWS::IAM::Policy` that grants `codeconnections:UseConnection`:

```json
{
"Resources": {
"BuildProjectRoleF32A1C0B": {
"Type": "AWS::IAM::Role"
},
"BuildProjectRoleDefaultPolicy69A0E080": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": ["codeconnections:UseConnection", "codeconnections:GetConnectionToken"],
"Resource": "arn:aws:codeconnections:..."
}
]
},
"Roles": [{ "Ref": "BuildProjectRoleF32A1C0B" }]
}
},
"BuildProject8375C9A4": {
"Type": "AWS::CodeBuild::Project",
"Properties": {
"ServiceRole": { "Fn::GetAtt": ["BuildProjectRoleF32A1C0B", "Arn"] },
"Source": {
"Auth": {
"Resource": "arn:aws:codeconnections:...",
"Type": "CODECONNECTIONS"
},
"Location": "https://github.com/my-org/my-repo.git",
"Type": "GITHUB"
}
}
/* MISSING: "DependsOn": ["BuildProjectRoleDefaultPolicy69A0E080"] */
}
}
}
```

### Possible Solution

`aws_codebuild` constructs should automatically detect when `CODECONNECTIONS` / `CODESTAR_CONNECTIONS` is used and ensure `AWS::CodeBuild::Project` has an explicit `DependsOn` dependency on the `AWS::IAM::Policy` granting those connection permissions.

#### ✅ Workaround Code (Succeeds Deterministically)

By creating a dedicated `iam.Policy` and explicitly attaching `addResourceDependency`, CloudFormation waits for IAM policy propagation before creating the CodeBuild project.

```typescript
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import * as iam from 'aws-cdk-lib/aws-iam';

export class CodeBuildWorkaroundStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const connectionArn = 'arn:aws:codeconnections:us-east-1:123456789012:connection/84941bd4-6795-4871-99f6-e2e4697138da';

const gitHubSource = codebuild.Source.gitHub({
owner: 'my-org',
repo: 'my-repo',
webhook: false,
});

const project = new codebuild.Project(this, 'BuildProject', {
projectName: 'my-repo-build',
source: gitHubSource,
environment: {
buildImage: codebuild.LinuxArmBuildImage.AMAZON_LINUX_2023_STANDARD_3_0,
computeType: codebuild.ComputeType.SMALL,
},
});

const cfnProject = project.node.defaultChild as codebuild.CfnProject;
const currentSource = cfnProject.source as codebuild.CfnProject.SourceProperty;
cfnProject.source = {
...currentSource,
type: 'GITHUB',
location: 'https://github.com/my-org/my-repo.git',
auth: {
type: 'CODECONNECTIONS',
resource: connectionArn,
},
};
cfnProject.triggers = undefined;

// 1. Create dedicated IAM Policy for CodeConnections
const connPolicy = new iam.Policy(this, 'ConnPolicy', {
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
'codeconnections:UseConnection',
'codeconnections:GetConnection',
'codeconnections:GetConnectionToken',
'codestar-connections:UseConnection',
'codestar-connections:GetConnection',
'codestar-connections:GetConnectionToken',
],
resources: [connectionArn],
}),
],
});

if (project.role) {
connPolicy.attachToRole(project.role);
}

// 2. Explicitly tell CloudFormation that AWS::CodeBuild::Project DEPENDS ON ConnPolicy
const cfnConnPolicy = connPolicy.node.defaultChild as cdk.CfnResource;
if (cfnConnPolicy) {
cfnProject.addResourceDependency(cfnConnPolicy);
}
}
}
```

**Synthesized CloudFormation Output (Workaround / Fixed):**
```json
"BuildProject8375C9A4": {
"Type": "AWS::CodeBuild::Project",
"DependsOn": [
"ConnPolicy6421E4D7"
],
"Properties": { ... }
}
```

### Additional Information/Context

_No response_

### AWS CDK Library version (aws-cdk-lib)

2.263.0

### AWS CDK CLI version

2.1133.0

### Node.js Version

v26.6.0

### OS

macOS 26

### Language

TypeScript

### Language Version

TypeScript (5.9.3)

### Other information

This behavior affects any AWS CodeBuild project relying on AWS CodeConnections / CodeStar Connections without manual CloudFormation `DependsOn` dependency overrides.

Contributor guide

Open the contributing guide

Research direction

Start in the aws_codebuild Project construct and trace how its AWS::CodeBuild::Project and service-role IAM policies are synthesized. Synthesize the provided CodeConnections reproduction and inspect the CloudFormation dependency graph; done means the project explicitly waits for the relevant policy and regression coverage verifies the dependency without breaking ordinary projects.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, typescript
Domain
cloud, devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.