Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline Usage of Prebuilt Providers #331

Merged
merged 12 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Choose a language:
## Documentation

* Install and run a quick start tutorial at [HashiCorp Learn](https://learn.hashicorp.com/terraform/cdktf/cdktf-install)
* Using CDK for Terraform CLI for importing Terraform [modules and providers](./docs/working-with-cdk-for-terraform/importing-providers-and-modules.md).
* Using CDK for Terraform CLI for importing Terraform [modules and providers](./docs/working-with-cdk-for-terraform/using-providers-and-modules.md).
* Explore the CDK for Terraform [CLI](./docs/cli-commands.md).
* Defining Terraform [outputs](./docs/working-with-cdk-for-terraform/terraform-outputs.md).
* Using Terraform [remote backend](./docs/working-with-cdk-for-terraform/remote-backend.md).
Expand Down
94 changes: 94 additions & 0 deletions docs/working-with-cdk-for-terraform/cdktf-json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# cdktf.json Configuration File

With the `cdktf.json` in your project root directory you can configure the behaviour of the Terraform CDK CLI:

## Specification

```ts
export enum Language {
TYPESCRIPT = 'typescript',
PYTHON = 'python',
DOTNET = 'dotnet', // not yet supported
JAVA = 'java', // not yet supported
}

export interface Config {
readonly app?: string; // The command to run in order to synthesize the code to Terraform compatible JSON
readonly language?: Language; // Target language for building provider or module bindings. Currently supported: `typescript` or `python`
readonly output: string; // Default: 'cdktf.out'. Where the synthesized JSON should go. Also will be the working directory for Terraform operations
readonly codeMakerOutput: string; // Default: '.gen'. Path where generated provider bindings will be rendered to.
readonly terraformProviders?: string[]; // Terraform Providers to build
readonly terraformModules?: string[]; // Terraform Modules to build
}
```

### Terraform Providers and Modules

The [following specifications](https://www.terraform.io/docs/configuration/provider-requirements.html#requiring-providers) schema should be followed for providers and modules

[source](https://www.terraform.io/docs/configuration/provider-requirements.html#source-addresses)@[version](https://www.terraform.io/docs/configuration/provider-requirements.html#version-constraints)

#### For HashiCorp maintained providers

Official HashiCorp [maintained providers](https://registry.terraform.io/browse/providers?tier=official) (e.g. `aws`, `google` or `azurerm`) can be specified in a short version like this: `"aws@~> 2.0"`

#### 3rd Party Providers

Community providers have to provide a fully qualified name, e.g. to define the Docker provider: `terraform-providers/docker@~> 2.0`

#### Modules

Similar to 3rd Party providers, the full registry namespace should be provided. Please note that only modules from the registry are supported at this point.

## Examples

### Minimal Configuration

A minimal configuration would define `app` only. This is useful, when planning to use [prebuilt providers]() and therefore no provider or modules bindings should be generated.
skorfmann marked this conversation as resolved.
Show resolved Hide resolved

```json
{
"app": "npm run --silent compile && node main.js"
}
```

### Changing Code Output Directories

This will synthesize JSON into `my-workdir` and all Terraform operations - such as `deploy` or `destroy` - will be performed in this directory.

```json
{
"app": "npm run --silent compile && node main.js",
"output": "my-workdir",
}
```

### Building Providers
skorfmann marked this conversation as resolved.
Show resolved Hide resolved

This will synthesize JSON into `my-workdir` and all Terraform operations - such as `deploy` or `destroy` - will be performed in this directory. See more details about this [here](./using-providers-and-modules.md)

```json
{
"language": "typescript",
"app": "npm run --silent compile && node main.js",
"terraformProviders": [
"aws@~> 2.0"
]
}
```

### Building Providers in Custom Directory

This will generate the `aws` provider bindings in the folder `./imports`.

```json
{
"language": "typescript",
skorfmann marked this conversation as resolved.
Show resolved Hide resolved
"app": "npm run --silent compile && node main.js",
"terraformProviders": [
"aws@~> 2.0"
],
"codeMakerOutput": "imports"
}
```

Original file line number Diff line number Diff line change
@@ -1,4 +1,26 @@
# Importing Providers and Modules
# Using Providers and Modules

## Prebuilt Providers

To improve the general user experience around provider imports and to allow building further abstractions on top of the Terraform provider bindings, a few popular providers are offered as prebuilt packages. At the moment the following providers are built and published to NPM / PyPi on a regular basis automatically.

- [AWS Provider](https://cdk.tf/provider/aws)
skorfmann marked this conversation as resolved.
Show resolved Hide resolved
- [Google Provider](https://cdk.tf/provider/google)
- [Azure Provider](https://cdk.tf/provider/azurerm)
- [Kubernetes Provider](https://cdk.tf/provider/kubernetes)
- [Docker Provider](https://cdk.tf/provider/docker)
- [Github Provider](https://cdk.tf/provider/github)
- [Null Provider](https://cdk.tf/provider/null)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I"m curious how this list was chosen. I was expecting all hashicorp official providers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly popularity, Github because I want to build an example case for Level 2 constructs and Null because it's a building block which useful for things like ecr asset

The current state is an advanced proof of concept. Mid / long term I think there would be more providers if not all at some point. For now, the goal is collecting feedback and building some examples to see if the UX is going in the right direction.

Things like:

  • versioning (version constraints for peer dependencies, how does it work in Python, ...)
  • major version changes (e.g. AWS 2.0 -> 3.0)
  • improve the auto publish pipeline
  • building upon these providers
  • adding tests to the provider publish pipeline


Please check the [Terraform CDK Proivders](https://cdk.tf/provider) organisation as well for an up to date list. As these are are normal npm / pypi packages, they can be used as any other dependency.
skorfmann marked this conversation as resolved.
Show resolved Hide resolved

e.g. in Typescript / Node:

```
anubhavmishra marked this conversation as resolved.
Show resolved Hide resolved
npm install -a @cdktf/provider-aws
```

## Build Providers and Modules
skorfmann marked this conversation as resolved.
Show resolved Hide resolved

CDK for Terraform allows you to import Terraform [providers](https://www.terraform.io/docs/providers/index.html) and [modules](https://www.terraform.io/docs/modules/index.html) to your project
using this workflow.
Expand Down Expand Up @@ -31,7 +53,7 @@ new MyStack(app, 'hello-terraform');
app.synth();
```

The project also has the `cdktf.json` file that defines what providers and modules are being used by the project.
The project also has the [cdktf.json](./cdktf-json.md) file that defines what providers and modules are being used by the project.

```bash
vim cdktf.json
Expand Down
4 changes: 4 additions & 0 deletions examples/typescript/aws-prebuilt/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.gen
cdktf.out
terraform.tfstate*
!tsconfig.json
15 changes: 15 additions & 0 deletions examples/typescript/aws-prebuilt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Typescript AWS with Prebuilt Provider

A CDK for Terraform application in TypeScript using the prebuilt [AWS provider](https://cdk.tf/provider/aws).
skorfmann marked this conversation as resolved.
Show resolved Hide resolved

## Usage

Install project dependencies:

```shell
yarn install
```

You can now edit the [main.ts](./main.ts) file if you want to modify any code.

Perform a `cdktf diff` to get a plan of what would be deployed.
3 changes: 3 additions & 0 deletions examples/typescript/aws-prebuilt/cdktf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "npm run --silent compile && node main.js"
}
49 changes: 49 additions & 0 deletions examples/typescript/aws-prebuilt/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Construct } from 'constructs';
import { App, TerraformStack, TerraformOutput } from 'cdktf';
import { DataAwsRegion, AwsProvider, DynamodbTable, SnsTopic } from '@cdktf/provider-aws'

export class HelloTerra extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);

new AwsProvider(this, 'aws', {
region: 'eu-central-1'
})

const region = new DataAwsRegion(this, 'region')

const table = new DynamodbTable(this, 'Hello', {
name: `my-first-table-${region.name}`,
hashKey: 'temp',
attribute: [
{ name: 'id', type: 'S' },
],
billingMode: "PAY_PER_REQUEST"
});

table.addOverride('hash_key', 'id')
// table.addOverride('hash_key', 'foo')
table.addOverride('lifecycle', { create_before_destroy: true })

const topicCount = 1
const topics = [...Array(topicCount).keys()].map((i) => {
return new SnsTopic(this, `Topic${i}`, {
displayName: `my-first-sns-topic${i}`
});
})

new TerraformOutput(this, 'table_name', {
value: table.name
})

topics.forEach((topic, i) => {
new TerraformOutput(this, `sns_topic${i}`, {
value: topic.name
})
})
}
}

const app = new App();
new HelloTerra(app, 'hello-terra');
app.synth();
21 changes: 21 additions & 0 deletions examples/typescript/aws-prebuilt/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "@examples/typescript-aws-prebuilt",
"version": "0.0.0",
"main": "index.js",
"license": "MPL-2.0",
"scripts": {
"build": "tsc",
"compile": "tsc --pretty",
"synth": "cdktf synth"
},
"devDependencies": {
"@types/node": "^14.0.26",
"cdktf-cli": "0.0.0",
"typescript": "^3.9.7"
},
"dependencies": {
"@cdktf/provider-aws": "^0.0.19",
"cdktf": "0.0.0",
"constructs": "^3.0.0"
}
}
33 changes: 33 additions & 0 deletions examples/typescript/aws-prebuilt/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"compilerOptions": {
"alwaysStrict": true,
"charset": "utf8",
"declaration": true,
"experimentalDecorators": true,
"inlineSourceMap": true,
"inlineSources": true,
"lib": [
"es2018"
],
"module": "CommonJS",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"stripInternal": true,
"target": "ES2018"
},
"include": [
"**/*.ts"
],
"exclude": [
"node_modules"
]
}
18 changes: 18 additions & 0 deletions examples/typescript/aws-prebuilt/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@types/node@^13.1.1":
version "13.1.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.4.tgz#4cfd90175a200ee9b02bd6b1cd19bc349741607e"
integrity sha512-Lue/mlp2egZJoHXZr4LndxDAd7i/7SQYhV0EjWfb/a4/OZ6tuVwMCVPiwkU5nsEipxEf7hmkSU7Em5VQ8P5NGA==

constructs@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/constructs/-/constructs-2.0.1.tgz#274d6b8ce6a698813495c444466b0c745349ddb5"
integrity sha512-edR85YFGI9TBT9byAo5vAfI0PRi+jFGzinwN3RAJwKfv6Yc9x9kALYfoEmgotp95qT7/k/iUQWHrH9BMJeqpdg==

typescript@^3.7.4:
version "3.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19"
integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==
2 changes: 1 addition & 1 deletion packages/cdktf-cli/bin/cmds/synth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Command implements yargs.CommandModule {
const outdir = argv.output;
const jsonOutput = argv.json;

if (!await fs.pathExists(config.codeMakerOutput)) {
if (config.checkCodeMakerOutput && !await fs.pathExists(config.codeMakerOutput)) {
console.error(`ERROR: synthesis failed, run "cdktf get" to generate providers in ${config.codeMakerOutput}`);
process.exit(1);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/cdktf-cli/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface Config {
readonly codeMakerOutput: string;
readonly terraformProviders?: string[];
readonly terraformModules?: string[];
checkCodeMakerOutput?: boolean;
}

export function readConfigSync(): Config {
Expand All @@ -28,5 +29,7 @@ export function readConfigSync(): Config {
};
}

config.checkCodeMakerOutput = Array.isArray(config.terraformModules) || Array.isArray(config.terraformProviders)
skorfmann marked this conversation as resolved.
Show resolved Hide resolved

return config;
}
17 changes: 16 additions & 1 deletion packages/cdktf-cli/templates/python/help
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

Synthesize:
cdktf synth Synthesize Terraform resources to cdktf.out/

Diff:
cdktf diff Perform a diff (terraform plan) for the given stack

Expand All @@ -19,4 +19,19 @@
Destroy:
cdktf destroy Destroy the given stack

Use Prebuilt Providers:

You can add one or multiple of the prebuilt providers listed below:

npm install -a @cdktf/provider-aws
npm install -a @cdktf/provider-google
npm install -a @cdktf/provider-azurerm
npm install -a @cdktf/provider-docker
npm install -a @cdktf/provider-github
npm install -a @cdktf/provider-null

Check for an up to date list here https://cdk.tf/provider

Alternatively, you can also build the provider bindings yourself:

========================================================================================================
48 changes: 48 additions & 0 deletions packages/cdktf-cli/templates/typescript-minimal/.hooks.sscaff.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const { execSync } = require('child_process');
const { readFileSync, writeFileSync } = require('fs');

const constructs_version = require('../../package.json').dependencies.constructs;

exports.post = ctx => {
// Terraform Cloud configuration settings if the organization name and workspace is set.
if (ctx.OrganizationName != '') {
console.log(`\nGenerating Terraform Cloud configuration for '${ctx.OrganizationName}' organization and '${ctx.WorkspaceName}' workspace.....`)
terraformCloudConfig(ctx.$base, ctx.OrganizationName, ctx.WorkspaceName);
}

const npm_cdktf = ctx.npm_cdktf;
const npm_cdktf_cli = ctx.npm_cdktf_cli;

if (!npm_cdktf) { throw new Error(`missing context "npm_cdktf"`); }
if (!npm_cdktf_cli) { throw new Error(`missing context "npm_cdktf_cli"`); }

installDeps([npm_cdktf, `constructs@${constructs_version}`]);
installDeps([npm_cdktf_cli, '@types/node', 'typescript'], true);

console.log(readFileSync('./help', 'utf-8'));
};

function installDeps(deps, isDev) {
const devDep = isDev ? '-D' : '';
// make sure we're installing dev dependencies as well
const env = Object.assign({}, process.env)
env['NODE_ENV'] = 'development'

execSync(`npm install ${devDep} ${deps.join(' ')}`, { stdio: 'inherit', env });
}

function terraformCloudConfig(baseName, organizationName, workspaceName) {
template = readFileSync('./main.ts', 'utf-8');

result = template.replace(`import { App, TerraformStack } from 'cdktf';`, `import { App, TerraformStack, RemoteBackend } from 'cdktf';`);
result = result.replace(`new MyStack(app, '${baseName}');`, `const stack = new MyStack(app, '${baseName}');
new RemoteBackend(stack, {
hostname: 'app.terraform.io',
organization: '${organizationName}',
workspaces: {
name: '${workspaceName}'
}
});`);

writeFileSync('./main.ts', result, 'utf-8');
}
Loading