Skip to content

Commit

Permalink
Add ability to specify resource options at the stack level (#245)
Browse files Browse the repository at this point in the history
It has been possible to specify Pulumi resource options at the stack
level, but it did not flow through to the actual resources. This PR
makes sure that the inheritance works correctly.

This PR also adds functionality to automatically set the Stack
environment based on the App provider. Because the App creates the
stacks in an async context, we can use provider functions to lookup the
environment and then pass the resolved environment to the stack.

For now this is something that the user will have to specify (example in
the `lookups` example test). This would mean that all Stacks have their
environment provided by default. This will cut down on the number of
Intrinsics used in the generated template. The reason I am not turning
this on by default is because it requires lookups to be enabled
(environment agnostic stacks use Intrinsics to find availability zones,
but explicit environment stacks use lookups).

If the user provides a provider to the Stack we are no longer in an
async context which means we can't determine the environment from the
provider and fall back to an environment agnostic stack.

re #61, re #219
  • Loading branch information
corymhall authored Nov 25, 2024
1 parent aa35dfb commit 596084d
Show file tree
Hide file tree
Showing 16 changed files with 663 additions and 52 deletions.
130 changes: 128 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ combination with CDK `fromXXX` methods.

**Example**
```ts
import * as pulumicdk from '@pulumi/cdk';
import * as aws from '@pulumi/aws';
import * as aws_route53 from 'aws-cdk-lib/aws-route53';

const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
const stack = new pulumicdk.Stack('example-stack');

Expand Down Expand Up @@ -183,6 +187,9 @@ outputs. You can do this in one of two ways.
Any `CfnOutput` that you create automatically gets added to the `App outputs`.

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as s3 from 'aws-cdk-lib/aws-s3';

const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
const stack = new pulumicdk.Stack('example-stack');
const bucket = new s3.Bucket(stack, 'Bucket');
Expand All @@ -196,6 +203,9 @@ export const bucketName = app.outputs['BucketName'];
**AppOutputs**

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as s3 from 'aws-cdk-lib/aws-s3';

const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutputs => {
const stack = new pulumicdk.Stack('example-stack');
const bucket = new s3.Bucket(stack, 'Bucket');
Expand Down Expand Up @@ -248,6 +258,93 @@ const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
});
```

### Stack Level Providers

It is also possible to customize the Providers at the Stack level. This can be
useful in cases where you need to deploy resources to different AWS regions.

```ts
import * as aws from '@pulumi/aws';
import * as ccapi from '@pulumi/aws-native';
import * as pulumicdk from '@pulumi/cdk';

const awsProvider = new aws.Provider('aws-provider');
const awsCCAPIProvider = new ccapi.Provider('ccapi-provider', {
// enable autoNaming
autoNaming: {
autoTrim: true,
randomSuffixMinLength: 7,
}
});

const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
// inherits the provider from the app
const defaultProviderStack = new pulumicdk.Stack('default-provider-stack');
const bucket = new s3.Bucket(defaultProviderStack, 'Bucket');

// use a different provider for this stack
const east2Stack = new pulumicdk.Stack('east2-stack', {
providers: [
new aws.Provider('east2-provider', { region: 'us-east-2' }),
new ccapi.Provider('east2-ccapi-provider', {
region: 'us-east-2',
autoNaming: {
autoTrim: true,
randomSuffixMinLength: 7,
},
}),
],
});
const bucket2 = new s3.Bucket(east2Stack, 'Bucket');
}, {
providers: [
dockerBuildProvider,
awsProvider,
awsCCAPIProvider,
]
});
```

One thing to note is that when you pass different custom providers to a Stack,
by default the Stack becomes an [environment agnostic stack](https://docs.aws.amazon.com/cdk/v2/guide/configure-env.html#configure-env-examples).
If you want to have the environment specified at the CDK Stack level, then you
also need to provide the environment to the Stack Props, as follows:

```ts
import * as aws from '@pulumi/aws';
import * as ccapi from '@pulumi/aws-native';
import * as pulumicdk from '@pulumi/cdk';

const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
// inherits the provider from the app and has the CDK env auto populated
// based on the default provider
const defaultProviderStack = new pulumicdk.Stack('default-provider-stack');
const bucket = new s3.Bucket(defaultProviderStack, 'Bucket');

// use a different provider for this stack
const east2Stack = new pulumicdk.Stack('east2-stack', {
props: {
env: {
region: 'us-east-2',
account: '12345678912',
},
},
providers: [
new aws.Provider('east2-provider', { region: 'us-east-2' }),
new ccapi.Provider('east2-ccapi-provider', {
region: 'us-east-2',
autoNaming: {
autoTrim: true,
randomSuffixMinLength: 7,
},
}),
],
});
const bucket2 = new s3.Bucket(east2Stack, 'Bucket');
});

```

## CDK Lookups

CDK [lookups](https://docs.aws.amazon.com/cdk/v2/guide/context.html#context_methods) are currently disabled by default.
Expand All @@ -260,6 +357,9 @@ Instead of using CDK Lookups you can use Pulumi functions along with CDK

**Example**
```ts
import * as pulumicdk from '@pulumi/cdk';
import * as aws from '@pulumi/aws';

const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutputs => {
const stack = new pulumicdk.Stack('example-stack');
// use getAmiOutput to lookup the AMI instead of ec2.LookupMachineImage
Expand Down Expand Up @@ -295,6 +395,9 @@ Pulumi twice (the first execution will fail).

**Example**
```ts
import * as pulumicdk from '@pulumi/cdk';
import * as aws_route53 from 'aws-cdk-lib/aws-route53';

const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutputs => {
const stack = new pulumicdk.Stack('example-stack');
const hostedZone = aws_route53.HostedZone.fromLookup(this, 'hosted-zone', {
Expand Down Expand Up @@ -335,6 +438,7 @@ of CDK.
Below is an example output using Pulumi's [Compliance Ready Policies](https://www.pulumi.com/docs/iac/packages-and-automation/crossguard/compliance-ready-policies/)

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as s3 from 'aws-cdk-lib/aws-s3';

const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutputs => {
Expand All @@ -358,6 +462,7 @@ Policies:
Pulumi CDK supports CDK Aspects, including aspects like [cdk-nag](https://github.com/cdklabs/cdk-nag)

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { AwsSolutionsChecks } from 'cdk-nag';

Expand All @@ -380,6 +485,7 @@ const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutput
Pulumi CDK also supports [CDK Policy Validation Plugins](https://docs.aws.amazon.com/cdk/v2/guide/policy-validation-synthesis.html).

```ts
import * as pulumicdk from '@pulumi/cdk';
import { CfnGuardValidator } from '@cdklabs/cdk-validator-cfnguard';
import * as s3 from 'aws-cdk-lib/aws-s3';

Expand Down Expand Up @@ -450,6 +556,9 @@ a reference.
### Simple mapping

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as aws from '@pulumi/aws';

const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutputs => {
const stack = new pulumicdk.Stack('example-stack');
}, {
Expand Down Expand Up @@ -479,6 +588,9 @@ resources. In these cases you should return the `logicalId` of the resource
along with the resource itself.

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as aws from '@pulumi/aws';

const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutputs => {
const stack = new pulumicdk.Stack('example-stack');
}, {
Expand Down Expand Up @@ -516,6 +628,12 @@ you create the asset. This is because Pulumi CDK will automatically create a ECR
Repository per image asset.

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as aws from '@pulumi/aws';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecs_patterns from 'aws-cdk-lib/aws-ecs-patterns';

const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
const stack = new pulumicdk.Stack('example-stack');

Expand Down Expand Up @@ -548,6 +666,8 @@ For example, if you wanted to set `protect` on database resources you could use
a transform like this.

```ts
import * as pulumicdk from '@pulumi/cdk';

const app = new pulumicdk.App('app', (scope: pulumicdk.App): pulumicdk.AppOutputs => {
const stack = new pulumicdk.Stack('example-stack');
}, {
Expand Down Expand Up @@ -577,13 +697,16 @@ In order to customize the settings, you can pass in a `PulumiSynthesizer` that
you create.

```ts
import * as pulumicdk from '@pulumi/cdk';
import * as s3 from 'aws-cdk-lib/aws-s3';

const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
const stack = new pulumicdk.Stack('example-stack');
const bucket = new s3.Bucket(stack, 'Bucket');
}, {
appOptions: {
props: {
defaultStackSynthesizer: new PulumiSynthesizer({
defaultStackSynthesizer: new pulumicdk.PulumiSynthesizer({
appId: `cdk-${pulumi.getStack()}`,
autoDeleteStagingAssets: false,
})
Expand Down Expand Up @@ -624,7 +747,10 @@ default. If you _are_ configuring your own `aws-native` provider then you will
have to enable this.

```ts
const nativeProvider = new aws_native.Provider('cdk-native-provider', {
import * as pulumicdk from '@pulumi/cdk';
import * as ccapi from '@pulumi/aws-native';

const nativeProvider = new ccapi.Provider('cdk-native-provider', {
region: 'us-east-2',
autoNaming: {
autoTrim: true,
Expand Down
70 changes: 63 additions & 7 deletions api-docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,45 @@ export const bucket = app.outputs['bucket'];

###### Defined in

[stack.ts:96](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L96)
[stack.ts:105](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L105)

#### Properties

| Property | Modifier | Type | Default value | Description | Defined in |
| ------ | ------ | ------ | ------ | ------ | ------ |
| `name` | `readonly` | `string` | `undefined` | The name of the component | [stack.ts:55](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L55) |
| `outputs` | `public` | `object` | `{}` | The collection of outputs from the AWS CDK Stack represented as Pulumi Outputs. Each CfnOutput defined in the AWS CDK Stack will populate a value in the outputs. | [stack.ts:61](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L61) |
| `name` | `readonly` | `string` | `undefined` | The name of the component | [stack.ts:57](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L57) |
| `outputs` | `public` | `object` | `{}` | The collection of outputs from the AWS CDK Stack represented as Pulumi Outputs. Each CfnOutput defined in the AWS CDK Stack will populate a value in the outputs. | [stack.ts:63](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L63) |

#### Accessors

##### env

###### Get Signature

> **get** **env**(): `Environment`
This can be used to get the CDK Environment based on the Pulumi Provider used for the App.
You can then use this to configure an explicit environment on Stacks.

###### Example

```ts
const app = new pulumicdk.App('app', (scope: pulumicdk.App) => {
const stack = new pulumicdk.Stack(scope, 'pulumi-stack', {
props: { env: app.env },
});
});
```

###### Returns

`Environment`

the CDK Environment configured for the App

###### Defined in

[stack.ts:155](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L155)

***

Expand Down Expand Up @@ -121,7 +152,7 @@ Create and register an AWS CDK stack deployed with Pulumi.

###### Defined in

[stack.ts:264](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L264)
[stack.ts:336](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L336)

#### Methods

Expand Down Expand Up @@ -151,7 +182,7 @@ A Pulumi Output value.

###### Defined in

[stack.ts:277](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L277)
[stack.ts:418](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L418)

## Interfaces

Expand Down Expand Up @@ -233,6 +264,31 @@ Options specific to the Pulumi CDK App component.

Options for creating a Pulumi CDK Stack

Any Pulumi resource options provided at the Stack level will override those configured
at the App level

#### Example

```ts
new App('testapp', (scope: App) => {
// This stack will inherit the options from the App
new Stack(scope, 'teststack1');

// Override the options for this stack
new Stack(scope, 'teststack', {
providers: [
new native.Provider('custom-provider', { region: 'us-east-1' }),
],
props: { env: { region: 'us-east-1' } },
})
}, {
providers: [
new native.Provider('app-provider', { region: 'us-west-2' }),
]

})
```

#### Extends

- `ComponentResourceOptions`
Expand All @@ -241,7 +297,7 @@ Options for creating a Pulumi CDK Stack

| Property | Type | Description | Defined in |
| ------ | ------ | ------ | ------ |
| `props?` | `StackProps` | The CDK Stack props | [stack.ts:230](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L230) |
| `props?` | `StackProps` | The CDK Stack props | [stack.ts:297](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L297) |

## Type Aliases

Expand All @@ -255,7 +311,7 @@ Options for creating a Pulumi CDK Stack

#### Defined in

[stack.ts:24](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L24)
[stack.ts:26](https://github.com/pulumi/pulumi-cdk/blob/main/src/stack.ts#L26)

## Functions

Expand Down
Loading

0 comments on commit 596084d

Please sign in to comment.