Skip to content

Commit

Permalink
feat: support for AWS CodeArtifact (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
gyalai-aws authored Sep 3, 2024
1 parent 766a65e commit 26adee6
Show file tree
Hide file tree
Showing 20 changed files with 418 additions and 39 deletions.
5 changes: 4 additions & 1 deletion docs/content/developer_guides/.pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ nav:
- "Modularizing Stacks": modularizing_stacks.md
- "CDK Context": "cdk_context.md"
- "Security": "security.md"
- "Python Dependencies": "python_dependencies.md"
- "Python Dependencies":
- "AWS CodeArtifact": "codeartifact.md"
- "python_dependencies.md"
- "NPM Dependencies":
- "AWS CodeArtifact": "codeartifact.md"
- "Private NPM Registry": "private_npm_registry.md"
- "GIT Integrations":
- "CodeCommit": "vcs_codecommit.md"
Expand Down
40 changes: 40 additions & 0 deletions docs/content/developer_guides/codeartifact.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Using CodeArtifact

AWS CodeArtifact is a fully managed artifact repository service that makes it easy for organizations of any size to securely store, publish, and share software packages used in their development process. AWS CodeArtifact supports popular package formats and works with commonly used build tools and package managers.

## Prerequisites
- If you do not have an existing AWS CodeArtifact repository please create one using the AWS Management Console or AWS CLI. For more information, see [Creating a repository](https://docs.aws.amazon.com/codeartifact/latest/ug/getting-started.html#get-started-create-repo). Ensure the repository is configured to upstream the desired package sources, you must be able to fetch 'aws-cdk-lib' and 'cdklabs' packages from the repository.

## Configuring the CI/CD pipeline

To use AWS CodeArtifact in your pipeline, you need to configure the `CodeArtifactPlugin` plugin. This plugin is responsible for setting up the necessary commands to authenticate with the AWS CodeArtifact repository and manage the required IAM permissions for the pipeline.

```typeScript
import { PipelineBlueprint, CodeArtifactPlugin } from '@cdklabs/cdk-cicd-wrapper';

const pipeline = PipelineBlueprint.builder()
.plugin(new CodeArtifactPlugin({
domain: 'my-domain',
repositoryName: 'my-repo',
}))
.synth(app);
```

The above snippet configures the pipeline to authenticate with the AWS CodeArtifact repository `my-domain/my-repo`. The plugin will automatically set up the necessary IAM permissions for the pipeline to access the repository.

## Using AWS CodeArtifact for Python/Swift/dotnet packages

To use AWS CodeArtifact for Python, Swift, or dotnet packages, you need to configure the plugin for those package types. The `CodeArtifactPlugin` accepts an optional `repositoryTypes` parameter that allows you to specify the package types you want to use with AWS CodeArtifact.

```typeScript
import { PipelineBlueprint, CodeArtifactPlugin, CodeArtifactRepositoryTypes} from '@cdklabs/cdk-cicd-wrapper';

const pipeline = PipelineBlueprint.builder()
.plugin(new CodeArtifactPlugin({
domain: 'my-domain',
repositoryName: 'my-repo',
repositoryTypes: [CodeArtifactRepositoryTypes.NPM, CodeArtifactRepositoryTypes.PIP, CodeArtifactRepositoryTypes.SWIFT, CodeArtifactRepositoryTypes.NUGET],
}))
.addStack(new MyStack())
.synth(app);
```
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ export interface PipelineProps {
*/
readonly codeBuildDefaults: pipelines.CodeBuildOptions;

/**
* Default options for the synth CodeBuild project.
*/
readonly synthCodeBuildDefaults?: pipelines.CodeBuildOptions;

/**
* Additional Pipeline options.
*/
Expand Down Expand Up @@ -209,6 +214,7 @@ export class CDKPipeline extends pipelines.CodePipeline {
},
primaryOutputDirectory: props.primaryOutputDirectory,
}),
synthCodeBuildDefaults: props.synthCodeBuildDefaults,
codeBuildDefaults: props.codeBuildDefaults,
...(props.options ?? {}),
});
Expand Down
30 changes: 27 additions & 3 deletions packages/@cdklabs/cdk-cicd-wrapper/src/common/types/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,15 +449,39 @@ export interface IPlugin {
/**
* The method called when the Pipeline configuration finalized.
*/
create?(context: ResourceContext): void;
create(context: ResourceContext): void;

/**
* The method called before the stage is created.
*/
beforeStage?(scope: Construct, context: ResourceContext): void;
beforeStage(scope: Construct, context: ResourceContext): void;

/**
* The method called after the stage is created.
*/
afterStage?(scope: Construct, context: ResourceContext): void;
afterStage(scope: Construct, context: ResourceContext): void;
}

