Overview
The starter kit includes three constructs: two that handle environment-aware naming and common infrastructure patterns, plus one that wires up GitHub Actions OIDC access for keyless deployments. Extend the base constructs for consistent behavior across your stacks.
Learn the fundamentals of creating AWS CDK constructs before diving into the patterns below.
BaseConstruct
Source: src/constructs/base-construct.ts
Base class that exposes environment context and safe naming helpers. All custom constructs should extend this class.
Constructor
new BaseConstruct(scope: Construct, id: string)
| Parameter | Type | Description |
|---|---|---|
scope | Construct | Parent construct scope |
id | string | Unique identifier within scope |
Properties
| Property | Type | Description |
|---|---|---|
branch | string | undefined | Cleaned branch name from GIT_BRANCH_REF. Returns undefined for main, develop, and version tags. |
environment | string | Environment name from ENVIRONMENT variable. Defaults to dev. |
account | string | AWS account ID of the current stack |
region | string | AWS region of the current stack |
Methods
unique
Generates environment or branch-suffixed resource names (max 64 characters).
unique(name: string): string
| Parameter | Type | Description |
|---|---|---|
name | string | Base name for the resource |
Returns: Suffixed resource name like data-123456789012-test or data-123456789012-feature-auth.
Usage example
import { Construct } from 'constructs';
import { BaseConstruct } from '../constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
export class DataBucketConstruct extends BaseConstruct {
public readonly bucket: s3.Bucket;
constructor(scope: Construct, id: string) {
super(scope, id);
// Environment-aware bucket name
this.bucket = new s3.Bucket(this, 'Bucket', {
bucketName: this.unique(`data-${this.account}`),
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});
// Conditional logic based on environment
if (this.environment === 'production') {
this.bucket.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);
}
}
}
NetworkConstruct
Source: src/constructs/network-construct.ts
Creates an opinionated VPC with environment-specific configuration. Includes public, private, and isolated subnets plus gateway endpoints and flow logs.
Constructor
new NetworkConstruct(scope: Construct, id: string)
| Parameter | Type | Description |
|---|---|---|
scope | Construct | Parent construct scope |
id | string | Unique identifier within scope |
Configuration by environment
| Setting | Development/Test | Production |
|---|---|---|
| CIDR | 172.16.0.0/16 (dev), 172.17.0.0/16 (test) | 172.18.0.0/16 |
| NAT Gateways | 1 | 3 |
| Availability Zones | 3 | 3 |
| Subnet size | /20 per subnet | /20 per subnet |
Resources created
| Resource | Description |
|---|---|
| VPC | Three-tier VPC with public, private, and isolated subnets |
| NAT Gateways | Environment-specific count for private subnet egress |
| Gateway Endpoints | S3 and DynamoDB endpoints included |
| Flow Logs | Stored in encrypted S3 bucket with blocked public access |
Properties
| Property | Type | Description |
|---|---|---|
vpc | ec2.Vpc | The created VPC instance |
Usage example
import { NetworkConstruct } from '../constructs';
export class AppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const network = new NetworkConstruct(this, 'Network');
// Use the VPC for other resources
new ecs.Cluster(this, 'Cluster', {
vpc: network.vpc,
});
}
}
GitHubActionsOidcConstruct
Source: src/constructs/github-actions-oidc-construct.ts
Creates a GitHub Actions OpenID Connect (OIDC) provider and an IAM deployment role so workflows can deploy to AWS without long-lived credentials. FoundationStack already uses this construct, so you rarely instantiate it directly, but it works standalone when you need a dedicated deployment role.
Constructor
new GitHubActionsOidcConstruct(scope: Construct, id: string, props: GitHubActionsOidcConstructProps)
GitHubActionsOidcConstructProps
| Property | Type | Required | Description |
|---|---|---|---|
environment | string | Yes | GitHub environment name allowed to assume the role. The trust policy is scoped to repo:<owner>/<repo>:environment:<environment>. |
additionalRepositories | string[] | No | Extra repository names, under the same GitHub owner, allowed to assume the role. Provide bare names like my-cdk-app. Defaults to only the current git remote repo. |
maxSessionDuration | cdk.Duration | No | Maximum session duration for the deployment role. Defaults to cdk.Duration.hours(2). |
roleName | string | No | Name of the IAM role. Defaults to process.env.GITHUB_DEPLOY_ROLE or GitHubActionsServiceRole. |
Resources created
| Resource | Type | Description |
|---|---|---|
| OIDC Provider | iam.OpenIdConnectProvider | Trusts token.actions.githubusercontent.com with client ID sts.amazonaws.com |
| Deployment role | iam.Role | Assumed via GitHub OIDC, attached AdministratorAccess, scoped to the environment and repositories |
Properties
| Property | Type | Description |
|---|---|---|
provider | iam.OpenIdConnectProvider | The GitHub Actions OIDC identity provider |
role | iam.Role | The IAM role assumed by GitHub Actions workflows |
The GitHub owner and repository name are resolved automatically from the current git remote (git config --get remote.origin.url), so no manual configuration is needed for the primary repository.
Usage example
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { GitHubActionsOidcConstruct } from '../constructs';
export class CiCdStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new GitHubActionsOidcConstruct(this, 'GitHubActionsOidc', {
environment: 'production',
maxSessionDuration: cdk.Duration.hours(1),
});
}
}
Creating custom constructs
Follow this pattern when building your own constructs:
// src/constructs/my-construct.ts
import { Construct } from 'constructs';
import { BaseConstruct } from './base-construct';
export interface MyConstructProps {
readonly featureEnabled?: boolean;
}
export class MyConstruct extends BaseConstruct {
constructor(scope: Construct, id: string, props?: MyConstructProps) {
super(scope, id);
// Access inherited properties
console.log(`Environment: ${this.environment}`);
console.log(`Branch: ${this.branch ?? 'none'}`);
// Use unique() for resource names
const resourceName = this.unique('my-resource');
}
}
Export from index:
// src/constructs/index.ts
export { BaseConstruct } from './base-construct';
export { GitHubActionsOidcConstruct } from './github-actions-oidc-construct';
export { NetworkConstruct } from './network-construct';
export { MyConstruct } from './my-construct';
Next steps
- Aspects - Add governance rules to your constructs
- Stacks - See how constructs are used in stacks
- Usage Examples - Common patterns and recipes