diff --git a/API.md b/API.md
index 9682d84c..8444d2c9 100644
--- a/API.md
+++ b/API.md
@@ -1781,6 +1781,7 @@ const gitHubWorkflowProps: GitHubWorkflowProps = { ... }
| jobSettings | JobSettings | Job level settings that will be applied to all jobs in the workflow, including synth and asset deploy jobs. |
| postBuildSteps | JobStep[] | GitHub workflow steps to execute after build. |
| preBuildSteps | JobStep[] | GitHub workflow steps to execute before build. |
+| preDeploySteps | JobStep[] | GitHub workflow steps to execute at the start of every deploy job, before AWS credentials are configured. |
| requireApproval | string | What approval level is required for deployments? |
| runner | Runner | The type of runner that the entire workflow runs on. |
| workflowName | string | Name of the workflow. |
@@ -1942,6 +1943,19 @@ GitHub workflow steps to execute before build.
---
+##### `preDeploySteps`Optional
+
+```typescript
+public readonly preDeploySteps: JobStep[];
+```
+
+- *Type:* JobStep[]
+- *Default:* []
+
+GitHub workflow steps to execute at the start of every deploy job, before AWS credentials are configured.
+
+---
+
##### `requireApproval`Optional
```typescript
diff --git a/src/pipeline.ts b/src/pipeline.ts
index 3ea4b30a..e775b101 100644
--- a/src/pipeline.ts
+++ b/src/pipeline.ts
@@ -105,6 +105,14 @@ export interface GitHubWorkflowProps extends PipelineBaseProps, AwsCredsCommonPr
*/
readonly postBuildSteps?: github.JobStep[];
+ /**
+ * GitHub workflow steps to execute at the start of every deploy job, before
+ * AWS credentials are configured.
+ *
+ * @default []
+ */
+ readonly preDeploySteps?: github.JobStep[];
+
/**
* What approval level is required for deployments? By default this is
* `never` to ensure that all automated deployments succeed.
@@ -158,6 +166,7 @@ export class GitHubWorkflow extends PipelineBase {
private readonly buildRunner: github.Runner;
private readonly preBuildSteps: github.JobStep[];
private readonly postBuildSteps: github.JobStep[];
+ private readonly preDeploySteps: github.JobStep[];
private readonly deployArgs: string[];
private readonly diffFirst: boolean;
private readonly jobOutputs: Record = {};
@@ -185,6 +194,7 @@ export class GitHubWorkflow extends PipelineBase {
this.buildContainer = props.buildContainer;
this.preBuildSteps = props.preBuildSteps ?? [];
this.postBuildSteps = props.postBuildSteps ?? [];
+ this.preDeploySteps = props.preDeploySteps ?? [];
this.jobSettings = props.jobSettings;
this.diffFirst = props.diffFirst ?? false;
this.deployArgs = props.deployArgs ?? [];
@@ -484,6 +494,7 @@ export class GitHubWorkflow extends PipelineBase {
needs: this.renderDependencies(node),
runsOn: this.runner.runsOn,
steps: [
+ ...this.preDeploySteps,
...this.stepsToUnpackageAssembly,
...awsCredentials.credentialSteps(region),
...this.stepsToDeploy(stack),
diff --git a/test/__snapshots__/github.test.ts.snap b/test/__snapshots__/github.test.ts.snap
index 32424c0e..83fba126 100644
--- a/test/__snapshots__/github.test.ts.snap
+++ b/test/__snapshots__/github.test.ts.snap
@@ -824,6 +824,79 @@ jobs:
"
`;
+exports[`single wave/stage/stack - with pre-deploy steps 1`] = `
+"# AUTOMATICALLY GENERATED FILE, DO NOT EDIT MANUALLY.
+# Generated by AWS CDK and [cdk-pipelines-github](https://github.com/cdklabs/cdk-pipelines-github)
+
+name: deploy
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch: {}
+jobs:
+ Build-Build:
+ name: Synthesize
+ permissions:
+ contents: read
+ id-token: none
+ runs-on: ubuntu-latest
+ needs: []
+ env: {}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Authenticate Via GitHub Secrets
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-region: us-east-1
+ role-duration-seconds: 1800
+ role-skip-session-tagging: true
+ aws-access-key-id: \${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: \${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ - name: Build
+ run: ""
+ - name: Package cdk.out
+ run: tar -zcf cdk.out.tgz github.out
+ - name: Upload cdk.out
+ uses: actions/upload-artifact@v4
+ with:
+ name: cdk.out
+ path: cdk.out.tgz
+ include-hidden-files: true
+ retention-days: 1
+ if-no-files-found: error
+ MyStack-MyStack-Deploy:
+ name: Deploy MyStack/MyStack
+ permissions:
+ contents: read
+ id-token: none
+ needs:
+ - Build-Build
+ runs-on: ubuntu-latest
+ steps:
+ - name: Pre Deploy
+ run: echo pre-deploy
+ - name: Download cdk.out
+ uses: actions/download-artifact@v4
+ with:
+ name: cdk.out
+ - name: Unpackage cdk.out
+ run: tar -zxf cdk.out.tgz
+ - name: Authenticate Via GitHub Secrets
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-region: us-east-1
+ role-duration-seconds: 1800
+ role-skip-session-tagging: true
+ aws-access-key-id: \${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: \${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ - id: Deploy
+ run: npx cdk --app github.out deploy MyStack/MyStack --require-approval=never
+ --exclusively
+"
+`;
+
exports[`single wave/stage/stack 1`] = `
"# AUTOMATICALLY GENERATED FILE, DO NOT EDIT MANUALLY.
# Generated by AWS CDK and [cdk-pipelines-github](https://github.com/cdklabs/cdk-pipelines-github)
diff --git a/test/github.test.ts b/test/github.test.ts
index d6fca241..6b90c71e 100644
--- a/test/github.test.ts
+++ b/test/github.test.ts
@@ -315,6 +315,34 @@ test('single wave/stage/stack', () => {
expect(readFileSync(pipeline.workflowPath, 'utf-8')).toMatchSnapshot();
});
});
+test('single wave/stage/stack - with pre-deploy steps', () => {
+ withTemporaryDirectory((dir) => {
+ const pipeline = new GitHubWorkflow(app, 'Pipeline', {
+ workflowPath: `${dir}/.github/workflows/deploy.yml`,
+ synth: new ShellStep('Build', {
+ commands: [],
+ }),
+ preDeploySteps: [{ name: 'Pre Deploy', run: 'echo pre-deploy' }],
+ });
+
+ const stage = new Stage(app, 'MyStack', {
+ env: { account: '111111111111', region: 'us-east-1' },
+ });
+
+ new Stack(stage, 'MyStack');
+
+ pipeline.addStage(stage);
+
+ app.synth();
+
+ const workflow = readFileSync(pipeline.workflowPath, 'utf-8');
+ const deployJob = workflow.slice(workflow.indexOf('name: Deploy MyStack/MyStack'));
+ expect(deployJob.indexOf('name: Pre Deploy')).toBeGreaterThan(-1);
+ expect(deployJob.indexOf('name: Pre Deploy')).toBeLessThan(deployJob.indexOf('name: Download cdk.out'));
+ expect(workflow.split('name: Pre Deploy').length - 1).toBe(1);
+ expect(workflow).toMatchSnapshot();
+ });
+});
test('single wave/stage/stack - with diff enabled', () => {
withTemporaryDirectory((dir) => {
const pipeline = new GitHubWorkflow(app, 'Pipeline', {