export abstract class PluginBase implements IPlugin {
abstract readonly name: string;

abstract readonly version: string;

// eslint-disable-next-line @typescript-eslint/no-unused-vars
//@ts-ignore
create(context: ResourceContext): void {
return;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
//@ts-ignore
beforeStage(scope: Construct, context: ResourceContext): void {
return;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
//@ts-ignore
afterStage(scope: Construct, context: ResourceContext): void {
return;
}
}
3 changes: 3 additions & 0 deletions packages/@cdklabs/cdk-cicd-wrapper/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ export * from './stacks';

// Exporting all exports from the './utils' file
export * from './utils';

// Exporting all exports from the './plugins' file
export * from './plugins';
2 changes: 1 addition & 1 deletion packages/@cdklabs/cdk-cicd-wrapper/src/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export * from './security/EncryptCloudWatchLogGroupsPlugin';
export * from './security/EncryptSNSTopicOnTransitPlugin';
export * from './security/RotateEncryptionKeysPlugin';
export * from './security/LambdaDLQPlugin';

export * from './utils/CodeArtifactPlugin';
/**
* Class containing static instances of various security and optimization plugins.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
import { IAspect, Aspects, RemovalPolicy } from 'aws-cdk-lib';
import { Key } from 'aws-cdk-lib/aws-kms';
import { Construct } from 'constructs';
import { IPlugin, ResourceContext, Stage } from '../../common';
import { PluginBase, ResourceContext, Stage } from '../../common';

/**
* Plugin to destroy encryption keys on delete.
*/
export class DestroyEncryptionKeysOnDeletePlugin implements IPlugin {
export class DestroyEncryptionKeysOnDeletePlugin extends PluginBase {
readonly name: string = 'DestroyEncryptionKeysOnDeletePlugin';

readonly version: string = '1.0';

constructor(readonly stagesToRetain: Stage[] = [Stage.PROD]) {}
constructor(readonly stagesToRetain: Stage[] = [Stage.PROD]) {
super();
}

afterStage(scope: Construct, context: ResourceContext): void {
if (this.stagesToRetain.includes(context.stage)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import { IAspect, Aspects, Names } from 'aws-cdk-lib';
import { CfnBucket } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import { GlobalResources, IPlugin, ResourceContext } from '../../common';
import { GlobalResources, PluginBase, ResourceContext } from '../../common';

/**
* Plugin to enable access logs for an S3 bucket.
*/
export class AccessLogsForBucketPlugin implements IPlugin {
export class AccessLogsForBucketPlugin extends PluginBase {
readonly name: string = 'AccessLogsForBucketPlugin';

readonly version: string = '1.0';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import { IAspect, Aspects } from 'aws-cdk-lib';
import { CfnSubnet } from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';
import { IPlugin, ResourceContext } from '../../common';
import { PluginBase, ResourceContext } from '../../common';

/**
* Plugin to disable public IP assignment for EC2 instances.
*/
export class DisablePublicIPAssignmentForEC2Plugin implements IPlugin {
export class DisablePublicIPAssignmentForEC2Plugin extends PluginBase {
readonly name: string = 'DisablePublicIPAssignmentForEC2Plugin';

readonly version: string = '1.0';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { IAspect, Aspects } from 'aws-cdk-lib';
import { Effect, PolicyStatement, AnyPrincipal } from 'aws-cdk-lib/aws-iam';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import { IPlugin, ResourceContext } from '../../common';
import { PluginBase, ResourceContext } from '../../common';

/**
* Plugin to enforce encryption in transit for an S3 bucket.
*/
export class EncryptBucketOnTransitPlugin implements IPlugin {
export class EncryptBucketOnTransitPlugin extends PluginBase {
readonly name: string = 'EncryptBucketOnTransitPlugin';

readonly version: string = '1.0';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { IAspect, Aspects } from 'aws-cdk-lib';
import { Key } from 'aws-cdk-lib/aws-kms';
import { CfnLogGroup } from 'aws-cdk-lib/aws-logs';
import { Construct } from 'constructs';
import { GlobalResources, IPlugin, ResourceContext } from '../../common';
import { GlobalResources, PluginBase, ResourceContext } from '../../common';

/**
* Plugin to encrypt CloudWatch Log Groups.
*/
export class EncryptCloudWatchLogGroupsPlugin implements IPlugin {
export class EncryptCloudWatchLogGroupsPlugin extends PluginBase {
readonly name: string = 'EncryptCloudWatchLogGroupsPlugin';

readonly version: string = '1.0';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { IAspect, Aspects } from 'aws-cdk-lib';
import { Effect, PolicyStatement, AnyPrincipal } from 'aws-cdk-lib/aws-iam';
import { Topic } from 'aws-cdk-lib/aws-sns';
import { Construct } from 'constructs';
import { IPlugin, ResourceContext } from '../../common';
import { PluginBase, ResourceContext } from '../../common';

/**
* Plugin to enable encryption for SNS topics.
*/
export class EncryptSNSTopicOnTransitPlugin implements IPlugin {
export class EncryptSNSTopicOnTransitPlugin extends PluginBase {
readonly name: string = 'EncryptSNSTopicOnTransitPlugin';

readonly version: string = '1.0';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@ import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import * as nag from 'cdk-nag';
import { Construct } from 'constructs';
import { IPlugin, ResourceContext } from '../../';
import { PluginBase, ResourceContext } from '../../';

export interface LambdaDLQPluginProps {
export interface ILambdaDLQPluginProps {
/**
* The retentionPeriod for the DLQ.
*/
retentionPeriod?: cdk.Duration;
}

export class LambdaDLQPlugin implements IPlugin {
export class LambdaDLQPlugin extends PluginBase {
readonly name = 'LambdaDLQPlugin';
readonly version = '1.0';

readonly retentionPeriod: cdk.Duration;

constructor(props?: LambdaDLQPluginProps) {
constructor(props?: ILambdaDLQPluginProps) {
super();
this.retentionPeriod = props?.retentionPeriod ?? cdk.Duration.days(14);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import { IAspect, Aspects } from 'aws-cdk-lib';
import { CfnKey } from 'aws-cdk-lib/aws-kms';
import { Construct } from 'constructs';
import { IPlugin, ResourceContext } from '../../common';
import { PluginBase, ResourceContext } from '../../common';

/**
* Plugin to enable key rotation for KMS keys.
*/
export class RotateEncryptionKeysPlugin implements IPlugin {
export class RotateEncryptionKeysPlugin extends PluginBase {
readonly name: string = 'RotateEncryptionKeysPlugin';

readonly version: string = '1.0';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { BuildSpec } from 'aws-cdk-lib/aws-codebuild';
import * as iam from 'aws-cdk-lib/aws-iam';
import { GlobalResources, PluginBase, ResourceContext } from '../../common';

export enum CodeArtifactRepositoryTypes {
NPM = 'npm',
PIP = 'pip',
NUGET = 'nuget',
SWIFT = 'swift',
DOTNET = 'dotnet',
TWINE = 'twine',
}

export interface CodeArtifactPluginProps {
readonly domain: string;

readonly account?: string;

readonly repositoryName: string;

readonly repositoryTypes?: CodeArtifactRepositoryTypes[];

readonly npmScope?: string;
}

/**
* Plugin to enable key rotation for KMS keys.
*/
export class CodeArtifactPlugin extends PluginBase {
readonly name: string = 'CodeArtifactPlugin';

readonly version: string = '1.0';

constructor(private readonly options: CodeArtifactPluginProps) {
super();
}

create(context: ResourceContext): void {
const { domain, repositoryName } = this.options;
const account = this.options.account || context.blueprintProps.deploymentDefinition.RES.env.account;
const region = context.blueprintProps.deploymentDefinition.RES.env.region;
const repositoryTypes = this.options.repositoryTypes || [CodeArtifactRepositoryTypes.NPM];

const commands = repositoryTypes.map((type) =>
type === CodeArtifactRepositoryTypes.NPM && this.options.npmScope
? `aws codeartifact login --domain ${domain} --domain-owner ${account} --repository ${repositoryName} --tool ${type} --namespace ${this.options.npmScope}`
: `aws codeartifact login --domain ${domain} --domain-owner ${account} --repository ${repositoryName} --tool ${type}`,
);

const ciDefinition = context.get(GlobalResources.CI_DEFINITION);

ciDefinition.append(
BuildSpec.fromObject({
phases: {
pre_build: {
commands: commands,
},
},
}),
);

ciDefinition.additionalPolicyStatements([
new iam.PolicyStatement({
actions: ['codeartifact:GetAuthorizationToken'],
resources: [`arn:aws:codeartifact:${region}:${account}:domain/${domain}`],
}),
new iam.PolicyStatement({
actions: ['codeartifact:GetRepositoryEndpoint', 'codeartifact:ReadFromRepository'],
resources: [`arn:aws:codeartifact:${region}:${account}:repository/${domain}/${repositoryName}`],
}),
new iam.PolicyStatement({
actions: ['sts:GetServiceBearerToken'],
resources: ['*'],
conditions: { StringEquals: { 'sts:AWSServiceName': 'codeartifact.amazonaws.com' } },
}),
]);
}
}
Loading

0 comments on commit 26adee6

Please sign in to comment.