diff --git a/.eslintrc.json b/.eslintrc.json
index b017cf4..85f17ab 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -35,11 +35,7 @@
}
},
"ignorePatterns": [
- "*.js",
- "*.d.ts",
- "node_modules/",
- "*.generated.ts",
- "coverage",
+ "src/generated/*.ts",
"!.projenrc.ts",
"!projenrc/**/*.ts"
],
diff --git a/.gitattributes b/.gitattributes
index 4958a7b..6db0cbd 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -19,5 +19,6 @@
/API.md linguist-generated
/LICENSE linguist-generated
/package.json linguist-generated
+/src/generated/IamRoleProps.ts linguist-generated
/tsconfig.dev.json linguist-generated
/yarn.lock linguist-generated
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index c6be6c4..0496abb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,3 +51,4 @@ junit.xml
tsconfig.json
!/API.md
!/.projenrc.ts
+!/src/generated/IamRoleProps.ts
diff --git a/.projen/deps.json b/.projen/deps.json
index c390362..5ccfbef 100644
--- a/.projen/deps.json
+++ b/.projen/deps.json
@@ -1,5 +1,9 @@
{
"dependencies": [
+ {
+ "name": "@mrgrain/jsii-struct-builder",
+ "type": "build"
+ },
{
"name": "@types/jest",
"type": "build"
@@ -98,7 +102,7 @@
},
{
"name": "aws-cdk-lib",
- "version": "^2.1.0",
+ "version": "^2.73.0",
"type": "peer"
},
{
diff --git a/.projen/files.json b/.projen/files.json
index 9d31ccb..89875e4 100644
--- a/.projen/files.json
+++ b/.projen/files.json
@@ -14,6 +14,7 @@
".projen/files.json",
".projen/tasks.json",
"LICENSE",
+ "src/generated/IamRoleProps.ts",
"tsconfig.dev.json"
],
"//": "~~ Generated by projen. To modify, edit .projenrc.ts and run \"npx projen\"."
diff --git a/.projen/tasks.json b/.projen/tasks.json
index 32cb622..723808c 100644
--- a/.projen/tasks.json
+++ b/.projen/tasks.json
@@ -206,7 +206,8 @@
"name": "release",
"description": "Prepare a release from \"main\" branch",
"env": {
- "RELEASE": "true"
+ "RELEASE": "true",
+ "MAJOR": "1"
},
"steps": [
{
diff --git a/.projenrc.ts b/.projenrc.ts
index ab9da13..c427ca5 100644
--- a/.projenrc.ts
+++ b/.projenrc.ts
@@ -1,13 +1,15 @@
+import { ProjenStruct, Struct } from "@mrgrain/jsii-struct-builder";
import { ReleasableCommits, awscdk } from "projen";
import { ProseWrap } from "projen/lib/javascript";
const project = new awscdk.AwsCdkConstructLibrary({
author: "Ben Limmer",
authorAddress: "hello@benlimmer.com",
- cdkVersion: "2.1.0",
+ cdkVersion: "2.73.0", // Released in April 2023
defaultReleaseBranch: "main",
name: "@blimmer/cdk-circleci-oidc",
repositoryUrl: "https://github.com/blimmer/cdk-circleci-oidc.git",
+ majorVersion: 1,
projenrcTs: true,
@@ -19,8 +21,14 @@ const project = new awscdk.AwsCdkConstructLibrary({
module: "cdk_circleci_oidc",
},
+ // deps: [],
+ devDeps: ["@mrgrain/jsii-struct-builder"],
depsUpgrade: false,
+ eslintOptions: {
+ dirs: ["src"],
+ ignorePatterns: ["src/generated/*.ts"], // ignore generated files
+ },
prettier: true,
prettierOptions: {
settings: {
@@ -28,11 +36,10 @@ const project = new awscdk.AwsCdkConstructLibrary({
proseWrap: ProseWrap.ALWAYS,
},
},
-
- // deps: [], /* Runtime dependencies of this module. */
- // description: undefined, /* The description is just a string that helps people understand the purpose of the package. */
- // devDeps: [], /* Build dependencies for this module. */
- // packageName: undefined, /* The "name" in package.json. */
});
+new ProjenStruct(project, { name: "RoleProps", filePath: "src/generated/IamRoleProps.ts" }).mixin(
+ Struct.fromFqn("aws-cdk-lib.aws_iam.RoleProps").omit("assumedBy").withoutDeprecated(),
+);
+
project.synth();
diff --git a/API.md b/API.md
index d76a1ed..4de29fb 100644
--- a/API.md
+++ b/API.md
@@ -1,14 +1,14 @@
# CircleCI OIDC
-This repository contains constructs to communicate between CircleCI and AWS via an Open ID Connect (OIDC) provider.
-The process is described in [this CircleCI blog post](https://circleci.com/blog/openid-connect-identity-tokens/).
+This repository contains constructs to communicate between CircleCI and AWS via an Open ID Connect (OIDC) provider. The
+process is described in [this CircleCI blog post](https://circleci.com/blog/openid-connect-identity-tokens/).
## Security Benefits
By using the OpenID Connect provider, you can communicate with AWS from CircleCI without saving static credentials
-(e.g., `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) in your CircleCI project settings or a context. Removing
-static credentials, especially in light of the early 2023 [breach](https://circleci.com/blog/jan-4-2023-incident-report/),
-is a best practice for security.
+(e.g., `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) in your CircleCI project settings or a context. Removing static
+credentials, especially in light of the early 2023 [breach](https://circleci.com/blog/jan-4-2023-incident-report/), is a
+best practice for security.
## Quick Start
@@ -25,46 +25,48 @@ yarn add @blimmer/cdk-circleci-oidc
Then, create the provider and role(s).
```typescript
-import { Stack, StackProps } from 'aws-cdk-lib';
-import { CircleCiOidcProvider, CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc';
-import { Construct } from 'constructs';
-import { ManagedPolicy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
-import { Bucket } from 'aws-cdk-lib/aws-s3';
+import { Stack, StackProps } from "aws-cdk-lib";
+import { CircleCiOidcProvider, CircleCiOidcRole } from "@blimmer/cdk-circleci-oidc";
+import { Construct } from "constructs";
+import { ManagedPolicy, PolicyStatement } from "aws-cdk-lib/aws-iam";
+import { Bucket } from "aws-cdk-lib/aws-s3";
export class CircleCiStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
- const provider = new CircleCiOidcProvider(this, 'OidcProvider', {
+ // The provider is only created _once per AWS account_. It might make sense to define this in a separate stack
+ // that defines more global resources. See below for how to use import the provider in stacks that don't define it.
+ const provider = new CircleCiOidcProvider(this, "OidcProvider", {
// Find your organization ID in the CircleCI dashboard under "Organization Settings"
- organizationId: '11111111-2222-3333-4444-555555555555',
+ organizationId: "11111111-2222-3333-4444-555555555555",
});
- const myCircleCiRole = new CircleCiOidcRole(this, 'MyCircleCiRole', {
- circleCiOidcProvider: provider,
+ const myCircleCiRole = new CircleCiOidcRole(this, "MyCircleCiRole", {
+ provider,
roleName: "MyCircleCiRole",
// Pass some managed policies to the role
- managedPolicies: [
- ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'),
- ],
- })
-
- // You can also access the role from the construct. This allows adding roles and using `grant` methods after the
- // construct has been created.
- myCircleCiRole.role.addToPolicy(new PolicyStatement({
- actions: ['s3:ListAllMyBuckets'],
- resources: ['*'],
- }));
-
- const bucket = new Bucket(this, 'MyBucket');
- bucket.grantRead(myCircleCiRole.role);
+ managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName("AmazonS3ReadOnlyAccess")],
+ });
+
+ // You can work with the CircleCI role like any other role
+ myCircleCiRole.addToPolicy(
+ new PolicyStatement({
+ actions: ["s3:ListAllMyBuckets"],
+ resources: ["*"],
+ }),
+ );
+
+ // Including using `.grant` convenience methods
+ const bucket = new Bucket(this, "MyBucket");
+ bucket.grantRead(myCircleCiRole);
}
}
```
-Now, in your `.circleci/config.yml` file, you can use the [AWS CLI Orb](https://circleci.com/developer/orbs/orb/circleci/aws-cli)
-to assume your new role.
+Now, in your `.circleci/config.yml` file, you can use the
+[AWS CLI Orb](https://circleci.com/developer/orbs/orb/circleci/aws-cli) to assume your new role.
```yaml
version: 2.1
@@ -87,56 +89,30 @@ jobs:
- checkout
# https://circleci.com/developer/orbs/orb/circleci/aws-cli#commands-setup
- aws-cli/setup:
- role_arn: 'arn:aws:iam::123456789101:role/MyCircleCiRole'
+ role_arn: "arn:aws:iam::123456789101:role/MyCircleCiRole"
- run:
name: List S3 Buckets
command: aws s3 ls
```
-## Cross Stack Usage
+## Usage in Stacks that Don't Define the Provider
-If you want to use the OIDC provider in another stack, you can use the `getProviderForExport` method.
+The `CircleCiOidcProvider` is only created **once per account**. You can use the
+`CircleCiOidcProvider.fromOrganizationId` method to import a previously created provider into any stack.
```typescript
-import { Stack, StackProps } from 'aws-cdk-lib';
-import { CircleCiOidcProvider } from '@blimmer/cdk-circleci-oidc';
-import { Construct } from 'constructs';
-
-export class CircleCiStack extends Stack {
- readonly circleCiOidcProvider: ManualCircleCiOidcProviderProps; // export for use in other stacks
+import { Stack, StackProps } from "aws-cdk-lib";
+import { CircleCiOidcRole, CircleCiOidcProvider } from "@blimmer/cdk-circleci-oidc";
+import { Construct } from "constructs";
+export class MyStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
- const provider = new CircleCiOidcProvider(this, 'OidcProvider', {
- // Find your organization ID in the CircleCI dashboard under "Organization Settings"
- organizationId: '11111111-2222-3333-4444-555555555555',
- });
-
- this.circleCiOidcProvider = provider.getProviderForExport(this.account);
- }
-}
-```
-
-```typescript
-import { Stack, StackProps } from 'aws-cdk-lib';
-import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc';
-import { Construct } from 'constructs';
-import type { CircleCiStack } from './CircleCiStack';
-
-interface ConsumingStackProps {
- circleci: CircleCi;
-}
-
-export class ConsumingStack extends Stack {
- constructor(scope: Construct, id: string, props: ConsumingStackProps) {
- super(scope, id, props);
- const { circleCiOidcProvider } = props.circleci;
-
- const myCircleCiRole = new CircleCiOidcRole(this, 'MyCircleCiRole', {
- circleCiOidcProvider,
+ const myCircleCiRole = new CircleCiOidcRole(this, "MyCircleCiRole", {
+ provider: CircleCiOidcProvider.fromOrganizationId(this, "11111111-2222-3333-4444-555555555555"),
roleName: "MyCircleCiRole",
- })
+ });
}
}
```
@@ -153,6 +129,11 @@ This package is available for Python as `cdk-circleci-oidc`.
pip install cdk-circleci-oidc
```
+## Upgrading Between Major Versions
+
+The API can be expected to change between major versions. Please consult the [UPGRADING docs](/UPGRADING.md.md) for for
+information.
+
## Contributing
Contributions, issues, and feedback are welcome!
@@ -163,11 +144,15 @@ Contributions, issues, and feedback are welcome!
### CircleCiOidcProvider
+- *Implements:* ICircleCiOidcProvider
+
This construct creates a CircleCI ODIC provider to allow AWS access from CircleCI jobs.
You'll need to instantiate
this construct once per AWS account you want to use CircleCI OIDC with.
+You can import a existing provider using `CircleCiOidcProvider.fromOrganizationId`.
+
To create a role that can be assumed by CircleCI jobs, use the `CircleCiOidcRole` construct.
#### Initializers
@@ -209,7 +194,22 @@ new CircleCiOidcProvider(scope: Construct, id: string, props: CircleCiOidcProvid
| **Name** | **Description** |
| --- | --- |
| toString
| Returns a string representation of this construct. |
-| getProviderForExport
| *No description.* |
+| overrideLogicalId
| Overrides the auto-generated logical ID with a specific ID. |
+| addDeletionOverride
| Syntactic sugar for `addOverride(path, undefined)`. |
+| addDependency
| Indicates that this resource depends on another resource and cannot be provisioned unless the other resource has been successfully provisioned. |
+| addDependsOn
| Indicates that this resource depends on another resource and cannot be provisioned unless the other resource has been successfully provisioned. |
+| addMetadata
| Add a value to the CloudFormation Resource Metadata. |
+| addOverride
| Adds an override to the synthesized CloudFormation resource. |
+| addPropertyDeletionOverride
| Adds an override that deletes the value of a property from the resource definition. |
+| addPropertyOverride
| Adds an override to a resource property. |
+| applyRemovalPolicy
| Sets the deletion policy of the resource based on the removal policy specified. |
+| getAtt
| Returns a token for an runtime attribute of this resource. |
+| getMetadata
| Retrieve a value value from the CloudFormation Resource Metadata. |
+| obtainDependencies
| Retrieves an array of resources this resource depends on. |
+| obtainResourceDependencies
| Get a shallow copy of dependencies between this resource and other resources in the same stack. |
+| removeDependency
| Indicates that this resource no longer depends on another resource. |
+| replaceDependency
| Replaces one dependency with another. |
+| inspect
| Examines the CloudFormation resource and discloses attributes. |
---
@@ -221,406 +221,1847 @@ public toString(): string
Returns a string representation of this construct.
-##### `getProviderForExport`
+##### `overrideLogicalId`
```typescript
-public getProviderForExport(accountId: string, importName?: string): ManualCircleCiOidcProviderProps
+public overrideLogicalId(newLogicalId: string): void
```
-###### `accountId`Required
+Overrides the auto-generated logical ID with a specific ID.
+
+###### `newLogicalId`Required
- *Type:* string
+The new logical ID to use for this stack element.
+
---
-###### `importName`Optional
+##### `addDeletionOverride`
-- *Type:* string
+```typescript
+public addDeletionOverride(path: string): void
+```
----
+Syntactic sugar for `addOverride(path, undefined)`.
-#### Static Functions
+###### `path`Required
-| **Name** | **Description** |
-| --- | --- |
-| isConstruct
| Checks if `x` is a construct. |
+- *Type:* string
+
+The path of the value to delete.
---
-##### ~~`isConstruct`~~
+##### `addDependency`
```typescript
-import { CircleCiOidcProvider } from '@blimmer/cdk-circleci-oidc'
-
-CircleCiOidcProvider.isConstruct(x: any)
+public addDependency(target: CfnResource): void
```
-Checks if `x` is a construct.
+Indicates that this resource depends on another resource and cannot be provisioned unless the other resource has been successfully provisioned.
-###### `x`Required
+This can be used for resources across stacks (or nested stack) boundaries
+and the dependency will automatically be transferred to the relevant scope.
-- *Type:* any
+###### `target`Required
-Any object.
+- *Type:* aws-cdk-lib.CfnResource
---
-#### Properties
+##### ~~`addDependsOn`~~
-| **Name** | **Type** | **Description** |
-| --- | --- | --- |
-| node
| constructs.Node
| The tree node. |
-| organizationId
| string
| *No description.* |
-| provider
| aws-cdk-lib.aws_iam.CfnOIDCProvider
| *No description.* |
+```typescript
+public addDependsOn(target: CfnResource): void
+```
+
+Indicates that this resource depends on another resource and cannot be provisioned unless the other resource has been successfully provisioned.
+
+###### `target`Required
+
+- *Type:* aws-cdk-lib.CfnResource
---
-##### `node`Required
+##### `addMetadata`
```typescript
-public readonly node: Node;
+public addMetadata(key: string, value: any): void
```
-- *Type:* constructs.Node
+Add a value to the CloudFormation Resource Metadata.
-The tree node.
+> [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html
----
+Note that this is a different set of metadata from CDK node metadata; this
+metadata ends up in the stack template under the resource, whereas CDK
+node metadata ends up in the Cloud Assembly.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html
-##### `organizationId`Required
+Note that this is a different set of metadata from CDK node metadata; this
+metadata ends up in the stack template under the resource, whereas CDK
+node metadata ends up in the Cloud Assembly.)
-```typescript
-public readonly organizationId: string;
-```
+###### `key`Required
- *Type:* string
---
-##### `provider`Required
-
-```typescript
-public readonly provider: CfnOIDCProvider;
-```
+###### `value`Required
-- *Type:* aws-cdk-lib.aws_iam.CfnOIDCProvider
+- *Type:* any
---
+##### `addOverride`
-### CircleCiOidcRole
+```typescript
+public addOverride(path: string, value: any): void
+```
-This construct creates a CircleCI ODIC provider to allow AWS access from CircleCI jobs.
+Adds an override to the synthesized CloudFormation resource.
-You'll need to instantiate
-this construct once per AWS account you want to use CircleCI OIDC with.
+To add a
+property override, either use `addPropertyOverride` or prefix `path` with
+"Properties." (i.e. `Properties.TopicName`).
-To create a role that can be assumed by CircleCI jobs, use the `CircleCiOidcRole` construct.
+If the override is nested, separate each nested level using a dot (.) in the path parameter.
+If there is an array as part of the nesting, specify the index in the path.
-#### Initializers
+To include a literal `.` in the property name, prefix with a `\`. In most
+programming languages you will need to write this as `"\\."` because the
+`\` itself will need to be escaped.
+For example,
```typescript
-import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
-
-new CircleCiOidcRole(scope: Construct, id: string, props: CircleCiOidcRoleProps)
+cfnResource.addOverride('Properties.GlobalSecondaryIndexes.0.Projection.NonKeyAttributes', ['myattribute']);
+cfnResource.addOverride('Properties.GlobalSecondaryIndexes.1.ProjectionType', 'INCLUDE');
+```
+would add the overrides
+```json
+"Properties": {
+ "GlobalSecondaryIndexes": [
+ {
+ "Projection": {
+ "NonKeyAttributes": [ "myattribute" ]
+ ...
+ }
+ ...
+ },
+ {
+ "ProjectionType": "INCLUDE"
+ ...
+ },
+ ]
+ ...
+}
```
-| **Name** | **Type** | **Description** |
-| --- | --- | --- |
-| scope
| constructs.Construct
| *No description.* |
-| id
| string
| *No description.* |
-| props
| CircleCiOidcRoleProps
| *No description.* |
-
----
-
-##### `scope`Required
+The `value` argument to `addOverride` will not be processed or translated
+in any way. Pass raw JSON values in here with the correct capitalization
+for CloudFormation. If you pass CDK classes or structs, they will be
+rendered with lowercased key names, and CloudFormation will reject the
+template.
-- *Type:* constructs.Construct
+###### `path`Required
----
+- *Type:* string
-##### `id`Required
+The path of the property, you can use dot notation to override values in complex types.
-- *Type:* string
+Any intermediate keys
+will be created as needed.
---
-##### `props`Required
-
-- *Type:* CircleCiOidcRoleProps
+###### `value`Required
----
+- *Type:* any
-#### Methods
+The value.
-| **Name** | **Description** |
-| --- | --- |
-| toString
| Returns a string representation of this construct. |
+Could be primitive or complex.
---
-##### `toString`
+##### `addPropertyDeletionOverride`
```typescript
-public toString(): string
+public addPropertyDeletionOverride(propertyPath: string): void
```
-Returns a string representation of this construct.
+Adds an override that deletes the value of a property from the resource definition.
-#### Static Functions
+###### `propertyPath`Required
-| **Name** | **Description** |
-| --- | --- |
-| isConstruct
| Checks if `x` is a construct. |
+- *Type:* string
+
+The path to the property.
---
-##### ~~`isConstruct`~~
+##### `addPropertyOverride`
```typescript
-import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
-
-CircleCiOidcRole.isConstruct(x: any)
+public addPropertyOverride(propertyPath: string, value: any): void
```
-Checks if `x` is a construct.
+Adds an override to a resource property.
-###### `x`Required
+Syntactic sugar for `addOverride("Properties.<...>", value)`.
-- *Type:* any
+###### `propertyPath`Required
-Any object.
+- *Type:* string
+
+The path of the property.
---
-#### Properties
+###### `value`Required
-| **Name** | **Type** | **Description** |
-| --- | --- | --- |
-| node
| constructs.Node
| The tree node. |
-| role
| aws-cdk-lib.aws_iam.Role
| *No description.* |
+- *Type:* any
+
+The value.
---
-##### `node`Required
+##### `applyRemovalPolicy`
```typescript
-public readonly node: Node;
+public applyRemovalPolicy(policy?: RemovalPolicy, options?: RemovalPolicyOptions): void
```
-- *Type:* constructs.Node
+Sets the deletion policy of the resource based on the removal policy specified.
-The tree node.
+The Removal Policy controls what happens to this resource when it stops
+being managed by CloudFormation, either because you've removed it from the
+CDK application or because you've made a change that requires the resource
+to be replaced.
----
+The resource can be deleted (`RemovalPolicy.DESTROY`), or left in your AWS
+account for data recovery and cleanup later (`RemovalPolicy.RETAIN`). In some
+cases, a snapshot can be taken of the resource prior to deletion
+(`RemovalPolicy.SNAPSHOT`). A list of resources that support this policy
+can be found in the following link:
-##### `role`Required
+> [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html#aws-attribute-deletionpolicy-options](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html#aws-attribute-deletionpolicy-options)
-```typescript
-public readonly role: Role;
-```
+###### `policy`Optional
-- *Type:* aws-cdk-lib.aws_iam.Role
+- *Type:* aws-cdk-lib.RemovalPolicy
---
+###### `options`Optional
-## Structs
+- *Type:* aws-cdk-lib.RemovalPolicyOptions
-### CircleCiOidcProviderProps
+---
-#### Initializer
+##### `getAtt`
```typescript
-import { CircleCiOidcProviderProps } from '@blimmer/cdk-circleci-oidc'
-
-const circleCiOidcProviderProps: CircleCiOidcProviderProps = { ... }
+public getAtt(attributeName: string, typeHint?: ResolutionTypeHint): Reference
```
-#### Properties
+Returns a token for an runtime attribute of this resource.
-| **Name** | **Type** | **Description** |
-| --- | --- | --- |
-| organizationId
| string
| The ID of your CircleCI organization. |
-| circleCiOidcThumbprints
| string[]
| The OIDC thumbprints used by the provider. |
+Ideally, use generated attribute accessors (e.g. `resource.arn`), but this can be used for future compatibility
+in case there is no generated attribute.
----
+###### `attributeName`Required
-##### `organizationId`Required
+- *Type:* string
-```typescript
-public readonly organizationId: string;
-```
+The name of the attribute.
-- *Type:* string
+---
-The ID of your CircleCI organization.
+###### `typeHint`Optional
-This is typically in a UUID format. You can find this ID in the CircleCI
-dashboard UI under the "Organization Settings" tab.
+- *Type:* aws-cdk-lib.ResolutionTypeHint
---
-##### `circleCiOidcThumbprints`Optional
+##### `getMetadata`
```typescript
-public readonly circleCiOidcThumbprints: string[];
+public getMetadata(key: string): any
```
-- *Type:* string[]
+Retrieve a value value from the CloudFormation Resource Metadata.
-The OIDC thumbprints used by the provider.
+> [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html
-You should not need to provide this value unless CircleCI suddenly
-rotates their OIDC thumbprints (e.g., in response to a security incident).
+Note that this is a different set of metadata from CDK node metadata; this
+metadata ends up in the stack template under the resource, whereas CDK
+node metadata ends up in the Cloud Assembly.](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html
-If you do need to generate this thumbprint, you can follow the instructions here:
-https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
+Note that this is a different set of metadata from CDK node metadata; this
+metadata ends up in the stack template under the resource, whereas CDK
+node metadata ends up in the Cloud Assembly.)
----
+###### `key`Required
-### CircleCiOidcRoleProps
+- *Type:* string
-#### Initializer
+---
-```typescript
-import { CircleCiOidcRoleProps } from '@blimmer/cdk-circleci-oidc'
+##### `obtainDependencies`
-const circleCiOidcRoleProps: CircleCiOidcRoleProps = { ... }
+```typescript
+public obtainDependencies(): Stack | CfnResource[]
```
-#### Properties
-
-| **Name** | **Type** | **Description** |
-| --- | --- | --- |
-| circleCiOidcProvider
| ManualCircleCiOidcProviderProps \| CircleCiOidcProvider
| *No description.* |
-| circleCiProjectIds
| string[]
| Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role. |
-| description
| string
| *No description.* |
-| inlinePolicies
| {[ key: string ]: aws-cdk-lib.aws_iam.PolicyDocument}
| *No description.* |
-| managedPolicies
| aws-cdk-lib.aws_iam.IManagedPolicy[]
| *No description.* |
-| roleName
| string
| You can pass an explicit role name if you'd like, since you need to reference the Role ARN within your CircleCI configuration. |
+Retrieves an array of resources this resource depends on.
----
+This assembles dependencies on resources across stacks (including nested stacks)
+automatically.
-##### `circleCiOidcProvider`Required
+##### `obtainResourceDependencies`
```typescript
-public readonly circleCiOidcProvider: ManualCircleCiOidcProviderProps | CircleCiOidcProvider;
+public obtainResourceDependencies(): CfnResource[]
```
-- *Type:* ManualCircleCiOidcProviderProps | CircleCiOidcProvider
-
----
+Get a shallow copy of dependencies between this resource and other resources in the same stack.
-##### `circleCiProjectIds`Optional
+##### `removeDependency`
```typescript
-public readonly circleCiProjectIds: string[];
+public removeDependency(target: CfnResource): void
```
-- *Type:* string[]
-- *Default:* All CircleCI projects in the provider's organization
+Indicates that this resource no longer depends on another resource.
-Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role.
+This can be used for resources across stacks (including nested stacks)
+and the dependency will automatically be removed from the relevant scope.
-If you don't provide this
-value, the role will be allowed to be assumed by any CircleCI project in your organization. You can find a
-project's ID in the CircleCI dashboard UI under the "Project Settings" tab. It's usually in a UUID format.
+###### `target`Required
+
+- *Type:* aws-cdk-lib.CfnResource
---
-##### `description`Optional
+##### `replaceDependency`
```typescript
-public readonly description: string;
+public replaceDependency(target: CfnResource, newTarget: CfnResource): void
```
-- *Type:* string
+Replaces one dependency with another.
+
+###### `target`Required
+
+- *Type:* aws-cdk-lib.CfnResource
+
+The dependency to replace.
---
-##### `inlinePolicies`Optional
+###### `newTarget`Required
-```typescript
-public readonly inlinePolicies: {[ key: string ]: PolicyDocument};
-```
+- *Type:* aws-cdk-lib.CfnResource
-- *Type:* {[ key: string ]: aws-cdk-lib.aws_iam.PolicyDocument}
+The new dependency to add.
---
-##### `managedPolicies`Optional
+##### `inspect`
```typescript
-public readonly managedPolicies: IManagedPolicy[];
+public inspect(inspector: TreeInspector): void
```
-- *Type:* aws-cdk-lib.aws_iam.IManagedPolicy[]
+Examines the CloudFormation resource and discloses attributes.
+
+###### `inspector`Required
+
+- *Type:* aws-cdk-lib.TreeInspector
+
+tree inspector to collect and process attributes.
---
-##### `roleName`Optional
+#### Static Functions
+
+| **Name** | **Description** |
+| --- | --- |
+| isConstruct
| Checks if `x` is a construct. |
+| isCfnElement
| Returns `true` if a construct is a stack element (i.e. part of the synthesized cloudformation template). |
+| isCfnResource
| Check whether the given construct is a CfnResource. |
+| fromOrganizationId
| *No description.* |
+
+---
+
+##### ~~`isConstruct`~~
```typescript
-public readonly roleName: string;
+import { CircleCiOidcProvider } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcProvider.isConstruct(x: any)
```
-- *Type:* string
-- *Default:* CloudFormation will auto-generate you a role name
+Checks if `x` is a construct.
-You can pass an explicit role name if you'd like, since you need to reference the Role ARN within your CircleCI configuration.
+###### `x`Required
----
+- *Type:* any
-### ManualCircleCiOidcProviderProps
+Any object.
-If you're using the {@link CircleCiOidcProvider} construct, pass it instead of these manually-defined props.
+---
-#### Initializer
+##### `isCfnElement`
```typescript
-import { ManualCircleCiOidcProviderProps } from '@blimmer/cdk-circleci-oidc'
+import { CircleCiOidcProvider } from '@blimmer/cdk-circleci-oidc'
-const manualCircleCiOidcProviderProps: ManualCircleCiOidcProviderProps = { ... }
+CircleCiOidcProvider.isCfnElement(x: any)
```
-#### Properties
+Returns `true` if a construct is a stack element (i.e. part of the synthesized cloudformation template).
-| **Name** | **Type** | **Description** |
-| --- | --- | --- |
-| organizationId
| string
| The ID of your CircleCI organization. |
-| provider
| aws-cdk-lib.aws_iam.IOpenIdConnectProvider
| The CircleCI OIDC provider. |
+Uses duck-typing instead of `instanceof` to allow stack elements from different
+versions of this library to be included in the same stack.
+
+###### `x`Required
+
+- *Type:* any
---
-##### `organizationId`Required
+##### `isCfnResource`
```typescript
-public readonly organizationId: string;
+import { CircleCiOidcProvider } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcProvider.isCfnResource(construct: IConstruct)
```
-- *Type:* string
+Check whether the given construct is a CfnResource.
-The ID of your CircleCI organization.
+###### `construct`Required
-This is typically in a UUID format. You can find this ID in the CircleCI
-dashboard UI under the "Organization Settings" tab.
+- *Type:* constructs.IConstruct
---
-##### `provider`Required
+##### `fromOrganizationId`
```typescript
-public readonly provider: IOpenIdConnectProvider;
+import { CircleCiOidcProvider } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcProvider.fromOrganizationId(scope: Construct, organizationId: string)
```
-- *Type:* aws-cdk-lib.aws_iam.IOpenIdConnectProvider
+###### `scope`Required
+
+- *Type:* constructs.Construct
+
+---
+
+###### `organizationId`Required
+
+- *Type:* string
+
+---
-The CircleCI OIDC provider.
+#### Properties
-You can either manually create it or import it.
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| node
| constructs.Node
| The tree node. |
+| creationStack
| string[]
| *No description.* |
+| logicalId
| string
| The logical ID for this CloudFormation stack element. |
+| stack
| aws-cdk-lib.Stack
| The stack in which this element is defined. |
+| ref
| string
| Return a string that will be resolved to a CloudFormation `{ Ref }` for this element. |
+| cfnOptions
| aws-cdk-lib.ICfnResourceOptions
| Options for this resource, such as condition, update policy etc. |
+| cfnResourceType
| string
| AWS resource type. |
+| attrArn
| string
| Returns the Amazon Resource Name (ARN) for the specified `AWS::IAM::OIDCProvider` resource. |
+| tags
| aws-cdk-lib.TagManager
| A list of tags that are attached to the specified IAM OIDC provider. |
+| thumbprintList
| string[]
| A list of certificate thumbprints that are associated with the specified IAM OIDC provider resource object. |
+| clientIdList
| string[]
| A list of client IDs (also known as audiences) that are associated with the specified IAM OIDC provider resource object. |
+| url
| string
| The URL that the IAM OIDC provider resource object is associated with. |
+| arn
| string
| *No description.* |
+| organizationId
| string
| *No description.* |
---
+##### `node`Required
+```typescript
+public readonly node: Node;
+```
+
+- *Type:* constructs.Node
+
+The tree node.
+
+---
+
+##### `creationStack`Required
+
+```typescript
+public readonly creationStack: string[];
+```
+
+- *Type:* string[]
+
+---
+
+##### `logicalId`Required
+
+```typescript
+public readonly logicalId: string;
+```
+
+- *Type:* string
+
+The logical ID for this CloudFormation stack element.
+
+The logical ID of the element
+is calculated from the path of the resource node in the construct tree.
+
+To override this value, use `overrideLogicalId(newLogicalId)`.
+
+---
+
+##### `stack`Required
+
+```typescript
+public readonly stack: Stack;
+```
+
+- *Type:* aws-cdk-lib.Stack
+
+The stack in which this element is defined.
+
+CfnElements must be defined within a stack scope (directly or indirectly).
+
+---
+
+##### `ref`Required
+
+```typescript
+public readonly ref: string;
+```
+
+- *Type:* string
+
+Return a string that will be resolved to a CloudFormation `{ Ref }` for this element.
+
+If, by any chance, the intrinsic reference of a resource is not a string, you could
+coerce it to an IResolvable through `Lazy.any({ produce: resource.ref })`.
+
+---
+
+##### `cfnOptions`Required
+
+```typescript
+public readonly cfnOptions: ICfnResourceOptions;
+```
+
+- *Type:* aws-cdk-lib.ICfnResourceOptions
+
+Options for this resource, such as condition, update policy etc.
+
+---
+
+##### `cfnResourceType`Required
+
+```typescript
+public readonly cfnResourceType: string;
+```
+
+- *Type:* string
+
+AWS resource type.
+
+---
+
+##### `attrArn`Required
+
+```typescript
+public readonly attrArn: string;
+```
+
+- *Type:* string
+
+Returns the Amazon Resource Name (ARN) for the specified `AWS::IAM::OIDCProvider` resource.
+
+---
+
+##### `tags`Required
+
+```typescript
+public readonly tags: TagManager;
+```
+
+- *Type:* aws-cdk-lib.TagManager
+
+A list of tags that are attached to the specified IAM OIDC provider.
+
+The returned list of tags is sorted by tag key. For more information about tagging, see [Tagging IAM resources](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) in the *IAM User Guide* .
+
+> [http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-tags](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-tags)
+
+---
+
+##### `thumbprintList`Required
+
+```typescript
+public readonly thumbprintList: string[];
+```
+
+- *Type:* string[]
+
+A list of certificate thumbprints that are associated with the specified IAM OIDC provider resource object.
+
+For more information, see [CreateOpenIDConnectProvider](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateOpenIDConnectProvider.html) .
+
+> [http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-thumbprintlist](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-thumbprintlist)
+
+---
+
+##### `clientIdList`Optional
+
+```typescript
+public readonly clientIdList: string[];
+```
+
+- *Type:* string[]
+
+A list of client IDs (also known as audiences) that are associated with the specified IAM OIDC provider resource object.
+
+For more information, see [CreateOpenIDConnectProvider](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateOpenIDConnectProvider.html) .
+
+> [http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-clientidlist](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-clientidlist)
+
+---
+
+##### `url`Optional
+
+```typescript
+public readonly url: string;
+```
+
+- *Type:* string
+
+The URL that the IAM OIDC provider resource object is associated with.
+
+For more information, see [CreateOpenIDConnectProvider](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateOpenIDConnectProvider.html) .
+
+> [http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-url](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-oidcprovider.html#cfn-iam-oidcprovider-url)
+
+---
+
+##### `arn`Required
+
+```typescript
+public readonly arn: string;
+```
+
+- *Type:* string
+
+---
+
+##### `organizationId`Required
+
+```typescript
+public readonly organizationId: string;
+```
+
+- *Type:* string
+
+---
+
+#### Constants
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| CFN_RESOURCE_TYPE_NAME
| string
| The CloudFormation resource type name for this resource class. |
+
+---
+
+##### `CFN_RESOURCE_TYPE_NAME`Required
+
+```typescript
+public readonly CFN_RESOURCE_TYPE_NAME: string;
+```
+
+- *Type:* string
+
+The CloudFormation resource type name for this resource class.
+
+---
+
+### CircleCiOidcRole
+
+Define an IAM Role that can be assumed by a CircleCI Job via the CircleCI OpenID Connect Identity Provider.
+
+#### Initializers
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+new CircleCiOidcRole(scope: Construct, id: string, props: CircleCiOidcRoleProps)
+```
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| scope
| constructs.Construct
| *No description.* |
+| id
| string
| *No description.* |
+| props
| CircleCiOidcRoleProps
| *No description.* |
+
+---
+
+##### `scope`Required
+
+- *Type:* constructs.Construct
+
+---
+
+##### `id`Required
+
+- *Type:* string
+
+---
+
+##### `props`Required
+
+- *Type:* CircleCiOidcRoleProps
+
+---
+
+#### Methods
+
+| **Name** | **Description** |
+| --- | --- |
+| toString
| Returns a string representation of this construct. |
+| applyRemovalPolicy
| Apply the given removal policy to this resource. |
+| addManagedPolicy
| Attaches a managed policy to this role. |
+| addToPolicy
| Add to the policy of this principal. |
+| addToPrincipalPolicy
| Adds a permission to the role's default policy document. |
+| attachInlinePolicy
| Attaches a policy to this role. |
+| grant
| Grant the actions defined in actions to the identity Principal on this resource. |
+| grantAssumeRole
| Grant permissions to the given principal to assume this role. |
+| grantPassRole
| Grant permissions to the given principal to pass this role. |
+| withoutPolicyUpdates
| Return a copy of this Role object whose Policies will not be updated. |
+
+---
+
+##### `toString`
+
+```typescript
+public toString(): string
+```
+
+Returns a string representation of this construct.
+
+##### `applyRemovalPolicy`
+
+```typescript
+public applyRemovalPolicy(policy: RemovalPolicy): void
+```
+
+Apply the given removal policy to this resource.
+
+The Removal Policy controls what happens to this resource when it stops
+being managed by CloudFormation, either because you've removed it from the
+CDK application or because you've made a change that requires the resource
+to be replaced.
+
+The resource can be deleted (`RemovalPolicy.DESTROY`), or left in your AWS
+account for data recovery and cleanup later (`RemovalPolicy.RETAIN`).
+
+###### `policy`Required
+
+- *Type:* aws-cdk-lib.RemovalPolicy
+
+---
+
+##### `addManagedPolicy`
+
+```typescript
+public addManagedPolicy(policy: IManagedPolicy): void
+```
+
+Attaches a managed policy to this role.
+
+###### `policy`Required
+
+- *Type:* aws-cdk-lib.aws_iam.IManagedPolicy
+
+The the managed policy to attach.
+
+---
+
+##### `addToPolicy`
+
+```typescript
+public addToPolicy(statement: PolicyStatement): boolean
+```
+
+Add to the policy of this principal.
+
+###### `statement`Required
+
+- *Type:* aws-cdk-lib.aws_iam.PolicyStatement
+
+---
+
+##### `addToPrincipalPolicy`
+
+```typescript
+public addToPrincipalPolicy(statement: PolicyStatement): AddToPrincipalPolicyResult
+```
+
+Adds a permission to the role's default policy document.
+
+If there is no default policy attached to this role, it will be created.
+
+###### `statement`Required
+
+- *Type:* aws-cdk-lib.aws_iam.PolicyStatement
+
+The permission statement to add to the policy document.
+
+---
+
+##### `attachInlinePolicy`
+
+```typescript
+public attachInlinePolicy(policy: Policy): void
+```
+
+Attaches a policy to this role.
+
+###### `policy`Required
+
+- *Type:* aws-cdk-lib.aws_iam.Policy
+
+The policy to attach.
+
+---
+
+##### `grant`
+
+```typescript
+public grant(grantee: IPrincipal, actions: string): Grant
+```
+
+Grant the actions defined in actions to the identity Principal on this resource.
+
+###### `grantee`Required
+
+- *Type:* aws-cdk-lib.aws_iam.IPrincipal
+
+---
+
+###### `actions`Required
+
+- *Type:* string
+
+---
+
+##### `grantAssumeRole`
+
+```typescript
+public grantAssumeRole(identity: IPrincipal): Grant
+```
+
+Grant permissions to the given principal to assume this role.
+
+###### `identity`Required
+
+- *Type:* aws-cdk-lib.aws_iam.IPrincipal
+
+---
+
+##### `grantPassRole`
+
+```typescript
+public grantPassRole(identity: IPrincipal): Grant
+```
+
+Grant permissions to the given principal to pass this role.
+
+###### `identity`Required
+
+- *Type:* aws-cdk-lib.aws_iam.IPrincipal
+
+---
+
+##### `withoutPolicyUpdates`
+
+```typescript
+public withoutPolicyUpdates(options?: WithoutPolicyUpdatesOptions): IRole
+```
+
+Return a copy of this Role object whose Policies will not be updated.
+
+Use the object returned by this method if you want this Role to be used by
+a construct without it automatically updating the Role's Policies.
+
+If you do, you are responsible for adding the correct statements to the
+Role's policies yourself.
+
+###### `options`Optional
+
+- *Type:* aws-cdk-lib.aws_iam.WithoutPolicyUpdatesOptions
+
+---
+
+#### Static Functions
+
+| **Name** | **Description** |
+| --- | --- |
+| isConstruct
| Checks if `x` is a construct. |
+| isOwnedResource
| Returns true if the construct was created by CDK, and false otherwise. |
+| isResource
| Check whether the given construct is a Resource. |
+| customizeRoles
| Customize the creation of IAM roles within the given scope. |
+| fromRoleArn
| Import an external role by ARN. |
+| fromRoleName
| Import an external role by name. |
+| isRole
| Return whether the given object is a Role. |
+
+---
+
+##### ~~`isConstruct`~~
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcRole.isConstruct(x: any)
+```
+
+Checks if `x` is a construct.
+
+###### `x`Required
+
+- *Type:* any
+
+Any object.
+
+---
+
+##### `isOwnedResource`
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcRole.isOwnedResource(construct: IConstruct)
+```
+
+Returns true if the construct was created by CDK, and false otherwise.
+
+###### `construct`Required
+
+- *Type:* constructs.IConstruct
+
+---
+
+##### `isResource`
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcRole.isResource(construct: IConstruct)
+```
+
+Check whether the given construct is a Resource.
+
+###### `construct`Required
+
+- *Type:* constructs.IConstruct
+
+---
+
+##### `customizeRoles`
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcRole.customizeRoles(scope: Construct, options?: CustomizeRolesOptions)
+```
+
+Customize the creation of IAM roles within the given scope.
+
+It is recommended that you **do not** use this method and instead allow
+CDK to manage role creation. This should only be used
+in environments where CDK applications are not allowed to created IAM roles.
+
+This can be used to prevent the CDK application from creating roles
+within the given scope and instead replace the references to the roles with
+precreated role names. A report will be synthesized in the cloud assembly (i.e. cdk.out)
+that will contain the list of IAM roles that would have been created along with the
+IAM policy statements that the role should contain. This report can then be used
+to create the IAM roles outside of CDK and then the created role names can be provided
+in `usePrecreatedRoles`.
+
+*Example*
+
+```typescript
+declare const app: App;
+Role.customizeRoles(app, {
+ usePrecreatedRoles: {
+ 'ConstructPath/To/Role': 'my-precreated-role-name',
+ },
+});
+```
+
+
+###### `scope`Required
+
+- *Type:* constructs.Construct
+
+construct scope to customize role creation.
+
+---
+
+###### `options`Optional
+
+- *Type:* aws-cdk-lib.aws_iam.CustomizeRolesOptions
+
+options for configuring role creation.
+
+---
+
+##### `fromRoleArn`
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcRole.fromRoleArn(scope: Construct, id: string, roleArn: string, options?: FromRoleArnOptions)
+```
+
+Import an external role by ARN.
+
+If the imported Role ARN is a Token (such as a
+`CfnParameter.valueAsString` or a `Fn.importValue()`) *and* the referenced
+role has a `path` (like `arn:...:role/AdminRoles/Alice`), the
+`roleName` property will not resolve to the correct value. Instead it
+will resolve to the first path component. We unfortunately cannot express
+the correct calculation of the full path name as a CloudFormation
+expression. In this scenario the Role ARN should be supplied without the
+`path` in order to resolve the correct role resource.
+
+###### `scope`Required
+
+- *Type:* constructs.Construct
+
+construct scope.
+
+---
+
+###### `id`Required
+
+- *Type:* string
+
+construct id.
+
+---
+
+###### `roleArn`Required
+
+- *Type:* string
+
+the ARN of the role to import.
+
+---
+
+###### `options`Optional
+
+- *Type:* aws-cdk-lib.aws_iam.FromRoleArnOptions
+
+allow customizing the behavior of the returned role.
+
+---
+
+##### `fromRoleName`
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcRole.fromRoleName(scope: Construct, id: string, roleName: string, options?: FromRoleNameOptions)
+```
+
+Import an external role by name.
+
+The imported role is assumed to exist in the same account as the account
+the scope's containing Stack is being deployed to.
+
+###### `scope`Required
+
+- *Type:* constructs.Construct
+
+construct scope.
+
+---
+
+###### `id`Required
+
+- *Type:* string
+
+construct id.
+
+---
+
+###### `roleName`Required
+
+- *Type:* string
+
+the name of the role to import.
+
+---
+
+###### `options`Optional
+
+- *Type:* aws-cdk-lib.aws_iam.FromRoleNameOptions
+
+allow customizing the behavior of the returned role.
+
+---
+
+##### `isRole`
+
+```typescript
+import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc'
+
+CircleCiOidcRole.isRole(x: any)
+```
+
+Return whether the given object is a Role.
+
+###### `x`Required
+
+- *Type:* any
+
+---
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| node
| constructs.Node
| The tree node. |
+| env
| aws-cdk-lib.ResourceEnvironment
| The environment this resource belongs to. |
+| stack
| aws-cdk-lib.Stack
| The stack in which this resource is defined. |
+| assumeRoleAction
| string
| When this Principal is used in an AssumeRole policy, the action to use. |
+| grantPrincipal
| aws-cdk-lib.aws_iam.IPrincipal
| The principal to grant permissions to. |
+| policyFragment
| aws-cdk-lib.aws_iam.PrincipalPolicyFragment
| Returns the role. |
+| roleArn
| string
| Returns the ARN of this role. |
+| roleId
| string
| Returns the stable and unique string identifying the role. |
+| roleName
| string
| Returns the name of the role. |
+| assumeRolePolicy
| aws-cdk-lib.aws_iam.PolicyDocument
| The assume role policy document associated with this role. |
+| permissionsBoundary
| aws-cdk-lib.aws_iam.IManagedPolicy
| Returns the permissions boundary attached to this role. |
+| principalAccount
| string
| The AWS account ID of this principal. |
+
+---
+
+##### `node`Required
+
+```typescript
+public readonly node: Node;
+```
+
+- *Type:* constructs.Node
+
+The tree node.
+
+---
+
+##### `env`Required
+
+```typescript
+public readonly env: ResourceEnvironment;
+```
+
+- *Type:* aws-cdk-lib.ResourceEnvironment
+
+The environment this resource belongs to.
+
+For resources that are created and managed by the CDK
+(generally, those created by creating new class instances like Role, Bucket, etc.),
+this is always the same as the environment of the stack they belong to;
+however, for imported resources
+(those obtained from static methods like fromRoleArn, fromBucketName, etc.),
+that might be different than the stack they were imported into.
+
+---
+
+##### `stack`Required
+
+```typescript
+public readonly stack: Stack;
+```
+
+- *Type:* aws-cdk-lib.Stack
+
+The stack in which this resource is defined.
+
+---
+
+##### `assumeRoleAction`Required
+
+```typescript
+public readonly assumeRoleAction: string;
+```
+
+- *Type:* string
+
+When this Principal is used in an AssumeRole policy, the action to use.
+
+---
+
+##### `grantPrincipal`Required
+
+```typescript
+public readonly grantPrincipal: IPrincipal;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IPrincipal
+
+The principal to grant permissions to.
+
+---
+
+##### `policyFragment`Required
+
+```typescript
+public readonly policyFragment: PrincipalPolicyFragment;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.PrincipalPolicyFragment
+
+Returns the role.
+
+---
+
+##### `roleArn`Required
+
+```typescript
+public readonly roleArn: string;
+```
+
+- *Type:* string
+
+Returns the ARN of this role.
+
+---
+
+##### `roleId`Required
+
+```typescript
+public readonly roleId: string;
+```
+
+- *Type:* string
+
+Returns the stable and unique string identifying the role.
+
+For example,
+AIDAJQABLZS4A3QDU576Q.
+
+---
+
+##### `roleName`Required
+
+```typescript
+public readonly roleName: string;
+```
+
+- *Type:* string
+
+Returns the name of the role.
+
+---
+
+##### `assumeRolePolicy`Optional
+
+```typescript
+public readonly assumeRolePolicy: PolicyDocument;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.PolicyDocument
+
+The assume role policy document associated with this role.
+
+---
+
+##### `permissionsBoundary`Optional
+
+```typescript
+public readonly permissionsBoundary: IManagedPolicy;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IManagedPolicy
+
+Returns the permissions boundary attached to this role.
+
+---
+
+##### `principalAccount`Optional
+
+```typescript
+public readonly principalAccount: string;
+```
+
+- *Type:* string
+
+The AWS account ID of this principal.
+
+Can be undefined when the account is not known
+(for example, for service principals).
+Can be a Token - in that case,
+it's assumed to be AWS::AccountId.
+
+---
+
+
+## Structs
+
+### CircleCiConfiguration
+
+#### Initializer
+
+```typescript
+import { CircleCiConfiguration } from '@blimmer/cdk-circleci-oidc'
+
+const circleCiConfiguration: CircleCiConfiguration = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| provider
| ICircleCiOidcProvider
| Reference to CircleCI OpenID Connect Provider configured in AWS IAM. |
+| projectIds
| string[]
| Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role. |
+
+---
+
+##### `provider`Required
+
+```typescript
+public readonly provider: ICircleCiOidcProvider;
+```
+
+- *Type:* ICircleCiOidcProvider
+
+Reference to CircleCI OpenID Connect Provider configured in AWS IAM.
+
+Either pass an construct defined by `new CircleCiOidcProvider`
+or a retrieved reference from `CircleCiOidcProvider.fromOrganizationId`.
+There can be only one (per AWS Account).
+
+---
+
+##### `projectIds`Optional
+
+```typescript
+public readonly projectIds: string[];
+```
+
+- *Type:* string[]
+- *Default:* All CircleCI projects in the provider's organization
+
+Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role.
+
+If you don't provide this
+value, the role will be allowed to be assumed by any CircleCI project in your organization. You can find a
+project's ID in the CircleCI dashboard UI under the "Project Settings" tab. It's usually in a UUID format.
+
+---
+
+### CircleCiOidcProviderProps
+
+#### Initializer
+
+```typescript
+import { CircleCiOidcProviderProps } from '@blimmer/cdk-circleci-oidc'
+
+const circleCiOidcProviderProps: CircleCiOidcProviderProps = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| organizationId
| string
| The ID of your CircleCI organization. |
+| thumbprints
| string[]
| The OIDC thumbprints used by the provider. |
+
+---
+
+##### `organizationId`Required
+
+```typescript
+public readonly organizationId: string;
+```
+
+- *Type:* string
+
+The ID of your CircleCI organization.
+
+This is typically in a UUID format. You can find this ID in the CircleCI
+dashboard UI under the "Organization Settings" tab.
+
+---
+
+##### `thumbprints`Optional
+
+```typescript
+public readonly thumbprints: string[];
+```
+
+- *Type:* string[]
+
+The OIDC thumbprints used by the provider.
+
+You should not need to provide this value unless CircleCI suddenly
+rotates their OIDC thumbprints (e.g., in response to a security incident).
+
+If you do need to generate this thumbprint, you can follow the instructions here:
+https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
+
+---
+
+### CircleCiOidcRoleProps
+
+Props that define the IAM Role that can be assumed by a CircleCI job via the CircleCI OpenID Connect Identity Provider.
+
+Besides {@link CircleCiConfiguration}, you may pass in any {@link RoleProps} except `assumedBy`
+which will be defined by this construct.
+
+#### Initializer
+
+```typescript
+import { CircleCiOidcRoleProps } from '@blimmer/cdk-circleci-oidc'
+
+const circleCiOidcRoleProps: CircleCiOidcRoleProps = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| provider
| ICircleCiOidcProvider
| Reference to CircleCI OpenID Connect Provider configured in AWS IAM. |
+| projectIds
| string[]
| Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role. |
+| description
| string
| A description of the role. |
+| externalIds
| string[]
| List of IDs that the role assumer needs to provide one of when assuming this role. |
+| inlinePolicies
| {[ key: string ]: aws-cdk-lib.aws_iam.PolicyDocument}
| A list of named policies to inline into this role. |
+| managedPolicies
| aws-cdk-lib.aws_iam.IManagedPolicy[]
| A list of managed policies associated with this role. |
+| maxSessionDuration
| aws-cdk-lib.Duration
| The maximum session duration that you want to set for the specified role. |
+| path
| string
| The path associated with this role. |
+| permissionsBoundary
| aws-cdk-lib.aws_iam.IManagedPolicy
| AWS supports permissions boundaries for IAM entities (users or roles). |
+| roleName
| string
| A name for the IAM role. |
+
+---
+
+##### `provider`Required
+
+```typescript
+public readonly provider: ICircleCiOidcProvider;
+```
+
+- *Type:* ICircleCiOidcProvider
+
+Reference to CircleCI OpenID Connect Provider configured in AWS IAM.
+
+Either pass an construct defined by `new CircleCiOidcProvider`
+or a retrieved reference from `CircleCiOidcProvider.fromOrganizationId`.
+There can be only one (per AWS Account).
+
+---
+
+##### `projectIds`Optional
+
+```typescript
+public readonly projectIds: string[];
+```
+
+- *Type:* string[]
+- *Default:* All CircleCI projects in the provider's organization
+
+Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role.
+
+If you don't provide this
+value, the role will be allowed to be assumed by any CircleCI project in your organization. You can find a
+project's ID in the CircleCI dashboard UI under the "Project Settings" tab. It's usually in a UUID format.
+
+---
+
+##### `description`Optional
+
+```typescript
+public readonly description: string;
+```
+
+- *Type:* string
+- *Default:* No description.
+
+A description of the role.
+
+It can be up to 1000 characters long.
+
+---
+
+##### `externalIds`Optional
+
+```typescript
+public readonly externalIds: string[];
+```
+
+- *Type:* string[]
+- *Default:* No external ID required
+
+List of IDs that the role assumer needs to provide one of when assuming this role.
+
+If the configured and provided external IDs do not match, the
+AssumeRole operation will fail.
+
+---
+
+##### `inlinePolicies`Optional
+
+```typescript
+public readonly inlinePolicies: {[ key: string ]: PolicyDocument};
+```
+
+- *Type:* {[ key: string ]: aws-cdk-lib.aws_iam.PolicyDocument}
+- *Default:* No policy is inlined in the Role resource.
+
+A list of named policies to inline into this role.
+
+These policies will be
+created with the role, whereas those added by ``addToPolicy`` are added
+using a separate CloudFormation resource (allowing a way around circular
+dependencies that could otherwise be introduced).
+
+---
+
+##### `managedPolicies`Optional
+
+```typescript
+public readonly managedPolicies: IManagedPolicy[];
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IManagedPolicy[]
+- *Default:* No managed policies.
+
+A list of managed policies associated with this role.
+
+You can add managed policies later using
+`addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName(policyName))`.
+
+---
+
+##### `maxSessionDuration`Optional
+
+```typescript
+public readonly maxSessionDuration: Duration;
+```
+
+- *Type:* aws-cdk-lib.Duration
+- *Default:* Duration.hours(1)
+
+The maximum session duration that you want to set for the specified role.
+
+This setting can have a value from 1 hour (3600sec) to 12 (43200sec) hours.
+
+Anyone who assumes the role from the AWS CLI or API can use the
+DurationSeconds API parameter or the duration-seconds CLI parameter to
+request a longer session. The MaxSessionDuration setting determines the
+maximum duration that can be requested using the DurationSeconds
+parameter.
+
+If users don't specify a value for the DurationSeconds parameter, their
+security credentials are valid for one hour by default. This applies when
+you use the AssumeRole* API operations or the assume-role* CLI operations
+but does not apply when you use those operations to create a console URL.
+
+> [https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html)
+
+---
+
+##### `path`Optional
+
+```typescript
+public readonly path: string;
+```
+
+- *Type:* string
+- *Default:* /
+
+The path associated with this role.
+
+For information about IAM paths, see
+Friendly Names and Paths in IAM User Guide.
+
+---
+
+##### `permissionsBoundary`Optional
+
+```typescript
+public readonly permissionsBoundary: IManagedPolicy;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IManagedPolicy
+- *Default:* No permissions boundary.
+
+AWS supports permissions boundaries for IAM entities (users or roles).
+
+A permissions boundary is an advanced feature for using a managed policy
+to set the maximum permissions that an identity-based policy can grant to
+an IAM entity. An entity's permissions boundary allows it to perform only
+the actions that are allowed by both its identity-based policies and its
+permissions boundaries.
+
+> [https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html)
+
+---
+
+##### `roleName`Optional
+
+```typescript
+public readonly roleName: string;
+```
+
+- *Type:* string
+- *Default:* AWS CloudFormation generates a unique physical ID and uses that ID for the role name.
+
+A name for the IAM role.
+
+For valid values, see the RoleName parameter for
+the CreateRole action in the IAM API Reference.
+
+IMPORTANT: If you specify a name, you cannot perform updates that require
+replacement of this resource. You can perform updates that require no or
+some interruption. If you must replace the resource, specify a new name.
+
+If you specify a name, you must specify the CAPABILITY_NAMED_IAM value to
+acknowledge your template's capabilities. For more information, see
+Acknowledging IAM Resources in AWS CloudFormation Templates.
+
+---
+
+### RoleProps
+
+RoleProps.
+
+#### Initializer
+
+```typescript
+import { RoleProps } from '@blimmer/cdk-circleci-oidc'
+
+const roleProps: RoleProps = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| description
| string
| A description of the role. |
+| externalIds
| string[]
| List of IDs that the role assumer needs to provide one of when assuming this role. |
+| inlinePolicies
| {[ key: string ]: aws-cdk-lib.aws_iam.PolicyDocument}
| A list of named policies to inline into this role. |
+| managedPolicies
| aws-cdk-lib.aws_iam.IManagedPolicy[]
| A list of managed policies associated with this role. |
+| maxSessionDuration
| aws-cdk-lib.Duration
| The maximum session duration that you want to set for the specified role. |
+| path
| string
| The path associated with this role. |
+| permissionsBoundary
| aws-cdk-lib.aws_iam.IManagedPolicy
| AWS supports permissions boundaries for IAM entities (users or roles). |
+| roleName
| string
| A name for the IAM role. |
+
+---
+
+##### `description`Optional
+
+```typescript
+public readonly description: string;
+```
+
+- *Type:* string
+- *Default:* No description.
+
+A description of the role.
+
+It can be up to 1000 characters long.
+
+---
+
+##### `externalIds`Optional
+
+```typescript
+public readonly externalIds: string[];
+```
+
+- *Type:* string[]
+- *Default:* No external ID required
+
+List of IDs that the role assumer needs to provide one of when assuming this role.
+
+If the configured and provided external IDs do not match, the
+AssumeRole operation will fail.
+
+---
+
+##### `inlinePolicies`Optional
+
+```typescript
+public readonly inlinePolicies: {[ key: string ]: PolicyDocument};
+```
+
+- *Type:* {[ key: string ]: aws-cdk-lib.aws_iam.PolicyDocument}
+- *Default:* No policy is inlined in the Role resource.
+
+A list of named policies to inline into this role.
+
+These policies will be
+created with the role, whereas those added by ``addToPolicy`` are added
+using a separate CloudFormation resource (allowing a way around circular
+dependencies that could otherwise be introduced).
+
+---
+
+##### `managedPolicies`Optional
+
+```typescript
+public readonly managedPolicies: IManagedPolicy[];
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IManagedPolicy[]
+- *Default:* No managed policies.
+
+A list of managed policies associated with this role.
+
+You can add managed policies later using
+`addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName(policyName))`.
+
+---
+
+##### `maxSessionDuration`Optional
+
+```typescript
+public readonly maxSessionDuration: Duration;
+```
+
+- *Type:* aws-cdk-lib.Duration
+- *Default:* Duration.hours(1)
+
+The maximum session duration that you want to set for the specified role.
+
+This setting can have a value from 1 hour (3600sec) to 12 (43200sec) hours.
+
+Anyone who assumes the role from the AWS CLI or API can use the
+DurationSeconds API parameter or the duration-seconds CLI parameter to
+request a longer session. The MaxSessionDuration setting determines the
+maximum duration that can be requested using the DurationSeconds
+parameter.
+
+If users don't specify a value for the DurationSeconds parameter, their
+security credentials are valid for one hour by default. This applies when
+you use the AssumeRole* API operations or the assume-role* CLI operations
+but does not apply when you use those operations to create a console URL.
+
+> [https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html)
+
+---
+
+##### `path`Optional
+
+```typescript
+public readonly path: string;
+```
+
+- *Type:* string
+- *Default:* /
+
+The path associated with this role.
+
+For information about IAM paths, see
+Friendly Names and Paths in IAM User Guide.
+
+---
+
+##### `permissionsBoundary`Optional
+
+```typescript
+public readonly permissionsBoundary: IManagedPolicy;
+```
+
+- *Type:* aws-cdk-lib.aws_iam.IManagedPolicy
+- *Default:* No permissions boundary.
+
+AWS supports permissions boundaries for IAM entities (users or roles).
+
+A permissions boundary is an advanced feature for using a managed policy
+to set the maximum permissions that an identity-based policy can grant to
+an IAM entity. An entity's permissions boundary allows it to perform only
+the actions that are allowed by both its identity-based policies and its
+permissions boundaries.
+
+> [https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html)
+
+---
+
+##### `roleName`Optional
+
+```typescript
+public readonly roleName: string;
+```
+
+- *Type:* string
+- *Default:* AWS CloudFormation generates a unique physical ID and uses that ID for the role name.
+
+A name for the IAM role.
+
+For valid values, see the RoleName parameter for
+the CreateRole action in the IAM API Reference.
+
+IMPORTANT: If you specify a name, you cannot perform updates that require
+replacement of this resource. You can perform updates that require no or
+some interruption. If you must replace the resource, specify a new name.
+
+If you specify a name, you must specify the CAPABILITY_NAMED_IAM value to
+acknowledge your template's capabilities. For more information, see
+Acknowledging IAM Resources in AWS CloudFormation Templates.
+
+---
+
+
+## Protocols
+
+### ICircleCiOidcProvider
+
+- *Implemented By:* CircleCiOidcProvider, ICircleCiOidcProvider
+
+Describes a CircleCI OpenID Connect Identity Provider for AWS IAM.
+
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| arn
| string
| *No description.* |
+| organizationId
| string
| *No description.* |
+
+---
+
+##### `arn`Required
+
+```typescript
+public readonly arn: string;
+```
+
+- *Type:* string
+
+---
+
+##### `organizationId`Required
+
+```typescript
+public readonly organizationId: string;
+```
+
+- *Type:* string
+
+---
diff --git a/README.md b/README.md
index 64edac4..80f75d0 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,14 @@
# CircleCI OIDC
-This repository contains constructs to communicate between CircleCI and AWS via an Open ID Connect (OIDC) provider.
-The process is described in [this CircleCI blog post](https://circleci.com/blog/openid-connect-identity-tokens/).
+This repository contains constructs to communicate between CircleCI and AWS via an Open ID Connect (OIDC) provider. The
+process is described in [this CircleCI blog post](https://circleci.com/blog/openid-connect-identity-tokens/).
## Security Benefits
By using the OpenID Connect provider, you can communicate with AWS from CircleCI without saving static credentials
-(e.g., `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) in your CircleCI project settings or a context. Removing
-static credentials, especially in light of the early 2023 [breach](https://circleci.com/blog/jan-4-2023-incident-report/),
-is a best practice for security.
+(e.g., `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) in your CircleCI project settings or a context. Removing static
+credentials, especially in light of the early 2023 [breach](https://circleci.com/blog/jan-4-2023-incident-report/), is a
+best practice for security.
## Quick Start
@@ -25,46 +25,48 @@ yarn add @blimmer/cdk-circleci-oidc
Then, create the provider and role(s).
```typescript
-import { Stack, StackProps } from 'aws-cdk-lib';
-import { CircleCiOidcProvider, CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc';
-import { Construct } from 'constructs';
-import { ManagedPolicy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
-import { Bucket } from 'aws-cdk-lib/aws-s3';
+import { Stack, StackProps } from "aws-cdk-lib";
+import { CircleCiOidcProvider, CircleCiOidcRole } from "@blimmer/cdk-circleci-oidc";
+import { Construct } from "constructs";
+import { ManagedPolicy, PolicyStatement } from "aws-cdk-lib/aws-iam";
+import { Bucket } from "aws-cdk-lib/aws-s3";
export class CircleCiStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
- const provider = new CircleCiOidcProvider(this, 'OidcProvider', {
+ // The provider is only created _once per AWS account_. It might make sense to define this in a separate stack
+ // that defines more global resources. See below for how to use import the provider in stacks that don't define it.
+ const provider = new CircleCiOidcProvider(this, "OidcProvider", {
// Find your organization ID in the CircleCI dashboard under "Organization Settings"
- organizationId: '11111111-2222-3333-4444-555555555555',
+ organizationId: "11111111-2222-3333-4444-555555555555",
});
- const myCircleCiRole = new CircleCiOidcRole(this, 'MyCircleCiRole', {
- circleCiOidcProvider: provider,
+ const myCircleCiRole = new CircleCiOidcRole(this, "MyCircleCiRole", {
+ provider,
roleName: "MyCircleCiRole",
// Pass some managed policies to the role
- managedPolicies: [
- ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess'),
- ],
- })
-
- // You can also access the role from the construct. This allows adding roles and using `grant` methods after the
- // construct has been created.
- myCircleCiRole.role.addToPolicy(new PolicyStatement({
- actions: ['s3:ListAllMyBuckets'],
- resources: ['*'],
- }));
-
- const bucket = new Bucket(this, 'MyBucket');
- bucket.grantRead(myCircleCiRole.role);
+ managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName("AmazonS3ReadOnlyAccess")],
+ });
+
+ // You can work with the CircleCI role like any other role
+ myCircleCiRole.addToPolicy(
+ new PolicyStatement({
+ actions: ["s3:ListAllMyBuckets"],
+ resources: ["*"],
+ }),
+ );
+
+ // Including using `.grant` convenience methods
+ const bucket = new Bucket(this, "MyBucket");
+ bucket.grantRead(myCircleCiRole);
}
}
```
-Now, in your `.circleci/config.yml` file, you can use the [AWS CLI Orb](https://circleci.com/developer/orbs/orb/circleci/aws-cli)
-to assume your new role.
+Now, in your `.circleci/config.yml` file, you can use the
+[AWS CLI Orb](https://circleci.com/developer/orbs/orb/circleci/aws-cli) to assume your new role.
```yaml
version: 2.1
@@ -87,56 +89,30 @@ jobs:
- checkout
# https://circleci.com/developer/orbs/orb/circleci/aws-cli#commands-setup
- aws-cli/setup:
- role_arn: 'arn:aws:iam::123456789101:role/MyCircleCiRole'
+ role_arn: "arn:aws:iam::123456789101:role/MyCircleCiRole"
- run:
name: List S3 Buckets
command: aws s3 ls
```
-## Cross Stack Usage
+## Usage in Stacks that Don't Define the Provider
-If you want to use the OIDC provider in another stack, you can use the `getProviderForExport` method.
+The `CircleCiOidcProvider` is only created **once per account**. You can use the
+`CircleCiOidcProvider.fromOrganizationId` method to import a previously created provider into any stack.
```typescript
-import { Stack, StackProps } from 'aws-cdk-lib';
-import { CircleCiOidcProvider } from '@blimmer/cdk-circleci-oidc';
-import { Construct } from 'constructs';
-
-export class CircleCiStack extends Stack {
- readonly circleCiOidcProvider: ManualCircleCiOidcProviderProps; // export for use in other stacks
+import { Stack, StackProps } from "aws-cdk-lib";
+import { CircleCiOidcRole, CircleCiOidcProvider } from "@blimmer/cdk-circleci-oidc";
+import { Construct } from "constructs";
+export class MyStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
- const provider = new CircleCiOidcProvider(this, 'OidcProvider', {
- // Find your organization ID in the CircleCI dashboard under "Organization Settings"
- organizationId: '11111111-2222-3333-4444-555555555555',
- });
-
- this.circleCiOidcProvider = provider.getProviderForExport(this.account);
- }
-}
-```
-
-```typescript
-import { Stack, StackProps } from 'aws-cdk-lib';
-import { CircleCiOidcRole } from '@blimmer/cdk-circleci-oidc';
-import { Construct } from 'constructs';
-import type { CircleCiStack } from './CircleCiStack';
-
-interface ConsumingStackProps {
- circleci: CircleCi;
-}
-
-export class ConsumingStack extends Stack {
- constructor(scope: Construct, id: string, props: ConsumingStackProps) {
- super(scope, id, props);
- const { circleCiOidcProvider } = props.circleci;
-
- const myCircleCiRole = new CircleCiOidcRole(this, 'MyCircleCiRole', {
- circleCiOidcProvider,
+ const myCircleCiRole = new CircleCiOidcRole(this, "MyCircleCiRole", {
+ provider: CircleCiOidcProvider.fromOrganizationId(this, "11111111-2222-3333-4444-555555555555"),
roleName: "MyCircleCiRole",
- })
+ });
}
}
```
@@ -153,6 +129,11 @@ This package is available for Python as `cdk-circleci-oidc`.
pip install cdk-circleci-oidc
```
+## Upgrading Between Major Versions
+
+The API can be expected to change between major versions. Please consult the [UPGRADING docs](/UPGRADING.md.md) for for
+information.
+
## Contributing
Contributions, issues, and feedback are welcome!
diff --git a/UPGRADING.md b/UPGRADING.md
new file mode 100644
index 0000000..b8b471a
--- /dev/null
+++ b/UPGRADING.md
@@ -0,0 +1,186 @@
+# Upgrading
+
+## 0.x to 1.x
+
+The API underwent breaking changes between the 0.x and 1.x releases.
+
+### Minimum CDK Version
+
+This construct is only tested with CDK 2.73.0 and later. You should upgrade to at least this version before upgrading to
+1.x.
+
+### Simpler Import of Existing Providers
+
+Previously, using an existing `CircleCiOidcProvider` was confusing and complicated. Now, you can very easily import an
+existing provider (e.g., one that's created in another stack or repo) by using the
+`CircleCiOidcProvider.fromOrganizationId` static method.
+
+Before:
+
+```typescript
+const provider: ManualCircleCiOidcProviderProps = {
+ provider: OpenIdConnectProvider.fromOpenIdConnectProviderArn(
+ this,
+ "CircleCiOidcProviderImport",
+ "arn:aws:iam::12345678910:oidc-provider/oidc.circleci.com/org/123e4567-e89b-12d3-a456-426614174000",
+ ),
+ organizationId: "123e4567-e89b-12d3-a456-426614174000",
+};
+
+const role = new CircleCiOidcRole(this, "CircleCiOidcRole", {
+ circleCiOidcProvider: provider,
+});
+```
+
+After:
+
+```typescript
+const provider = CircleCiOidcProvider.fromOrganizationId(this, "123e4567-e89b-12d3-a456-426614174000");
+const role = new CircleCiOidcRole(this, "CircleCiOidcRole", {
+ provider,
+});
+```
+
+Much better!
+
+### Constructor Property Changes
+
+#### `CircleCiOidcProvider`
+
+The property `circleCiOidcThumbprints` has been renamed to `thumbprints` for brevity.
+
+#### `CircleCiOidcRole`
+
+The properties `circleCiOidcProvider` and `circleCiProjectIds` have been renamed to `provider` and `projectIds` for
+brevity.
+
+Before:
+
+```typescript
+const role = new CircleCiOidcRole(this, "CircleCiOidcRole", {
+ circleCiOidcProvider: provider,
+ circleCiProjectIds: ["b4f04e57-c8b2-4d80-9526-dc9b1b7a63ad"],
+});
+```
+
+After:
+
+```typescript
+const role = new CircleCiOidcRole(this, "CircleCiOidcRole", {
+ provider,
+ projectIds: ["b4f04e57-c8b2-4d80-9526-dc9b1b7a63ad"],
+});
+```
+
+### Removal of Outer Constructs
+
+`CircleCiOidcProvider` and `CircleCiOidcRole` now _extend_ `CfnOIDCProvider` and `Role`, respectively. This makes them
+simpler to work with and more idiomatic to the CDK.
+
+Before:
+
+```typescript
+const role = new CircleCiOidcRole(this, "CircleCiOidcRole", {
+ circleCiOidcProvider: provider,
+ circleCiProjectIds: ["b4f04e57-c8b2-4d80-9526-dc9b1b7a63ad"],
+});
+
+// It was annoying to have to access role.role to add permissions
+role.role.addToPolicy(
+ new PolicyStatement({
+ actions: ["s3:GetObject"],
+ resources: ["arn:aws:s3:::my-bucket/*"],
+ }),
+);
+bucket.grantRead(role.role);
+```
+
+After:
+
+```typescript
+const role = new CircleCiOidcRole(this, "CircleCiOidcRole", {
+ provider,
+ projectIds: ["b4f04e57-c8b2-4d80-9526-dc9b1b7a63ad"],
+});
+
+// Now the `CircleCiOidcRole` is a `Role` and you can add permissions directly
+role.addToPolicy(
+ new PolicyStatement({
+ actions: ["s3:GetObject"],
+ resources: ["arn:aws:s3:::my-bucket/*"],
+ }),
+);
+```
+
+### `cdk diff` Caused by Internal Refactoring
+
+Because of the [removal of outer constructs](#removal-of-outer-constructs), you might see changes like this in your CDK
+diff when upgrading:
+
+```shell
+Resources
+[-] AWS::IAM::OIDCProvider CircleCiOidcProvider/CircleCiOidcProvider CircleCiOidcProviderBE49A2E7 destroy
+[-] AWS::IAM::Role CircleCiOidcRole/CircleCiOidcRole CircleCiOidcRoleDC0C8DDB destroy
+[+] AWS::IAM::OIDCProvider CircleCiOidcProvider CircleCiOidcProvider
+[+] AWS::IAM::Role CircleCiOidcRole CircleCiOidcRoleC059EF20
+```
+
+If applied, this diff will destroy the old `CircleCiOidcProvider` and `CircleCiOidcRole` and create new ones. You have
+two options to deal with this internal refactoring.
+
+1. Destroy and recreate the affected providers and roles.
+2. Prevent the destroy by overriding the logical ID of the new providers and roles.
+
+#### Destroy and Recreate
+
+This is the "cleanest" option because your CDK code won't contain overrides to work around the internal refactoring.
+However, you'll likely have to issue two separate `cdk deploy` commands to destroy, then recreate the resources.
+
+1. Delete the old resource from your stack and run a `cdk deploy`. This will destroy the old resource.
+1. Add the new resource to your stack and run a `cdk deploy`. This will create the new resource. Note that if you're not
+ passing the `roleName` property, you will likely get a new role name generated by CloudFormation.
+
+NOTE: If you didn't specify an explicit `roleName` when creating the `CircleCiOidcRole`, you don't need to trigger two
+deploys. The role name will be generated by CloudFormation and will be different from the old role name, so the new role
+will be created, then the old one will be destroyed. You _must_ trigger two deploys with the `CircleCiOidcProvider`
+because the name is static.
+
+#### Prevent Destroy by Overriding Logical ID
+
+If it would be disruptive to recreate your `CircleCiOidcProvider` or `CircleCiOidcRole`s, you can use the
+`.overrideLogicalId` property to keep the old logical ID and prevent the destroy.
+
+1. Run a `cdk diff` on your stack to see the logical IDs of the old resources that would be destroyed. You'll see output
+ like this:
+
+ ```shell
+ Resources
+ [-] AWS::IAM::OIDCProvider CircleCiOidcProvider/CircleCiOidcProvider CircleCiOidcProviderBE49A2E7 destroy
+ [-] AWS::IAM::Role CircleCiOidcRole/CircleCiOidcRole CircleCiOidcRoleDC0C8DDB destroy
+ [+] AWS::IAM::OIDCProvider CircleCiOidcProvider CircleCiOidcProvider
+ [+] AWS::IAM::Role CircleCiOidcRole CircleCiOidcRoleC059EF20
+ ```
+
+1. Grab the old logical IDs and use them to override the logical IDs of the new resources. For example:
+
+ ```typescript
+ const provider = new CircleCiOidcProvider(this, "CircleCiOidcProvider", {
+ organizationId: "123e4567-e89b-12d3-a456-426614174000",
+ });
+ provider.overrideLogicalId("CircleCiOidcProviderBE49A2E7");
+
+ const role = new CircleCiOidcRole(this, "CircleCiOidcRole", {
+ provider: provider,
+ });
+ (role.node.defaultChild as unknown as CfnResource).overrideLogicalId("CircleCiOidcRoleDC0C8DDB");
+ ```
+
+ By specifying the old logical IDs, you prevent the destroy of the old resources while still using the new underlying
+ constructs.
+
+1. Validate that the resources will not be destroyed by running another `cdk diff`. You should see that the old
+ resources are no longer marked for destruction.
+
+ ```shell
+ There were no differences
+ ```
diff --git a/package.json b/package.json
index 1932251..8ce2832 100644
--- a/package.json
+++ b/package.json
@@ -33,11 +33,12 @@
"organization": false
},
"devDependencies": {
+ "@mrgrain/jsii-struct-builder": "^0.7.21",
"@types/jest": "^27",
"@types/node": "^18",
"@typescript-eslint/eslint-plugin": "^6",
"@typescript-eslint/parser": "^6",
- "aws-cdk-lib": "2.1.0",
+ "aws-cdk-lib": "2.73.0",
"constructs": "10.0.5",
"eslint": "^8",
"eslint-config-prettier": "^9.1.0",
@@ -59,7 +60,7 @@
"typescript": "^4.9.5"
},
"peerDependencies": {
- "aws-cdk-lib": "^2.1.0",
+ "aws-cdk-lib": "^2.73.0",
"constructs": "^10.0.5"
},
"keywords": [
@@ -67,6 +68,14 @@
],
"main": "lib/index.js",
"license": "Apache-2.0",
+ "typesVersions": {
+ "<=3.9": {
+ "lib/*": [
+ "lib/.types-compat/ts3.9/*",
+ "lib/.types-compat/ts3.9/*/index.d.ts"
+ ]
+ }
+ },
"version": "0.0.0",
"jest": {
"testMatch": [
diff --git a/src/CircleCiOidcProvider.ts b/src/CircleCiOidcProvider.ts
index f035bb2..03ad0d5 100644
--- a/src/CircleCiOidcProvider.ts
+++ b/src/CircleCiOidcProvider.ts
@@ -1,6 +1,12 @@
-import { CfnOIDCProvider, OpenIdConnectProvider } from "aws-cdk-lib/aws-iam";
+import { Stack } from "aws-cdk-lib";
+import { CfnOIDCProvider } from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";
-import { ManualCircleCiOidcProviderProps } from "./CircleCiOidcRole";
+
+/** Describes a CircleCI OpenID Connect Identity Provider for AWS IAM. */
+export interface ICircleCiOidcProvider {
+ readonly arn: string;
+ readonly organizationId: string;
+}
export interface CircleCiOidcProviderProps {
/**
@@ -16,47 +22,38 @@ export interface CircleCiOidcProviderProps {
* If you do need to generate this thumbprint, you can follow the instructions here:
* https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html
*/
- readonly circleCiOidcThumbprints?: string[];
+ readonly thumbprints?: string[];
}
/**
* This construct creates a CircleCI ODIC provider to allow AWS access from CircleCI jobs. You'll need to instantiate
* this construct once per AWS account you want to use CircleCI OIDC with.
*
+ * You can import a existing provider using `CircleCiOidcProvider.fromOrganizationId`.
+ *
* To create a role that can be assumed by CircleCI jobs, use the `CircleCiOidcRole` construct.
*/
-export class CircleCiOidcProvider extends Construct {
- public readonly provider: CfnOIDCProvider;
+export class CircleCiOidcProvider extends CfnOIDCProvider implements ICircleCiOidcProvider {
+ public static fromOrganizationId(scope: Construct, organizationId: string): ICircleCiOidcProvider {
+ const accountId = Stack.of(scope).account;
+ const providerArn = `arn:aws:iam::${accountId}:oidc-provider/oidc.circleci.com/org/${organizationId}`;
+ return {
+ arn: providerArn,
+ organizationId,
+ };
+ }
+
public readonly organizationId: string;
+ public readonly arn: string;
constructor(scope: Construct, id: string, props: CircleCiOidcProviderProps) {
- super(scope, id);
-
- const { organizationId, circleCiOidcThumbprints = ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"] } = props;
-
- // The L2 construct uses a Custom Resource, which is slow and has a few known issues
- // (see https://github.com/aws/aws-cdk/issues/21197#issuecomment-1312843734)
- // Therefore, we use the L1 OIDC provider construct directly instead.
- this.provider = new CfnOIDCProvider(this, "CircleCiOidcProvider", {
- url: `https://oidc.circleci.com/org/${organizationId}`,
- clientIdList: [organizationId],
- thumbprintList: circleCiOidcThumbprints,
+ super(scope, id, {
+ url: `https://oidc.circleci.com/org/${props.organizationId}`,
+ clientIdList: [props.organizationId],
+ thumbprintList: props.thumbprints ?? ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"],
});
- this.organizationId = organizationId;
- }
-
- public getProviderForExport(
- accountId: string,
- importName = "CircleCiOidcProviderForExport",
- ): ManualCircleCiOidcProviderProps {
- return {
- provider: OpenIdConnectProvider.fromOpenIdConnectProviderArn(
- this,
- importName,
- `arn:aws:iam::${accountId}:oidc-provider/oidc.circleci.com/org/${this.organizationId}`,
- ),
- organizationId: this.organizationId,
- };
+ this.arn = this.attrArn;
+ this.organizationId = props.organizationId;
}
}
diff --git a/src/CircleCiOidcRole.ts b/src/CircleCiOidcRole.ts
index 362f800..584ba9a 100644
--- a/src/CircleCiOidcRole.ts
+++ b/src/CircleCiOidcRole.ts
@@ -1,33 +1,17 @@
-import {
- Condition,
- IManagedPolicy,
- IOpenIdConnectProvider,
- OpenIdConnectPrincipal,
- OpenIdConnectProvider,
- PolicyDocument,
- Role,
-} from "aws-cdk-lib/aws-iam";
+import { OpenIdConnectPrincipal, OpenIdConnectProvider, Role } from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";
-import { CircleCiOidcProvider } from "./CircleCiOidcProvider";
+import { type ICircleCiOidcProvider } from "./CircleCiOidcProvider";
+import type { RoleProps } from "./generated/IamRoleProps";
-/**
- * If you're using the {@link CircleCiOidcProvider} construct, pass it instead of these manually-defined props.
- */
-export interface ManualCircleCiOidcProviderProps {
+export interface CircleCiConfiguration {
/**
- * The CircleCI OIDC provider. You can either manually create it or import it.
+ * Reference to CircleCI OpenID Connect Provider configured in AWS IAM.
+ *
+ * Either pass an construct defined by `new CircleCiOidcProvider`
+ * or a retrieved reference from `CircleCiOidcProvider.fromOrganizationId`.
+ * There can be only one (per AWS Account).
*/
- readonly provider: IOpenIdConnectProvider;
-
- /**
- * The ID of your CircleCI organization. This is typically in a UUID format. You can find this ID in the CircleCI
- * dashboard UI under the "Organization Settings" tab.
- */
- readonly organizationId: string;
-}
-
-export interface CircleCiOidcRoleProps {
- readonly circleCiOidcProvider: CircleCiOidcProvider | ManualCircleCiOidcProviderProps;
+ readonly provider: ICircleCiOidcProvider;
/**
* Provide the UUID(s) of the CircleCI project(s) you want to be allowed to use this role. If you don't provide this
@@ -36,72 +20,55 @@ export interface CircleCiOidcRoleProps {
*
* @default - All CircleCI projects in the provider's organization
*/
- readonly circleCiProjectIds?: string[];
-
- /**
- * You can pass an explicit role name if you'd like, since you need to reference the Role ARN within your CircleCI
- * configuration.
- *
- * @default - CloudFormation will auto-generate you a role name
- */
- readonly roleName?: string;
-
- readonly managedPolicies?: IManagedPolicy[];
- readonly inlinePolicies?: {
- [name: string]: PolicyDocument;
- };
- readonly description?: string;
+ readonly projectIds?: string[];
}
/**
- * This construct creates a CircleCI ODIC provider to allow AWS access from CircleCI jobs. You'll need to instantiate
- * this construct once per AWS account you want to use CircleCI OIDC with.
+ * Props that define the IAM Role that can be assumed by a CircleCI job
+ * via the CircleCI OpenID Connect Identity Provider.
*
- * To create a role that can be assumed by CircleCI jobs, use the `CircleCiOidcRole` construct.
+ * Besides {@link CircleCiConfiguration}, you may pass in any {@link RoleProps} except `assumedBy`
+ * which will be defined by this construct.
*/
-export class CircleCiOidcRole extends Construct {
- readonly role: Role;
+export interface CircleCiOidcRoleProps extends CircleCiConfiguration, RoleProps {}
+/** Define an IAM Role that can be assumed by a CircleCI Job via the CircleCI OpenID Connect Identity Provider. */
+export class CircleCiOidcRole extends Role {
constructor(scope: Construct, id: string, props: CircleCiOidcRoleProps) {
- super(scope, id);
-
- const { circleCiProjectIds, circleCiOidcProvider, ...roleProps } = props;
- const { provider, organizationId } = this.extractOpenIdConnectProvider(circleCiOidcProvider);
- const oidcUrl = `oidc.circleci.com/org/${organizationId}`;
-
- this.role = new Role(this, "CircleCiOidcRole", {
- assumedBy: new OpenIdConnectPrincipal(provider, {
- StringEquals: { [`${oidcUrl}:aud`]: organizationId },
- ...this.generateProjectCondition(oidcUrl, organizationId, circleCiProjectIds),
- }),
- ...roleProps,
+ super(scope, id, {
+ assumedBy: new OpenIdConnectPrincipal(
+ // We use the CfnOIDCProvider instead of the OpenIdConnectProvider since it's overly complex
+ // See https://github.com/aws/aws-cdk/issues/21197
+ // However, the OpenIdConnectPrincipal still expects the L2 OpenIdConnectProvider, so we "import" it here to
+ // make TypeScript happy with the types.
+ OpenIdConnectProvider.fromOpenIdConnectProviderArn(
+ scope,
+ `CircleCiOidcProviderImport${id}`,
+ props.provider.arn,
+ ),
+ {
+ StringEquals: {
+ [`oidc.circleci.com/org/${props.provider.organizationId}:aud`]: props.provider.organizationId,
+ },
+ ...generateProjectCondition(
+ `oidc.circleci.com/org/${props.provider.organizationId}`,
+ props.provider.organizationId,
+ props.projectIds,
+ ),
+ },
+ ),
});
}
+}
- private extractOpenIdConnectProvider(provider: CircleCiOidcProvider | ManualCircleCiOidcProviderProps) {
- if (provider instanceof CircleCiOidcProvider) {
- return {
- provider: OpenIdConnectProvider.fromOpenIdConnectProviderArn(
- this,
- "ImportOidcProvider",
- provider.provider.attrArn,
- ),
- organizationId: provider.organizationId,
- };
- } else {
- return provider;
- }
+function generateProjectCondition(oidcUrl: string, organizationId: string, projectIds?: string[]) {
+ if (!projectIds || projectIds.length === 0) {
+ return {};
}
- private generateProjectCondition(oidcUrl: string, organizationId: string, circleCiProjectIds?: string[]): Condition {
- if (!circleCiProjectIds || circleCiProjectIds.length === 0) {
- return {};
- }
-
- return {
- StringLike: {
- [`${oidcUrl}:sub`]: circleCiProjectIds.map((projectId) => `org/${organizationId}/project/${projectId}/*`),
- },
- };
- }
+ return {
+ StringLike: {
+ [`${oidcUrl}:sub`]: projectIds.map((projectId) => `org/${organizationId}/project/${projectId}/*`),
+ },
+ };
}
diff --git a/src/generated/IamRoleProps.ts b/src/generated/IamRoleProps.ts
new file mode 100644
index 0000000..70a1757
--- /dev/null
+++ b/src/generated/IamRoleProps.ts
@@ -0,0 +1,97 @@
+// ~~ Generated by projen. To modify, edit .projenrc.ts and run "npx projen".
+import type { aws_iam, Duration } from 'aws-cdk-lib';
+
+/**
+ * RoleProps
+ */
+export interface RoleProps {
+ /**
+ * A name for the IAM role.
+ * For valid values, see the RoleName parameter for
+ * the CreateRole action in the IAM API Reference.
+ *
+ * IMPORTANT: If you specify a name, you cannot perform updates that require
+ * replacement of this resource. You can perform updates that require no or
+ * some interruption. If you must replace the resource, specify a new name.
+ *
+ * If you specify a name, you must specify the CAPABILITY_NAMED_IAM value to
+ * acknowledge your template's capabilities. For more information, see
+ * Acknowledging IAM Resources in AWS CloudFormation Templates.
+ * @default - AWS CloudFormation generates a unique physical ID and uses that ID
+for the role name.
+ * @stability stable
+ */
+ readonly roleName?: string;
+ /**
+ * AWS supports permissions boundaries for IAM entities (users or roles).
+ * A permissions boundary is an advanced feature for using a managed policy
+ * to set the maximum permissions that an identity-based policy can grant to
+ * an IAM entity. An entity's permissions boundary allows it to perform only
+ * the actions that are allowed by both its identity-based policies and its
+ * permissions boundaries.
+ * @default - No permissions boundary.
+ * @stability stable
+ * @link https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html
+ */
+ readonly permissionsBoundary?: aws_iam.IManagedPolicy;
+ /**
+ * The path associated with this role.
+ * For information about IAM paths, see
+ * Friendly Names and Paths in IAM User Guide.
+ * @default /
+ * @stability stable
+ */
+ readonly path?: string;
+ /**
+ * The maximum session duration that you want to set for the specified role.
+ * This setting can have a value from 1 hour (3600sec) to 12 (43200sec) hours.
+ *
+ * Anyone who assumes the role from the AWS CLI or API can use the
+ * DurationSeconds API parameter or the duration-seconds CLI parameter to
+ * request a longer session. The MaxSessionDuration setting determines the
+ * maximum duration that can be requested using the DurationSeconds
+ * parameter.
+ *
+ * If users don't specify a value for the DurationSeconds parameter, their
+ * security credentials are valid for one hour by default. This applies when
+ * you use the AssumeRole* API operations or the assume-role* CLI operations
+ * but does not apply when you use those operations to create a console URL.
+ * @default Duration.hours(1)
+ * @stability stable
+ * @link https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use.html
+ */
+ readonly maxSessionDuration?: Duration;
+ /**
+ * A list of managed policies associated with this role.
+ * You can add managed policies later using
+ * `addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName(policyName))`.
+ * @default - No managed policies.
+ * @stability stable
+ */
+ readonly managedPolicies?: Array;
+ /**
+ * A list of named policies to inline into this role.
+ * These policies will be
+ * created with the role, whereas those added by ``addToPolicy`` are added
+ * using a separate CloudFormation resource (allowing a way around circular
+ * dependencies that could otherwise be introduced).
+ * @default - No policy is inlined in the Role resource.
+ * @stability stable
+ */
+ readonly inlinePolicies?: Record;
+ /**
+ * List of IDs that the role assumer needs to provide one of when assuming this role.
+ * If the configured and provided external IDs do not match, the
+ * AssumeRole operation will fail.
+ * @default No external ID required
+ * @stability stable
+ */
+ readonly externalIds?: Array;
+ /**
+ * A description of the role.
+ * It can be up to 1000 characters long.
+ * @default - No description.
+ * @stability stable
+ */
+ readonly description?: string;
+}
diff --git a/src/index.ts b/src/index.ts
index cfa1665..10a9e24 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,2 +1,3 @@
export * from "./CircleCiOidcProvider";
export * from "./CircleCiOidcRole";
+export * from "./generated/IamRoleProps";
diff --git a/test/CircleCiOidcProvider.test.ts b/test/CircleCiOidcProvider.test.ts
index 87c976f..720093a 100644
--- a/test/CircleCiOidcProvider.test.ts
+++ b/test/CircleCiOidcProvider.test.ts
@@ -3,6 +3,18 @@ import { Template } from "aws-cdk-lib/assertions";
import { CircleCiOidcProvider } from "../src";
describe("CircleCiOidcProvider", () => {
+ it("creates the proper URL", () => {
+ const app = new App();
+ const stack = new Stack(app, "TestStack");
+ new CircleCiOidcProvider(stack, "CircleCiOidcProvider", {
+ organizationId: "1234",
+ });
+
+ Template.fromStack(stack).hasResourceProperties("AWS::IAM::OIDCProvider", {
+ Url: "https://oidc.circleci.com/org/1234",
+ });
+ });
+
it("uses the organization ID as the client ID", () => {
const app = new App();
const stack = new Stack(app, "TestStack");
@@ -14,6 +26,7 @@ describe("CircleCiOidcProvider", () => {
ClientIdList: ["1234"],
});
});
+
it("uses a default thumbprint list", () => {
const app = new App();
const stack = new Stack(app, "TestStack");
@@ -26,20 +39,36 @@ describe("CircleCiOidcProvider", () => {
});
});
- it("can export a provider for use in other stacks", () => {
+ it("can override the thumbprint list", () => {
const app = new App();
const stack = new Stack(app, "TestStack");
- const provider = new CircleCiOidcProvider(stack, "CircleCiOidcProvider", {
+ new CircleCiOidcProvider(stack, "CircleCiOidcProvider", {
organizationId: "1234",
+ thumbprints: ["thumbprint"],
});
- const accountId = "123456789012";
- const providerForExport = provider.getProviderForExport(accountId);
+ Template.fromStack(stack).hasResourceProperties("AWS::IAM::OIDCProvider", {
+ ThumbprintList: ["thumbprint"],
+ });
+ });
+
+ it("can import an existing provider", () => {
+ const app = new App();
+ const stack = new Stack(app, "TestStack", {
+ env: { account: "123456789012", region: "us-west-2" },
+ });
+ const provider = CircleCiOidcProvider.fromOrganizationId(stack, "1234");
+
+ expect(provider.arn).toEqual("arn:aws:iam::123456789012:oidc-provider/oidc.circleci.com/org/1234");
+ expect(provider.organizationId).toEqual("1234");
+ });
+
+ it("can import an existing provider when the stack is environment agnostic", () => {
+ const app = new App();
+ const stack = new Stack(app, "TestStack");
+ const provider = CircleCiOidcProvider.fromOrganizationId(stack, "1234");
- expect(providerForExport.organizationId).toEqual("1234");
- expect(providerForExport.provider.openIdConnectProviderArn).toEqual(
- "arn:aws:iam::123456789012:oidc-provider/oidc.circleci.com/org/1234",
- );
- expect(providerForExport.provider.openIdConnectProviderIssuer).toEqual("oidc.circleci.com/org/1234");
+ expect(provider.arn).toEqual(`arn:aws:iam::${Stack.of(stack).account}:oidc-provider/oidc.circleci.com/org/1234`);
+ expect(provider.organizationId).toEqual("1234");
});
});
diff --git a/test/CircleCiOidcRole.test.ts b/test/CircleCiOidcRole.test.ts
index dff9af6..ccbf0ac 100644
--- a/test/CircleCiOidcRole.test.ts
+++ b/test/CircleCiOidcRole.test.ts
@@ -1,6 +1,5 @@
import { App, Stack } from "aws-cdk-lib";
import { Match, Template } from "aws-cdk-lib/assertions";
-import { OpenIdConnectProvider } from "aws-cdk-lib/aws-iam";
import { Queue } from "aws-cdk-lib/aws-sqs";
import { CircleCiOidcProvider, CircleCiOidcRole } from "../src";
@@ -12,7 +11,7 @@ describe("CircleCiOidcRole", () => {
organizationId: "1234",
});
new CircleCiOidcRole(stack, "CircleCiOidcRole", {
- circleCiOidcProvider: provider,
+ provider,
});
Template.fromStack(stack).hasResourceProperties("AWS::IAM::Role", {
@@ -28,7 +27,7 @@ describe("CircleCiOidcRole", () => {
},
Principal: {
Federated: {
- "Fn::GetAtt": ["CircleCiOidcProviderBE49A2E7", "Arn"],
+ "Fn::GetAtt": ["CircleCiOidcProvider", "Arn"],
},
},
}),
@@ -41,14 +40,7 @@ describe("CircleCiOidcRole", () => {
const app = new App();
const stack = new Stack(app, "TestStack");
new CircleCiOidcRole(stack, "CircleCiOidcRole", {
- circleCiOidcProvider: {
- provider: OpenIdConnectProvider.fromOpenIdConnectProviderArn(
- stack,
- "ImportProvider",
- "arn:aws:iam::12345678910:oidc-provider/circleci",
- ),
- organizationId: "1234",
- },
+ provider: CircleCiOidcProvider.fromOrganizationId(stack, "1234"),
});
Template.fromStack(stack).hasResourceProperties("AWS::IAM::Role", {
@@ -63,7 +55,18 @@ describe("CircleCiOidcRole", () => {
},
},
Principal: {
- Federated: "arn:aws:iam::12345678910:oidc-provider/circleci",
+ Federated: {
+ "Fn::Join": [
+ "",
+ [
+ "arn:aws:iam::",
+ {
+ Ref: "AWS::AccountId",
+ },
+ ":oidc-provider/oidc.circleci.com/org/1234",
+ ],
+ ],
+ },
},
}),
],
@@ -78,8 +81,8 @@ describe("CircleCiOidcRole", () => {
organizationId: "1234",
});
new CircleCiOidcRole(stack, "CircleCiOidcRole", {
- circleCiOidcProvider: provider,
- circleCiProjectIds: ["1234", "5678"],
+ provider: provider,
+ projectIds: ["1234", "5678"],
});
Template.fromStack(stack).hasResourceProperties("AWS::IAM::Role", {
@@ -108,8 +111,8 @@ describe("CircleCiOidcRole", () => {
const provider = new CircleCiOidcProvider(stack, "CircleCiOidcProvider", {
organizationId: "1234",
});
- const { role } = new CircleCiOidcRole(stack, "CircleCiOidcRole", {
- circleCiOidcProvider: provider,
+ const role = new CircleCiOidcRole(stack, "CircleCiOidcRole", {
+ provider,
});
const queue = new Queue(stack, "Queue");
@@ -119,7 +122,7 @@ describe("CircleCiOidcRole", () => {
// Attached to the role
Roles: [
{
- Ref: "CircleCiOidcRoleDC0C8DDB",
+ Ref: "CircleCiOidcRoleC059EF20",
},
],
PolicyDocument: {
diff --git a/yarn.lock b/yarn.lock
index 30ef1a5..656426d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15,6 +15,21 @@
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
+"@aws-cdk/asset-awscli-v1@^2.2.97":
+ version "2.2.202"
+ resolved "https://registry.yarnpkg.com/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz#4627201d71f6a5c60db36385ce09cb81005f4b32"
+ integrity sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==
+
+"@aws-cdk/asset-kubectl-v20@^2.1.1":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz#d8e20b5f5dc20128ea2000dc479ca3c7ddc27248"
+ integrity sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==
+
+"@aws-cdk/asset-node-proxy-agent-v5@^2.0.77":
+ version "2.0.166"
+ resolved "https://registry.yarnpkg.com/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.166.tgz#467507db141cd829ff8aa9d6ea5519310a4276b8"
+ integrity sha512-j0xnccpUQHXJKPgCwQcGGNu4lRiC1PptYfdxBIH1L4dRK91iBxtSQHESRQX+yB47oGLaF/WfNN/aF3WXwlhikg==
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.1", "@babel/code-frame@^7.24.2":
version "7.24.2"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae"
@@ -625,6 +640,13 @@
dependencies:
ajv "^8.12.0"
+"@mrgrain/jsii-struct-builder@^0.7.21":
+ version "0.7.21"
+ resolved "https://registry.yarnpkg.com/@mrgrain/jsii-struct-builder/-/jsii-struct-builder-0.7.21.tgz#c08842b3bb451fbd9d3473d16325226ef48462c8"
+ integrity sha512-+DgF8/TzBRG98Xc/uq9XeZjBJ28NO5k0y8oBV5nIY8j/OD3jOWHPHC7VaxI6FubNQ3UhZaqaQ6UoqcLeZy8m+w==
+ dependencies:
+ "@jsii/spec" "^1.96.0"
+
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -1024,7 +1046,7 @@ ajv@^6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
-ajv@^8.12.0:
+ajv@^8.0.1, ajv@^8.12.0:
version "8.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
@@ -1176,6 +1198,11 @@ arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==
+astral-regex@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+ integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -1193,19 +1220,23 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
-aws-cdk-lib@2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.1.0.tgz#2497484cfd4e2eeaba99b070bbfa54486d52ae34"
- integrity sha512-W607G3aSrWpawpcqzIuUYKlU+grfvkbszyqikyVYqJgMHFCCQXq0S1ynPMzfQ49CwjlwZsu4LIsPM+dNR+Yj6g==
+aws-cdk-lib@2.73.0:
+ version "2.73.0"
+ resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.73.0.tgz#1cba582bc36a98d613b1f89680df1b17d71f43f9"
+ integrity sha512-r9CUe3R7EThr9U0Eb7kQCK4Ee34TDeMH+bonvGD9rNRRTYDauvAgNCsx4DZYYksPrXLRzWjzVbuXAHaDDzWt+A==
dependencies:
+ "@aws-cdk/asset-awscli-v1" "^2.2.97"
+ "@aws-cdk/asset-kubectl-v20" "^2.1.1"
+ "@aws-cdk/asset-node-proxy-agent-v5" "^2.0.77"
"@balena/dockerignore" "^1.0.2"
case "1.6.3"
fs-extra "^9.1.0"
- ignore "^5.1.9"
- jsonschema "^1.4.0"
- minimatch "^3.0.4"
- punycode "^2.1.1"
- semver "^7.3.5"
+ ignore "^5.2.4"
+ jsonschema "^1.4.1"
+ minimatch "^3.1.2"
+ punycode "^2.3.0"
+ semver "^7.3.8"
+ table "^6.8.1"
yaml "1.10.2"
babel-jest@^27.5.1:
@@ -2796,7 +2827,7 @@ iconv-lite@0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-ignore@^5.1.9, ignore@^5.2.0, ignore@^5.2.4:
+ignore@^5.2.0, ignore@^5.2.4:
version "5.3.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
@@ -3768,7 +3799,7 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
-jsonschema@^1.4.0:
+jsonschema@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab"
integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==
@@ -3863,6 +3894,11 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+lodash.truncate@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
+ integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
+
lodash@^4.17.15, lodash@^4.7.0:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@@ -4402,7 +4438,7 @@ psl@^1.1.33:
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
-punycode@^2.1.0, punycode@^2.1.1:
+punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
@@ -4648,7 +4684,7 @@ semver-intersect@^1.4.0, semver-intersect@^1.5.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
-semver@7.x, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0:
+semver@7.x, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.8, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
@@ -4736,6 +4772,15 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+slice-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+ integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
sort-json@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/sort-json/-/sort-json-2.0.1.tgz#7338783bef807185dc37d5b02e3afd905d537cfb"
@@ -5011,6 +5056,17 @@ synckit@^0.8.6:
"@pkgr/core" "^0.1.0"
tslib "^2.6.2"
+table@^6.8.1:
+ version "6.8.2"
+ resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58"
+ integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==
+ dependencies:
+ ajv "^8.0.1"
+ lodash.truncate "^4.4.2"
+ slice-ansi "^4.0.0"
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+
tapable@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"