Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
18 changes: 18 additions & 0 deletions packages/aws-cdk-lib/aws-codebuild/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ Example:
aws codebuild import-source-credentials --server-type GITHUB --auth-type PERSONAL_ACCESS_TOKEN --token <token_value>
```

Alternatively, you can use a CodeConnections connection for GitHub App authentication:

```ts
const gitHubSource = codebuild.Source.gitHub({
owner: 'awslabs',
repo: 'aws-cdk',
connectionArn: 'arn:aws:codeconnections:us-east-1:123456789012:connection/your-connection-id',
webhookFilters: [
codebuild.FilterGroup
.inEventOf(codebuild.EventAction.WORKFLOW_JOB_QUEUED),
],
});
```

When `connectionArn` is provided, the source uses CodeConnections (GitHub App) authentication
instead of OAuth or personal access token credentials. The required IAM permissions
for the connection are automatically granted to the project's role.

### `BitBucketSource`

This source type can be used to build code from a BitBucket repository.
Expand Down
29 changes: 29 additions & 0 deletions packages/aws-cdk-lib/aws-codebuild/lib/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,20 @@ export interface GitHubSourceProps extends CommonGithubSourceProps {
* @default undefined will create an organization webhook
*/
readonly repo?: string;

/**
* The ARN of the CodeConnections connection to use for authentication.
*
* When provided, the source will use CodeConnections (GitHub App) authentication
* instead of the default OAuth or personal access token credentials.
*
* The required IAM permissions for the connection will be automatically granted
* to the project's role.
*
* @see https://docs.aws.amazon.com/codebuild/latest/userguide/connections-github-app.html
* @default - the source will use the default credentials configured for GitHub in the account
*/
readonly connectionArn?: string;
}

/**
Expand All @@ -771,20 +785,35 @@ class GitHubSource extends CommonGithubSource {
public readonly type = GITHUB_SOURCE_TYPE;
private readonly sourceLocation: string;
private readonly organization?: string;
private readonly connectionArn?: string;
protected readonly webhookFilters: FilterGroup[];
constructor(props: GitHubSourceProps) {
super(props);
this.organization = props.repo === undefined ? props.owner : undefined;
this.webhookFilters = props.webhookFilters ?? (this.organization ? [FilterGroup.inEventOf(EventAction.WORKFLOW_JOB_QUEUED)] : []);
this.sourceLocation = this.organization ? 'CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION' : `https://github.com/${props.owner}/${props.repo}.git`;
this.connectionArn = props.connectionArn;
}

public bind(_scope: Construct, project: IProject): SourceConfig {
if (this.connectionArn) {
project.addToRolePolicy(new iam.PolicyStatement({
actions: [
'codeconnections:UseConnection',
],
resources: [this.connectionArn],
}));
}

const superConfig = super.bind(_scope, project);
return {
sourceProperty: {
...superConfig.sourceProperty,
location: this.sourceLocation,
auth: this.connectionArn ? {
type: 'CODECONNECTIONS',
resource: this.connectionArn,
} : undefined,
},
sourceVersion: superConfig.sourceVersion,
buildTriggers: this.organization
Expand Down
128 changes: 128 additions & 0 deletions packages/aws-cdk-lib/aws-codebuild/test/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,134 @@ describe('GitHub source', () => {
});
});

test('can create GitHub source with CodeConnections auth', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
new codebuild.Project(stack, 'Project', {
source: codebuild.Source.gitHub({
owner: 'testowner',
repo: 'testrepo',
connectionArn: 'arn:aws:codeconnections:us-east-1:123456789012:connection/test-connection-id',
webhookFilters: [
codebuild.FilterGroup.inEventOf(codebuild.EventAction.WORKFLOW_JOB_QUEUED),
],
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', {
Source: {
Type: 'GITHUB',
Location: 'https://github.com/testowner/testrepo.git',
Auth: {
Type: 'CODECONNECTIONS',
Resource: 'arn:aws:codeconnections:us-east-1:123456789012:connection/test-connection-id',
},
},
Triggers: {
Webhook: true,
FilterGroups: [
[
{
Type: 'EVENT',
Pattern: 'WORKFLOW_JOB_QUEUED',
},
],
],
},
});
});

test('can create organizational webhook with CodeConnections auth', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
new codebuild.Project(stack, 'Project', {
source: codebuild.Source.gitHub({
owner: 'testowner',
connectionArn: 'arn:aws:codeconnections:us-east-1:123456789012:connection/test-connection-id',
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', {
Source: {
Type: 'GITHUB',
Location: 'CODEBUILD_DEFAULT_WEBHOOK_SOURCE_LOCATION',
Auth: {
Type: 'CODECONNECTIONS',
Resource: 'arn:aws:codeconnections:us-east-1:123456789012:connection/test-connection-id',
},
},
Triggers: {
Webhook: true,
ScopeConfiguration: {
Name: 'testowner',
},
FilterGroups: [
[
{
Type: 'EVENT',
Pattern: 'WORKFLOW_JOB_QUEUED',
},
],
],
},
});
});

test('CodeConnections auth grants required IAM permissions', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
new codebuild.Project(stack, 'Project', {
source: codebuild.Source.gitHub({
owner: 'testowner',
repo: 'testrepo',
connectionArn: 'arn:aws:codeconnections:us-east-1:123456789012:connection/test-connection-id',
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', {
PolicyDocument: {
Statement: Match.arrayWith([
Match.objectLike({
Action: 'codeconnections:UseConnection',
Effect: 'Allow',
Resource: 'arn:aws:codeconnections:us-east-1:123456789012:connection/test-connection-id',
}),
]),
},
});
});

test('GitHub source without connectionArn does not set auth', () => {
// GIVEN
const stack = new cdk.Stack();

// WHEN
new codebuild.Project(stack, 'Project', {
source: codebuild.Source.gitHub({
owner: 'testowner',
repo: 'testrepo',
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CodeBuild::Project', {
Source: {
Type: 'GITHUB',
Location: 'https://github.com/testowner/testrepo.git',
Auth: Match.absent(),
},
});
});

test('can be added to a CodePipeline', () => {
const stack = new cdk.Stack();
const project = new codebuild.Project(stack, 'Project', {
Expand Down
Loading