nx-terraform 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +11 -0
  2. package/generators.json +15 -0
  3. package/package.json +18 -0
  4. package/src/generators/preset/generator.d.ts +4 -0
  5. package/src/generators/preset/generator.js +20 -0
  6. package/src/generators/preset/generator.js.map +1 -0
  7. package/src/generators/preset/schema.d.ts +4 -0
  8. package/src/generators/preset/schema.json +33 -0
  9. package/src/generators/terraform-backend/files/aws-s3-backend/README.md +70 -0
  10. package/src/generators/terraform-backend/files/aws-s3-backend/__ignoreFile__ +3 -0
  11. package/src/generators/terraform-backend/files/aws-s3-backend/backend.tf +3 -0
  12. package/src/generators/terraform-backend/files/aws-s3-backend/locals.tf +3 -0
  13. package/src/generators/terraform-backend/files/aws-s3-backend/main.tf +12 -0
  14. package/src/generators/terraform-backend/files/aws-s3-backend/package.json +12 -0
  15. package/src/generators/terraform-backend/files/aws-s3-backend/provider.tf +32 -0
  16. package/src/generators/terraform-backend/files/aws-s3-backend/s3.tf +37 -0
  17. package/src/generators/terraform-backend/files/aws-s3-backend/scripts/check_bucket.sh +11 -0
  18. package/src/generators/terraform-backend/files/aws-s3-backend/variables.tf +9 -0
  19. package/src/generators/terraform-backend/files/local-backend/README.md +19 -0
  20. package/src/generators/terraform-backend/files/local-backend/__ignoreFile__ +3 -0
  21. package/src/generators/terraform-backend/files/local-backend/backend.tf +5 -0
  22. package/src/generators/terraform-backend/files/local-backend/locals.tf +3 -0
  23. package/src/generators/terraform-backend/files/local-backend/main.tf +8 -0
  24. package/src/generators/terraform-backend/files/local-backend/package.json +12 -0
  25. package/src/generators/terraform-backend/files/local-backend/variables.tf +0 -0
  26. package/src/generators/terraform-backend/schema.d.ts +11 -0
  27. package/src/generators/terraform-backend/schema.json +38 -0
  28. package/src/generators/terraform-backend/terraform-backend.d.ts +4 -0
  29. package/src/generators/terraform-backend/terraform-backend.js +29 -0
  30. package/src/generators/terraform-backend/terraform-backend.js.map +1 -0
  31. package/src/index.d.ts +0 -0
  32. package/src/index.js +1 -0
  33. package/src/index.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # nx-terraform
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build nx-terraform` to build the library.
8
+
9
+ ## Running unit tests
10
+
11
+ Run `nx test nx-terraform` to execute the unit tests via [Jest](https://jestjs.io).
@@ -0,0 +1,15 @@
1
+ {
2
+ "generators": {
3
+ "preset": {
4
+ "factory": "./src/generators/preset/generator",
5
+ "schema": "./src/generators/preset/schema.json",
6
+ "description": "preset generator",
7
+ "x-use-standalone-layout": true
8
+ },
9
+ "terraform-backend": {
10
+ "factory": "./src/generators/terraform-backend/terraform-backend",
11
+ "schema": "./src/generators/terraform-backend/schema.json",
12
+ "description": "terraform-backend generator"
13
+ }
14
+ }
15
+ }
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "nx-terraform",
3
+ "version": "0.2.0",
4
+ "type": "commonjs",
5
+ "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
7
+ "repository": {
8
+ "url": "https://github.com/alexpialetski/nx-terraform"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "dependencies": {
14
+ "@nx/devkit": "22.0.2",
15
+ "tslib": "^2.3.0"
16
+ },
17
+ "generators": "./generators.json"
18
+ }
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { PresetGeneratorSchema } from './schema';
3
+ export declare function presetGenerator(tree: Tree, options: PresetGeneratorSchema): Promise<import("@nx/devkit").GeneratorCallback>;
4
+ export default presetGenerator;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.presetGenerator = presetGenerator;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const terraform_backend_1 = require("../terraform-backend/terraform-backend");
7
+ function presetGenerator(tree, options) {
8
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
9
+ const tasks = [];
10
+ // Scaffold terraform backend project
11
+ yield (0, terraform_backend_1.terraformBackendGenerator)(tree, {
12
+ name: 'terraform-setup',
13
+ backendType: options.backendType,
14
+ });
15
+ yield (0, devkit_1.formatFiles)(tree);
16
+ return (0, devkit_1.runTasksInSerial)(...tasks);
17
+ });
18
+ }
19
+ exports.default = presetGenerator;
20
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-terraform/src/generators/preset/generator.ts"],"names":[],"mappings":";;AAKA,0CAeC;;AApBD,uCAAiE;AAGjE,8EAAmF;AAEnF,SAAsB,eAAe,CACnC,IAAU,EACV,OAA8B;;QAE9B,MAAM,KAAK,GAAG,EAAE,CAAC;QAEjB,qCAAqC;QACrC,MAAM,IAAA,6CAAyB,EAAC,IAAI,EAAE;YACpC,IAAI,EAAE,iBAAiB;YACvB,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC,CAAC;QAEH,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;QAExB,OAAO,IAAA,yBAAgB,EAAC,GAAG,KAAK,CAAC,CAAC;IACpC,CAAC;CAAA;AAED,kBAAe,eAAe,CAAC"}
@@ -0,0 +1,4 @@
1
+ export interface PresetGeneratorSchema {
2
+ projectName: string;
3
+ backendType: 'aws-s3' | 'local';
4
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "NxTerraformPreset",
4
+ "title": "Nx Terraform preset",
5
+ "description": "Initializes a workspace with a Terraform backend project configured via nx-terraform preset.",
6
+ "type": "object",
7
+ "properties": {
8
+ "projectName": {
9
+ "type": "string",
10
+ "description": "Name of the initial Terraform backend project.",
11
+ "aliases": ["name"],
12
+ "$default": {
13
+ "$source": "argv",
14
+ "index": 0
15
+ },
16
+ "x-prompt": "What project name would you like to use?"
17
+ },
18
+ "backendType": {
19
+ "type": "string",
20
+ "description": "Terraform backend type to scaffold (aws-s3 or local).",
21
+ "enum": ["aws-s3", "local"],
22
+ "x-prompt": {
23
+ "message": "Select Terraform backend type",
24
+ "type": "list",
25
+ "items": [
26
+ { "value": "aws-s3", "label": "AWS S3 remote backend" },
27
+ { "value": "local", "label": "Local backend" }
28
+ ]
29
+ }
30
+ }
31
+ },
32
+ "required": ["projectName", "backendType"]
33
+ }
@@ -0,0 +1,70 @@
1
+ # Terraform Backend: AWS S3
2
+
3
+ This package (`<%= name %>`) provisions the AWS infrastructure required for a remote Terraform state backend using S3 and optional locking features.
4
+
5
+ ## 📋 Overview
6
+
7
+ This backend includes:
8
+
9
+ - **S3 Bucket** for Terraform state storage with versioning and object lock
10
+ - **Backend Configuration** file generation for cluster package initialization
11
+ - **Bucket Existence Validation** to handle temporary AWS account scenarios
12
+
13
+ ## 🏗️ Resources Created
14
+
15
+ ### S3 Bucket (`aws_s3_bucket.tf_state`)
16
+
17
+ - **Purpose**: Stores Terraform state for the cluster infrastructure
18
+ - **Features**:
19
+ - Versioning enabled for state history
20
+ - Object lock enabled for state protection
21
+ - Force destroy enabled for temporary accounts
22
+ - **Naming**: `<%= bucketNamePrefix %>-${data.aws_caller_identity.current.account_id}-${data.aws_region.current.region}` (prefix + AWS account id + region)
23
+
24
+ ### Backend Configuration (`local_file.backend_config`)
25
+
26
+ - **Purpose**: Generates `backend.config` file for cluster initialization
27
+ - **Content**: S3 bucket name, key, and region configuration
28
+ - **Usage**: Referenced by cluster package Terraform initialization
29
+
30
+ ### Bucket Existence Check (`data.external.check_bucket`)
31
+
32
+ - **Purpose**: Prevents resource conflicts in temporary AWS accounts
33
+ - **Script**: `scripts/check_bucket.sh`
34
+ - **Logic**: Only creates bucket if it doesn't already exist
35
+
36
+ ## 🔐 Security Considerations
37
+
38
+ ### S3 Bucket Security
39
+
40
+ - **Versioning**: Enabled to track state changes
41
+ - **Object Lock**: Prevents accidental state deletion
42
+ - **Access Control**: Controlled through IAM policies
43
+
44
+ ### Additional Security
45
+
46
+ For production environments, additionally consider:
47
+
48
+ - KMS encryption for S3 bucket
49
+ - Bucket policies for access control
50
+ - VPC endpoints for S3 access
51
+ - **Lifecycle Policies**: Can be configured for state retention
52
+ - **Monitoring**: CloudTrail logging for audit and compliance
53
+
54
+ ## 🔄 Dependencies
55
+
56
+ - **Upstream**: AWS Provider configuration
57
+ - **Downstream**: Packages that depend on initialized backend
58
+
59
+ ## 📊 Outputs
60
+
61
+ Generated by the Nx Terraform backend generator (type: <%= backendType %>).
62
+
63
+ - **backend.config**: Backend configuration for cluster package Terraform initialization
64
+ - **S3 bucket**: Terraform state storage with versioning and object lock enabled
65
+
66
+ After creation, other Terraform modules can reference the generated `backend.config` file for initialization.
67
+
68
+ ## 🧪 Validation
69
+
70
+ The included script `scripts/check_bucket.sh` prevents duplicate bucket creation when working in ephemeral AWS accounts.
@@ -0,0 +1,3 @@
1
+ backend.config
2
+ .terraform
3
+ tfplan
@@ -0,0 +1,3 @@
1
+ terraform {
2
+ backend "local" {}
3
+ }
@@ -0,0 +1,3 @@
1
+ locals {
2
+ bucket_name = "<%= bucketNamePrefix %>-${data.aws_caller_identity.current.account_id}-${data.aws_region.current.region}"
3
+ }
@@ -0,0 +1,12 @@
1
+ data "aws_region" "current" {}
2
+
3
+ data "aws_caller_identity" "current" {}
4
+
5
+ resource "local_file" "backend_config" {
6
+ content = <<EOF
7
+ bucket = "${local.bucket_name}"
8
+ key = "tf_state"
9
+ region = "${data.aws_region.current.region}"
10
+ EOF
11
+ filename = "backend.config"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "<%= name %>",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "clean": "rimraf terraform.tfstate.d tfplan"
7
+ },
8
+ "dependencies": {},
9
+ "devDependencies": {
10
+ "rimraf": "^5.0.10"
11
+ }
12
+ }
@@ -0,0 +1,32 @@
1
+ terraform {
2
+ required_providers {
3
+ aws = {
4
+ source = "hashicorp/aws"
5
+ version = "6.2.0"
6
+ }
7
+ local = {
8
+ source = "hashicorp/local"
9
+ version = "2.5.3"
10
+ }
11
+ external = {
12
+ source = "hashicorp/external"
13
+ version = "2.3.5"
14
+ }
15
+ }
16
+
17
+ # needed for NX cache reset when new module is added
18
+ # required_modules {
19
+ # github-oidc = {
20
+ # source = "terraform-module/github-oidc-provider/aws"
21
+ # version = "~> 1"
22
+ # }
23
+ # }
24
+ }
25
+
26
+ provider "aws" {
27
+ region = var.region
28
+ }
29
+
30
+ provider "local" {}
31
+
32
+ provider "external" {}
@@ -0,0 +1,37 @@
1
+ // check if bucket already exists
2
+ data "external" "check_bucket" {
3
+ program = ["bash", "./scripts/check_bucket.sh"]
4
+
5
+
6
+ query = {
7
+ bucket_name = local.bucket_name
8
+ }
9
+ }
10
+
11
+ resource "aws_s3_bucket" "tf_state" {
12
+ count = data.external.check_bucket.result.exists == "false" ? 1 : 0
13
+
14
+ bucket = local.bucket_name
15
+ object_lock_enabled = true
16
+ force_destroy = true
17
+
18
+ lifecycle {
19
+ prevent_destroy = false
20
+ }
21
+ }
22
+
23
+ resource "aws_s3_bucket_versioning" "tf_state" {
24
+ count = data.external.check_bucket.result.exists == "false" ? 1 : 0
25
+
26
+ bucket = local.bucket_name
27
+
28
+ versioning_configuration {
29
+ status = "Enabled"
30
+ }
31
+ }
32
+
33
+ resource "aws_s3_bucket_object_lock_configuration" "tf_state" {
34
+ count = data.external.check_bucket.result.exists == "false" ? 1 : 0
35
+
36
+ bucket = local.bucket_name
37
+ }
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+
3
+ # Exit if any of the intermediate steps fail
4
+ set -e
5
+ eval "$(jq -r '@sh "BUCKET_NAME=\(.bucket_name)"')"
6
+
7
+ if aws s3api head-bucket --bucket "$BUCKET_NAME" >/dev/null 2>&1; then
8
+ echo '{"exists": "true"}'
9
+ else
10
+ echo '{"exists": "false"}'
11
+ fi
@@ -0,0 +1,9 @@
1
+ variable "region" {
2
+ description = "AWS region to deploy resources"
3
+ type = string
4
+ }
5
+
6
+ variable "account_id" {
7
+ description = "AWS account ID"
8
+ type = string
9
+ }
@@ -0,0 +1,19 @@
1
+ # Terraform Backend: Local
2
+
3
+ This package (`<%= name %>`) sets up a local Terraform backend storing state on the filesystem. Suitable for quick prototyping or development where remote state durability and collaboration are not required.
4
+
5
+ State will be stored in the working directory under terraform.tfstate.
6
+
7
+ ## ⚠️ When To Use
8
+
9
+ Use the local backend only for:
10
+
11
+ - Personal development
12
+ - Small throwaway experiments
13
+ - CI jobs that don't need shared state
14
+
15
+ Avoid for:
16
+
17
+ - Team collaboration
18
+ - Production infrastructure
19
+ - Environments needing state locking/versioning
@@ -0,0 +1,3 @@
1
+ backend.config
2
+ .terraform
3
+ tfplan
@@ -0,0 +1,5 @@
1
+ terraform {
2
+ backend "local" {
3
+ path = local.state_file_path
4
+ }
5
+ }
@@ -0,0 +1,3 @@
1
+ locals {
2
+ state_file_path = "${path.module}/terraform.tfstate"
3
+ }
@@ -0,0 +1,8 @@
1
+
2
+ # TODO: update backend configuration with needed for local backend
3
+ resource "local_file" "backend_config" {
4
+ content = <<EOF
5
+ path = "${local.state_file_path}"
6
+ EOF
7
+ filename = "backend.config"
8
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "<%= name %>",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "clean": "rimraf terraform.tfstate.d tfplan"
7
+ },
8
+ "dependencies": {},
9
+ "devDependencies": {
10
+ "rimraf": "^5.0.10"
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ export interface TerraformBackendGeneratorSchema {
2
+ name: string;
3
+ backendType: 'aws-s3' | 'local';
4
+ bucketNamePrefix?: string;
5
+ }
6
+
7
+ export interface TerraformBackendGeneratorNormalizedSchema
8
+ extends TerraformBackendGeneratorSchema {
9
+ bucketNamePrefix: string;
10
+ ignoreFile: string;
11
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "TerraformBackend",
4
+ "title": "",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "description": "",
10
+ "$default": {
11
+ "$source": "argv",
12
+ "index": 0
13
+ },
14
+ "x-prompt": "What name would you like to use?"
15
+ },
16
+ "backendType": {
17
+ "type": "string",
18
+ "description": "Type of Terraform backend to generate (aws-s3 or local).",
19
+ "enum": ["aws-s3", "local"],
20
+ "x-prompt": {
21
+ "message": "Which Terraform backend type?",
22
+ "type": "list",
23
+ "items": [
24
+ { "value": "aws-s3", "label": "AWS S3 remote backend" },
25
+ { "value": "local", "label": "Local backend (local state files)" }
26
+ ]
27
+ }
28
+ },
29
+ "bucketNamePrefix": {
30
+ "type": "string",
31
+ "description": "Optional prefix for derived bucket name (aws-s3). Default: tf-rs-school-",
32
+ "minLength": 3,
33
+ "pattern": "^[a-z0-9][a-z0-9.-]{1,30}$",
34
+ "x-prompt": "(Optional) Prefix for bucket name? Leave blank for tf-rs-school-"
35
+ }
36
+ },
37
+ "required": ["name", "backendType"]
38
+ }
@@ -0,0 +1,4 @@
1
+ import { Tree } from '@nx/devkit';
2
+ import { TerraformBackendGeneratorSchema } from './schema';
3
+ export declare function terraformBackendGenerator(tree: Tree, options: TerraformBackendGeneratorSchema): Promise<void>;
4
+ export default terraformBackendGenerator;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.terraformBackendGenerator = terraformBackendGenerator;
4
+ const tslib_1 = require("tslib");
5
+ const devkit_1 = require("@nx/devkit");
6
+ const path = require("path");
7
+ function terraformBackendGenerator(tree, options) {
8
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
9
+ const projectRoot = `packages/${options.name}`;
10
+ const normalizedOptions = normalizeOptions(options);
11
+ // Minimal project.json configuration
12
+ (0, devkit_1.addProjectConfiguration)(tree, normalizedOptions.name, {
13
+ root: projectRoot,
14
+ projectType: 'application',
15
+ sourceRoot: `${projectRoot}`,
16
+ targets: {},
17
+ });
18
+ // Select template directory based on backendType
19
+ const templateDir = path.join(__dirname, 'files', normalizedOptions.backendType === 'aws-s3'
20
+ ? 'aws-s3-backend'
21
+ : 'local-backend');
22
+ (0, devkit_1.generateFiles)(tree, templateDir, projectRoot, normalizedOptions);
23
+ // No post-processing needed; EJS handled bucket name logic.
24
+ yield (0, devkit_1.formatFiles)(tree);
25
+ });
26
+ }
27
+ const normalizeOptions = (options) => (Object.assign(Object.assign({}, options), { bucketNamePrefix: options.bucketNamePrefix || 'terraform-state', ignoreFile: '.gitignore' }));
28
+ exports.default = terraformBackendGenerator;
29
+ //# sourceMappingURL=terraform-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terraform-backend.js","sourceRoot":"","sources":["../../../../../../packages/nx-terraform/src/generators/terraform-backend/terraform-backend.ts"],"names":[],"mappings":";;AAYA,8DA4BC;;AAxCD,uCAKoB;AACpB,6BAA6B;AAM7B,SAAsB,yBAAyB,CAC7C,IAAU,EACV,OAAwC;;QAExC,MAAM,WAAW,GAAG,YAAY,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAEpD,qCAAqC;QACrC,IAAA,gCAAuB,EAAC,IAAI,EAAE,iBAAiB,CAAC,IAAI,EAAE;YACpD,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,aAAa;YAC1B,UAAU,EAAE,GAAG,WAAW,EAAE;YAC5B,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QAEH,iDAAiD;QACjD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAC3B,SAAS,EACT,OAAO,EACP,iBAAiB,CAAC,WAAW,KAAK,QAAQ;YACxC,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,eAAe,CACpB,CAAC;QAEF,IAAA,sBAAa,EAAC,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;QAEjE,4DAA4D;QAC5D,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CAAA;AAED,MAAM,gBAAgB,GAAG,CACvB,OAAwC,EACG,EAAE,CAAC,iCAC3C,OAAO,KACV,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,iBAAiB,EAC/D,UAAU,EAAE,YAAY,IACxB,CAAC;AAEH,kBAAe,yBAAyB,CAAC"}
package/src/index.d.ts ADDED
File without changes
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/nx-terraform/src/index.ts"],"names":[],"mappings":""}