@salte-common/terraflow 0.1.0-alpha.1
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.
- package/LICENSE +22 -0
- package/README.md +278 -0
- package/RELEASE_SUMMARY.md +53 -0
- package/STANDARDS_COMPLIANCE.md +85 -0
- package/bin/terraflow.js +3 -0
- package/bin/tf.js +3 -0
- package/dist/commands/apply.d.ts +7 -0
- package/dist/commands/apply.js +12 -0
- package/dist/commands/base.d.ts +7 -0
- package/dist/commands/base.js +12 -0
- package/dist/commands/config.d.ts +25 -0
- package/dist/commands/config.js +354 -0
- package/dist/commands/destroy.d.ts +7 -0
- package/dist/commands/destroy.js +12 -0
- package/dist/commands/init.d.ts +68 -0
- package/dist/commands/init.js +131 -0
- package/dist/commands/plan.d.ts +7 -0
- package/dist/commands/plan.js +12 -0
- package/dist/core/backend-state.d.ts +25 -0
- package/dist/core/backend-state.js +77 -0
- package/dist/core/config.d.ts +83 -0
- package/dist/core/config.js +295 -0
- package/dist/core/context.d.ts +52 -0
- package/dist/core/context.js +192 -0
- package/dist/core/environment.d.ts +62 -0
- package/dist/core/environment.js +205 -0
- package/dist/core/errors.d.ts +22 -0
- package/dist/core/errors.js +36 -0
- package/dist/core/plugin-loader.d.ts +21 -0
- package/dist/core/plugin-loader.js +136 -0
- package/dist/core/terraform.d.ts +45 -0
- package/dist/core/terraform.js +247 -0
- package/dist/core/validator.d.ts +103 -0
- package/dist/core/validator.js +304 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +184 -0
- package/dist/plugins/auth/aws-assume-role.d.ts +10 -0
- package/dist/plugins/auth/aws-assume-role.js +110 -0
- package/dist/plugins/auth/azure-service-principal.d.ts +10 -0
- package/dist/plugins/auth/azure-service-principal.js +99 -0
- package/dist/plugins/auth/gcp-service-account.d.ts +10 -0
- package/dist/plugins/auth/gcp-service-account.js +105 -0
- package/dist/plugins/backends/azurerm.d.ts +10 -0
- package/dist/plugins/backends/azurerm.js +117 -0
- package/dist/plugins/backends/gcs.d.ts +10 -0
- package/dist/plugins/backends/gcs.js +75 -0
- package/dist/plugins/backends/local.d.ts +11 -0
- package/dist/plugins/backends/local.js +37 -0
- package/dist/plugins/backends/s3.d.ts +10 -0
- package/dist/plugins/backends/s3.js +185 -0
- package/dist/plugins/secrets/aws-secrets.d.ts +12 -0
- package/dist/plugins/secrets/aws-secrets.js +125 -0
- package/dist/plugins/secrets/azure-keyvault.d.ts +12 -0
- package/dist/plugins/secrets/azure-keyvault.js +178 -0
- package/dist/plugins/secrets/env.d.ts +24 -0
- package/dist/plugins/secrets/env.js +62 -0
- package/dist/plugins/secrets/gcp-secret-manager.d.ts +12 -0
- package/dist/plugins/secrets/gcp-secret-manager.js +157 -0
- package/dist/templates/application/go/go.mod.template +4 -0
- package/dist/templates/application/go/main.template +8 -0
- package/dist/templates/application/go/test.template +11 -0
- package/dist/templates/application/javascript/main.template +14 -0
- package/dist/templates/application/javascript/test.template +8 -0
- package/dist/templates/application/python/main.template +13 -0
- package/dist/templates/application/python/requirements.txt.template +3 -0
- package/dist/templates/application/python/test.template +8 -0
- package/dist/templates/application/typescript/main.template +14 -0
- package/dist/templates/application/typescript/test.template +8 -0
- package/dist/templates/application/typescript/tsconfig.json.template +20 -0
- package/dist/templates/config/README.md.template +82 -0
- package/dist/templates/config/env.example.template +22 -0
- package/dist/templates/config/gitignore.template +40 -0
- package/dist/templates/config/tfwconfig.yml.template +69 -0
- package/dist/templates/templates/application/go/go.mod.template +4 -0
- package/dist/templates/templates/application/go/main.template +8 -0
- package/dist/templates/templates/application/go/test.template +11 -0
- package/dist/templates/templates/application/javascript/main.template +14 -0
- package/dist/templates/templates/application/javascript/test.template +8 -0
- package/dist/templates/templates/application/python/main.template +13 -0
- package/dist/templates/templates/application/python/requirements.txt.template +3 -0
- package/dist/templates/templates/application/python/test.template +8 -0
- package/dist/templates/templates/application/typescript/main.template +14 -0
- package/dist/templates/templates/application/typescript/test.template +8 -0
- package/dist/templates/templates/application/typescript/tsconfig.json.template +20 -0
- package/dist/templates/templates/config/README.md.template +82 -0
- package/dist/templates/templates/config/env.example.template +22 -0
- package/dist/templates/templates/config/gitignore.template +40 -0
- package/dist/templates/templates/config/tfwconfig.yml.template +69 -0
- package/dist/templates/templates/terraform/aws/_init.tf.template +24 -0
- package/dist/templates/templates/terraform/aws/inputs.tf.template +11 -0
- package/dist/templates/templates/terraform/azure/_init.tf.template +19 -0
- package/dist/templates/templates/terraform/azure/inputs.tf.template +11 -0
- package/dist/templates/templates/terraform/gcp/_init.tf.template +20 -0
- package/dist/templates/templates/terraform/gcp/inputs.tf.template +16 -0
- package/dist/templates/templates/terraform/locals.tf.template +9 -0
- package/dist/templates/templates/terraform/main.tf.template +8 -0
- package/dist/templates/templates/terraform/modules/inputs.tf.template +5 -0
- package/dist/templates/templates/terraform/modules/main.tf.template +2 -0
- package/dist/templates/templates/terraform/modules/outputs.tf.template +2 -0
- package/dist/templates/templates/terraform/outputs.tf.template +6 -0
- package/dist/templates/terraform/aws/_init.tf.template +24 -0
- package/dist/templates/terraform/aws/inputs.tf.template +11 -0
- package/dist/templates/terraform/azure/_init.tf.template +19 -0
- package/dist/templates/terraform/azure/inputs.tf.template +11 -0
- package/dist/templates/terraform/gcp/_init.tf.template +20 -0
- package/dist/templates/terraform/gcp/inputs.tf.template +16 -0
- package/dist/templates/terraform/locals.tf.template +9 -0
- package/dist/templates/terraform/main.tf.template +8 -0
- package/dist/templates/terraform/modules/inputs.tf.template +5 -0
- package/dist/templates/terraform/modules/main.tf.template +2 -0
- package/dist/templates/terraform/modules/outputs.tf.template +2 -0
- package/dist/templates/terraform/outputs.tf.template +6 -0
- package/dist/types/config.d.ts +92 -0
- package/dist/types/config.js +6 -0
- package/dist/types/context.d.ts +59 -0
- package/dist/types/context.js +6 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +23 -0
- package/dist/types/plugins.d.ts +77 -0
- package/dist/types/plugins.js +6 -0
- package/dist/utils/cloud.d.ts +43 -0
- package/dist/utils/cloud.js +150 -0
- package/dist/utils/git.d.ts +88 -0
- package/dist/utils/git.js +258 -0
- package/dist/utils/logger.d.ts +67 -0
- package/dist/utils/logger.js +121 -0
- package/dist/utils/scaffolding.d.ts +92 -0
- package/dist/utils/scaffolding.js +338 -0
- package/dist/utils/templates.d.ts +25 -0
- package/dist/utils/templates.js +70 -0
- package/package.json +60 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GCS backend plugin
|
|
4
|
+
* Handles Google Cloud Storage Terraform state backend
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.gcsBackend = void 0;
|
|
8
|
+
const errors_1 = require("../../core/errors");
|
|
9
|
+
const logger_1 = require("../../utils/logger");
|
|
10
|
+
const templates_1 = require("../../utils/templates");
|
|
11
|
+
/**
|
|
12
|
+
* GCS backend plugin
|
|
13
|
+
*/
|
|
14
|
+
exports.gcsBackend = {
|
|
15
|
+
name: 'gcs',
|
|
16
|
+
/**
|
|
17
|
+
* Validate GCS backend configuration
|
|
18
|
+
* @param config - Backend configuration
|
|
19
|
+
* @throws {ConfigError} If configuration is invalid
|
|
20
|
+
*/
|
|
21
|
+
validate: async (config) => {
|
|
22
|
+
if (!config.config) {
|
|
23
|
+
throw new errors_1.ConfigError('GCS backend requires configuration');
|
|
24
|
+
}
|
|
25
|
+
const gcsConfig = config.config;
|
|
26
|
+
// Required fields
|
|
27
|
+
if (!gcsConfig.bucket) {
|
|
28
|
+
throw new errors_1.ConfigError('GCS backend requires "bucket" configuration');
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
/**
|
|
32
|
+
* Generate Terraform backend configuration arguments
|
|
33
|
+
* @param config - Backend configuration
|
|
34
|
+
* @param context - Execution context (for template variable resolution)
|
|
35
|
+
* @returns Array of -backend-config arguments for terraform init
|
|
36
|
+
*/
|
|
37
|
+
getBackendConfig: async (config, context) => {
|
|
38
|
+
if (!config.config) {
|
|
39
|
+
throw new errors_1.ConfigError('GCS backend requires configuration');
|
|
40
|
+
}
|
|
41
|
+
// Build template context from execution context
|
|
42
|
+
const templateVars = {
|
|
43
|
+
...context.templateVars,
|
|
44
|
+
};
|
|
45
|
+
// Add cloud-specific variables
|
|
46
|
+
if (context.cloud.gcpProjectId) {
|
|
47
|
+
templateVars.GCP_PROJECT_ID = context.cloud.gcpProjectId;
|
|
48
|
+
}
|
|
49
|
+
// Resolve template variables in config
|
|
50
|
+
const resolvedConfig = templates_1.TemplateUtils.resolveObject(config.config, templateVars);
|
|
51
|
+
const gcsConfig = resolvedConfig;
|
|
52
|
+
// Build backend-config arguments
|
|
53
|
+
const backendArgs = [];
|
|
54
|
+
// Required fields
|
|
55
|
+
backendArgs.push(`-backend-config=bucket=${gcsConfig.bucket}`);
|
|
56
|
+
// Optional fields with defaults
|
|
57
|
+
const prefix = gcsConfig.prefix || 'terraform/state';
|
|
58
|
+
backendArgs.push(`-backend-config=prefix=${prefix}`);
|
|
59
|
+
if (gcsConfig.credentials) {
|
|
60
|
+
backendArgs.push(`-backend-config=credentials=${gcsConfig.credentials}`);
|
|
61
|
+
}
|
|
62
|
+
if (gcsConfig.impersonate_service_account) {
|
|
63
|
+
backendArgs.push(`-backend-config=impersonate_service_account=${gcsConfig.impersonate_service_account}`);
|
|
64
|
+
}
|
|
65
|
+
if (gcsConfig.access_token) {
|
|
66
|
+
backendArgs.push(`-backend-config=access_token=${gcsConfig.access_token}`);
|
|
67
|
+
}
|
|
68
|
+
if (gcsConfig.encryption_key) {
|
|
69
|
+
backendArgs.push(`-backend-config=encryption_key=${gcsConfig.encryption_key}`);
|
|
70
|
+
}
|
|
71
|
+
logger_1.Logger.debug(`Generated ${backendArgs.length} backend-config arguments for GCS backend`);
|
|
72
|
+
return backendArgs;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=gcs.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local backend plugin
|
|
3
|
+
* Handles local Terraform state backend
|
|
4
|
+
*/
|
|
5
|
+
import type { BackendPlugin } from '../../types';
|
|
6
|
+
/**
|
|
7
|
+
* Local backend plugin
|
|
8
|
+
* Default backend that stores state locally in terraform.tfstate
|
|
9
|
+
*/
|
|
10
|
+
export declare const localBackend: BackendPlugin;
|
|
11
|
+
//# sourceMappingURL=local.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Local backend plugin
|
|
4
|
+
* Handles local Terraform state backend
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.localBackend = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Local backend plugin
|
|
10
|
+
* Default backend that stores state locally in terraform.tfstate
|
|
11
|
+
*/
|
|
12
|
+
exports.localBackend = {
|
|
13
|
+
name: 'local',
|
|
14
|
+
/**
|
|
15
|
+
* Validate backend configuration
|
|
16
|
+
* Local backend always validates successfully (no configuration needed)
|
|
17
|
+
* @param _config - Backend configuration (ignored for local backend)
|
|
18
|
+
*/
|
|
19
|
+
validate: async (_config) => {
|
|
20
|
+
// Local backend requires no validation
|
|
21
|
+
// State is stored in terraform.tfstate in the working directory
|
|
22
|
+
return Promise.resolve();
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* Generate Terraform backend configuration arguments
|
|
26
|
+
* Local backend requires no backend-config flags
|
|
27
|
+
* @param _config - Backend configuration (ignored for local backend)
|
|
28
|
+
* @param _context - Execution context (ignored for local backend)
|
|
29
|
+
* @returns Empty array (no backend-config flags needed)
|
|
30
|
+
*/
|
|
31
|
+
getBackendConfig: async (_config, _context) => {
|
|
32
|
+
// Local backend doesn't require any -backend-config flags
|
|
33
|
+
// Terraform uses local backend by default if no backend is configured
|
|
34
|
+
return [];
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=local.js.map
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* S3 backend plugin
|
|
4
|
+
* Handles AWS S3 Terraform state backend
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.s3Backend = void 0;
|
|
41
|
+
const errors_1 = require("../../core/errors");
|
|
42
|
+
const logger_1 = require("../../utils/logger");
|
|
43
|
+
const templates_1 = require("../../utils/templates");
|
|
44
|
+
/**
|
|
45
|
+
* S3 backend plugin
|
|
46
|
+
*/
|
|
47
|
+
exports.s3Backend = {
|
|
48
|
+
name: 's3',
|
|
49
|
+
/**
|
|
50
|
+
* Validate S3 backend configuration
|
|
51
|
+
* @param config - Backend configuration
|
|
52
|
+
* @throws {ConfigError} If configuration is invalid
|
|
53
|
+
*/
|
|
54
|
+
validate: async (config) => {
|
|
55
|
+
if (!config.config) {
|
|
56
|
+
throw new errors_1.ConfigError('S3 backend requires configuration');
|
|
57
|
+
}
|
|
58
|
+
const s3Config = config.config;
|
|
59
|
+
// Required fields
|
|
60
|
+
if (!s3Config.bucket) {
|
|
61
|
+
throw new errors_1.ConfigError('S3 backend requires "bucket" configuration');
|
|
62
|
+
}
|
|
63
|
+
if (!s3Config.key) {
|
|
64
|
+
throw new errors_1.ConfigError('S3 backend requires "key" configuration');
|
|
65
|
+
}
|
|
66
|
+
// Warn if encrypt is false (not secure)
|
|
67
|
+
if (s3Config.encrypt === false) {
|
|
68
|
+
logger_1.Logger.warn('⚠️ S3 backend encryption is disabled. This is not recommended for production use.');
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Generate Terraform backend configuration arguments
|
|
73
|
+
* @param config - Backend configuration
|
|
74
|
+
* @param context - Execution context (for template variable resolution)
|
|
75
|
+
* @returns Array of -backend-config arguments for terraform init
|
|
76
|
+
*/
|
|
77
|
+
getBackendConfig: async (config, context) => {
|
|
78
|
+
if (!config.config) {
|
|
79
|
+
throw new errors_1.ConfigError('S3 backend requires configuration');
|
|
80
|
+
}
|
|
81
|
+
// Resolve template variables in config
|
|
82
|
+
const resolvedConfig = templates_1.TemplateUtils.resolveObject(config.config, context.templateVars);
|
|
83
|
+
// Apply defaults
|
|
84
|
+
const s3Config = {
|
|
85
|
+
encrypt: true, // Default: encryption enabled
|
|
86
|
+
dynamodb_table: 'terraform-statelock', // Default: standard DynamoDB table name
|
|
87
|
+
...resolvedConfig,
|
|
88
|
+
};
|
|
89
|
+
// Build backend-config arguments
|
|
90
|
+
const backendArgs = [];
|
|
91
|
+
// Required fields
|
|
92
|
+
backendArgs.push(`-backend-config=bucket=${s3Config.bucket}`);
|
|
93
|
+
backendArgs.push(`-backend-config=key=${s3Config.key}`);
|
|
94
|
+
// Optional fields
|
|
95
|
+
if (s3Config.region) {
|
|
96
|
+
backendArgs.push(`-backend-config=region=${s3Config.region}`);
|
|
97
|
+
}
|
|
98
|
+
if (s3Config.encrypt !== undefined) {
|
|
99
|
+
backendArgs.push(`-backend-config=encrypt=${s3Config.encrypt}`);
|
|
100
|
+
}
|
|
101
|
+
if (s3Config.dynamodb_table) {
|
|
102
|
+
backendArgs.push(`-backend-config=dynamodb_table=${s3Config.dynamodb_table}`);
|
|
103
|
+
}
|
|
104
|
+
if (s3Config.kms_key_id) {
|
|
105
|
+
backendArgs.push(`-backend-config=kms_key_id=${s3Config.kms_key_id}`);
|
|
106
|
+
}
|
|
107
|
+
// AWS credential options (optional)
|
|
108
|
+
if (s3Config.profile) {
|
|
109
|
+
backendArgs.push(`-backend-config=profile=${s3Config.profile}`);
|
|
110
|
+
}
|
|
111
|
+
if (s3Config.role_arn) {
|
|
112
|
+
backendArgs.push(`-backend-config=role_arn=${s3Config.role_arn}`);
|
|
113
|
+
}
|
|
114
|
+
if (s3Config.session_name) {
|
|
115
|
+
backendArgs.push(`-backend-config=session_name=${s3Config.session_name}`);
|
|
116
|
+
}
|
|
117
|
+
if (s3Config.external_id) {
|
|
118
|
+
backendArgs.push(`-backend-config=external_id=${s3Config.external_id}`);
|
|
119
|
+
}
|
|
120
|
+
if (s3Config.assume_role_duration_seconds) {
|
|
121
|
+
backendArgs.push(`-backend-config=assume_role_duration_seconds=${s3Config.assume_role_duration_seconds}`);
|
|
122
|
+
}
|
|
123
|
+
// Advanced options (optional)
|
|
124
|
+
if (s3Config.access_key) {
|
|
125
|
+
backendArgs.push(`-backend-config=access_key=${s3Config.access_key}`);
|
|
126
|
+
}
|
|
127
|
+
if (s3Config.secret_key) {
|
|
128
|
+
backendArgs.push(`-backend-config=secret_key=${s3Config.secret_key}`);
|
|
129
|
+
}
|
|
130
|
+
if (s3Config.token) {
|
|
131
|
+
backendArgs.push(`-backend-config=token=${s3Config.token}`);
|
|
132
|
+
}
|
|
133
|
+
if (s3Config.iam_endpoint) {
|
|
134
|
+
backendArgs.push(`-backend-config=iam_endpoint=${s3Config.iam_endpoint}`);
|
|
135
|
+
}
|
|
136
|
+
if (s3Config.max_retries !== undefined) {
|
|
137
|
+
backendArgs.push(`-backend-config=max_retries=${s3Config.max_retries}`);
|
|
138
|
+
}
|
|
139
|
+
if (s3Config.sse_customer_key) {
|
|
140
|
+
backendArgs.push(`-backend-config=sse_customer_key=${s3Config.sse_customer_key}`);
|
|
141
|
+
}
|
|
142
|
+
if (s3Config.skip_credentials_validation !== undefined) {
|
|
143
|
+
backendArgs.push(`-backend-config=skip_credentials_validation=${s3Config.skip_credentials_validation}`);
|
|
144
|
+
}
|
|
145
|
+
if (s3Config.skip_metadata_api_check !== undefined) {
|
|
146
|
+
backendArgs.push(`-backend-config=skip_metadata_api_check=${s3Config.skip_metadata_api_check}`);
|
|
147
|
+
}
|
|
148
|
+
if (s3Config.skip_region_validation !== undefined) {
|
|
149
|
+
backendArgs.push(`-backend-config=skip_region_validation=${s3Config.skip_region_validation}`);
|
|
150
|
+
}
|
|
151
|
+
if (s3Config.force_path_style !== undefined) {
|
|
152
|
+
backendArgs.push(`-backend-config=force_path_style=${s3Config.force_path_style}`);
|
|
153
|
+
}
|
|
154
|
+
return backendArgs;
|
|
155
|
+
},
|
|
156
|
+
/**
|
|
157
|
+
* Optional setup hook to verify bucket exists
|
|
158
|
+
* @param config - Backend configuration
|
|
159
|
+
* @param context - Execution context
|
|
160
|
+
*/
|
|
161
|
+
setup: async (config, context) => {
|
|
162
|
+
// Resolve template variables in config
|
|
163
|
+
const resolvedConfig = templates_1.TemplateUtils.resolveObject(config.config, context.templateVars);
|
|
164
|
+
const bucket = resolvedConfig.bucket;
|
|
165
|
+
if (!bucket) {
|
|
166
|
+
return; // Validation will catch this
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
// Try to check if bucket exists using AWS CLI
|
|
170
|
+
// This is optional, so we'll just log if it fails
|
|
171
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
172
|
+
execSync(`aws s3api head-bucket --bucket ${bucket}`, {
|
|
173
|
+
stdio: 'pipe',
|
|
174
|
+
encoding: 'utf8',
|
|
175
|
+
});
|
|
176
|
+
logger_1.Logger.debug(`✅ S3 bucket ${bucket} exists and is accessible`);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// Bucket might not exist or we might not have permissions
|
|
180
|
+
// This is not fatal - terraform init will handle it
|
|
181
|
+
logger_1.Logger.debug(`Could not verify S3 bucket ${bucket}: ${error instanceof Error ? error.message : String(error)}`);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
//# sourceMappingURL=s3.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Secrets Manager plugin
|
|
3
|
+
* Retrieves secrets from AWS Secrets Manager and converts them to TF_VAR_* environment variables
|
|
4
|
+
*
|
|
5
|
+
* CONVENTION: Every key in the secret automatically becomes TF_VAR_{key}
|
|
6
|
+
*/
|
|
7
|
+
import type { SecretsPlugin } from '../../types';
|
|
8
|
+
/**
|
|
9
|
+
* AWS Secrets Manager secrets plugin
|
|
10
|
+
*/
|
|
11
|
+
export declare const awsSecrets: SecretsPlugin;
|
|
12
|
+
//# sourceMappingURL=aws-secrets.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AWS Secrets Manager plugin
|
|
4
|
+
* Retrieves secrets from AWS Secrets Manager and converts them to TF_VAR_* environment variables
|
|
5
|
+
*
|
|
6
|
+
* CONVENTION: Every key in the secret automatically becomes TF_VAR_{key}
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.awsSecrets = void 0;
|
|
10
|
+
const client_secrets_manager_1 = require("@aws-sdk/client-secrets-manager");
|
|
11
|
+
const errors_1 = require("../../core/errors");
|
|
12
|
+
const logger_1 = require("../../utils/logger");
|
|
13
|
+
/**
|
|
14
|
+
* AWS Secrets Manager secrets plugin
|
|
15
|
+
*/
|
|
16
|
+
exports.awsSecrets = {
|
|
17
|
+
name: 'aws-secrets',
|
|
18
|
+
/**
|
|
19
|
+
* Validate AWS Secrets Manager configuration
|
|
20
|
+
* @param config - Secrets configuration
|
|
21
|
+
* @throws {ConfigError} If configuration is invalid
|
|
22
|
+
*/
|
|
23
|
+
validate: async (config) => {
|
|
24
|
+
if (!config.config) {
|
|
25
|
+
throw new errors_1.ConfigError('AWS Secrets Manager requires configuration');
|
|
26
|
+
}
|
|
27
|
+
const awsConfig = config.config;
|
|
28
|
+
// secret_name is required
|
|
29
|
+
if (!awsConfig.secret_name) {
|
|
30
|
+
throw new errors_1.ConfigError('AWS Secrets Manager requires "secret_name" configuration');
|
|
31
|
+
}
|
|
32
|
+
// region should be set (from config or AWS_REGION env)
|
|
33
|
+
const region = awsConfig.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION;
|
|
34
|
+
if (!region) {
|
|
35
|
+
throw new errors_1.ConfigError('AWS Secrets Manager requires "region" configuration or AWS_REGION environment variable');
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* Retrieve secrets from AWS Secrets Manager
|
|
40
|
+
* All keys are automatically prefixed with TF_VAR_
|
|
41
|
+
* @param config - Secrets configuration
|
|
42
|
+
* @param context - Execution context
|
|
43
|
+
* @returns Record of environment variable key-value pairs (prefixed with TF_VAR_)
|
|
44
|
+
*/
|
|
45
|
+
getSecrets: async (config, context) => {
|
|
46
|
+
if (!config.config) {
|
|
47
|
+
throw new errors_1.ConfigError('AWS Secrets Manager requires configuration');
|
|
48
|
+
}
|
|
49
|
+
const awsConfig = config.config;
|
|
50
|
+
const secretName = awsConfig.secret_name;
|
|
51
|
+
// Determine region: config > context > env > default
|
|
52
|
+
const region = awsConfig.region ||
|
|
53
|
+
context.cloud.awsRegion ||
|
|
54
|
+
process.env.AWS_REGION ||
|
|
55
|
+
process.env.AWS_DEFAULT_REGION ||
|
|
56
|
+
'us-east-1';
|
|
57
|
+
try {
|
|
58
|
+
logger_1.Logger.debug(`Fetching secret "${secretName}" from AWS Secrets Manager (region: ${region})`);
|
|
59
|
+
// Create Secrets Manager client
|
|
60
|
+
const client = new client_secrets_manager_1.SecretsManagerClient({
|
|
61
|
+
region,
|
|
62
|
+
});
|
|
63
|
+
// Get secret value
|
|
64
|
+
const command = new client_secrets_manager_1.GetSecretValueCommand({
|
|
65
|
+
SecretId: secretName,
|
|
66
|
+
});
|
|
67
|
+
const response = await client.send(command);
|
|
68
|
+
if (!response.SecretString) {
|
|
69
|
+
throw new errors_1.ConfigError(`Secret "${secretName}" exists but does not contain a string value. Binary secrets are not supported.`);
|
|
70
|
+
}
|
|
71
|
+
// Parse JSON secret value
|
|
72
|
+
let secretData;
|
|
73
|
+
try {
|
|
74
|
+
secretData = JSON.parse(response.SecretString);
|
|
75
|
+
}
|
|
76
|
+
catch (parseError) {
|
|
77
|
+
throw new errors_1.ConfigError(`Secret "${secretName}" does not contain valid JSON. Error: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
78
|
+
}
|
|
79
|
+
// Convert all keys to TF_VAR_{key} format
|
|
80
|
+
const tfVars = {};
|
|
81
|
+
for (const key in secretData) {
|
|
82
|
+
if (Object.prototype.hasOwnProperty.call(secretData, key)) {
|
|
83
|
+
const value = secretData[key];
|
|
84
|
+
const tfVarKey = `TF_VAR_${key}`;
|
|
85
|
+
// Convert value to string
|
|
86
|
+
if (value === null || value === undefined) {
|
|
87
|
+
tfVars[tfVarKey] = '';
|
|
88
|
+
}
|
|
89
|
+
else if (typeof value === 'string') {
|
|
90
|
+
tfVars[tfVarKey] = value;
|
|
91
|
+
}
|
|
92
|
+
else if (typeof value === 'number' || typeof value === 'boolean') {
|
|
93
|
+
tfVars[tfVarKey] = String(value);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// For objects/arrays, convert to JSON string
|
|
97
|
+
tfVars[tfVarKey] = JSON.stringify(value);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
logger_1.Logger.info(`✅ Loaded ${Object.keys(tfVars).length} Terraform variables from AWS Secrets Manager`);
|
|
102
|
+
return tfVars;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// Handle specific AWS errors
|
|
106
|
+
if (error instanceof client_secrets_manager_1.ResourceNotFoundException) {
|
|
107
|
+
throw new errors_1.ConfigError(`Secret "${secretName}" not found in AWS Secrets Manager.`);
|
|
108
|
+
}
|
|
109
|
+
if (error instanceof Error &&
|
|
110
|
+
(error.name === 'AccessDeniedException' || error.message.includes('AccessDenied'))) {
|
|
111
|
+
throw new errors_1.ConfigError(`Access denied when fetching secret "${secretName}". Ensure your AWS credentials have permission to read this secret.`);
|
|
112
|
+
}
|
|
113
|
+
if (error instanceof errors_1.ConfigError) {
|
|
114
|
+
// Re-throw ConfigError as-is
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
// Generic error handling
|
|
118
|
+
if (error instanceof Error) {
|
|
119
|
+
throw new errors_1.ConfigError(`Failed to fetch secret "${secretName}" from AWS Secrets Manager: ${error.message}. Ensure you have valid AWS credentials and permissions.`);
|
|
120
|
+
}
|
|
121
|
+
throw new errors_1.ConfigError(`Failed to fetch secret "${secretName}": ${String(error)}`);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=aws-secrets.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Azure Key Vault secrets plugin
|
|
3
|
+
* Retrieves secrets from Azure Key Vault and converts them to TF_VAR_* environment variables
|
|
4
|
+
*
|
|
5
|
+
* CONVENTION: Every key in the secret automatically becomes TF_VAR_{key}
|
|
6
|
+
*/
|
|
7
|
+
import type { SecretsPlugin } from '../../types';
|
|
8
|
+
/**
|
|
9
|
+
* Azure Key Vault secrets plugin
|
|
10
|
+
*/
|
|
11
|
+
export declare const azureKeyvault: SecretsPlugin;
|
|
12
|
+
//# sourceMappingURL=azure-keyvault.d.ts.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Azure Key Vault secrets plugin
|
|
4
|
+
* Retrieves secrets from Azure Key Vault and converts them to TF_VAR_* environment variables
|
|
5
|
+
*
|
|
6
|
+
* CONVENTION: Every key in the secret automatically becomes TF_VAR_{key}
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.azureKeyvault = void 0;
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const errors_1 = require("../../core/errors");
|
|
12
|
+
const logger_1 = require("../../utils/logger");
|
|
13
|
+
/**
|
|
14
|
+
* Azure Key Vault secrets plugin
|
|
15
|
+
*/
|
|
16
|
+
exports.azureKeyvault = {
|
|
17
|
+
name: 'azure-keyvault',
|
|
18
|
+
/**
|
|
19
|
+
* Validate Azure Key Vault configuration
|
|
20
|
+
* @param config - Secrets configuration
|
|
21
|
+
* @throws {ConfigError} If configuration is invalid
|
|
22
|
+
*/
|
|
23
|
+
validate: async (config) => {
|
|
24
|
+
if (!config.config) {
|
|
25
|
+
throw new errors_1.ConfigError('Azure Key Vault requires configuration');
|
|
26
|
+
}
|
|
27
|
+
const azureConfig = config.config;
|
|
28
|
+
// vault_name is required
|
|
29
|
+
if (!azureConfig.vault_name) {
|
|
30
|
+
throw new errors_1.ConfigError('Azure Key Vault requires "vault_name" configuration');
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* Retrieve secrets from Azure Key Vault using Azure CLI
|
|
35
|
+
* All keys are automatically prefixed with TF_VAR_
|
|
36
|
+
* @param config - Secrets configuration
|
|
37
|
+
* @param _context - Execution context (unused but required by interface)
|
|
38
|
+
* @returns Record of environment variable key-value pairs (prefixed with TF_VAR_)
|
|
39
|
+
*/
|
|
40
|
+
getSecrets: async (config, _context) => {
|
|
41
|
+
if (!config.config) {
|
|
42
|
+
throw new errors_1.ConfigError('Azure Key Vault requires configuration');
|
|
43
|
+
}
|
|
44
|
+
const azureConfig = config.config;
|
|
45
|
+
const vaultName = azureConfig.vault_name;
|
|
46
|
+
const secretName = azureConfig.secret_name;
|
|
47
|
+
try {
|
|
48
|
+
logger_1.Logger.debug(`Fetching secret from Azure Key Vault: ${vaultName}`);
|
|
49
|
+
const tfVars = {};
|
|
50
|
+
if (secretName) {
|
|
51
|
+
// Fetch single secret using Azure CLI
|
|
52
|
+
logger_1.Logger.debug(`Fetching secret "${secretName}" from Azure Key Vault`);
|
|
53
|
+
try {
|
|
54
|
+
const result = (0, child_process_1.execSync)(`az keyvault secret show --vault-name ${vaultName} --name ${secretName} --output json`, {
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
57
|
+
});
|
|
58
|
+
const secretResponse = JSON.parse(result);
|
|
59
|
+
const secretValue = secretResponse.value;
|
|
60
|
+
if (!secretValue) {
|
|
61
|
+
throw new errors_1.ConfigError(`Secret "${secretName}" exists but does not contain a value.`);
|
|
62
|
+
}
|
|
63
|
+
// Try to parse as JSON
|
|
64
|
+
try {
|
|
65
|
+
const secretData = JSON.parse(secretValue);
|
|
66
|
+
if (typeof secretData === 'object' &&
|
|
67
|
+
secretData !== null &&
|
|
68
|
+
!Array.isArray(secretData)) {
|
|
69
|
+
// It's a JSON object, extract key-value pairs
|
|
70
|
+
for (const key in secretData) {
|
|
71
|
+
if (Object.prototype.hasOwnProperty.call(secretData, key)) {
|
|
72
|
+
const value = secretData[key];
|
|
73
|
+
const tfVarKey = `TF_VAR_${key}`;
|
|
74
|
+
if (value === null || value === undefined) {
|
|
75
|
+
tfVars[tfVarKey] = '';
|
|
76
|
+
}
|
|
77
|
+
else if (typeof value === 'string') {
|
|
78
|
+
tfVars[tfVarKey] = value;
|
|
79
|
+
}
|
|
80
|
+
else if (typeof value === 'number' || typeof value === 'boolean') {
|
|
81
|
+
tfVars[tfVarKey] = String(value);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
tfVars[tfVarKey] = JSON.stringify(value);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Not a JSON object, treat the entire secret value as a single key
|
|
91
|
+
const tfVarKey = `TF_VAR_${secretName}`;
|
|
92
|
+
tfVars[tfVarKey] = secretValue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Not valid JSON, treat the entire secret value as a single key
|
|
97
|
+
const tfVarKey = `TF_VAR_${secretName}`;
|
|
98
|
+
tfVars[tfVarKey] = secretValue;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error instanceof errors_1.ConfigError) {
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
106
|
+
if (errorMessage.includes('(SecretNotFound)') || errorMessage.includes('404')) {
|
|
107
|
+
throw new errors_1.ConfigError(`Secret "${secretName}" not found in Azure Key Vault "${vaultName}".`);
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// List all secrets in the vault and fetch their values using Azure CLI
|
|
114
|
+
logger_1.Logger.debug('Fetching all secrets from Azure Key Vault');
|
|
115
|
+
try {
|
|
116
|
+
const listResult = (0, child_process_1.execSync)(`az keyvault secret list --vault-name ${vaultName} --output json`, {
|
|
117
|
+
encoding: 'utf8',
|
|
118
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
119
|
+
});
|
|
120
|
+
const secrets = JSON.parse(listResult);
|
|
121
|
+
for (const secretProperties of secrets) {
|
|
122
|
+
if (secretProperties.name) {
|
|
123
|
+
try {
|
|
124
|
+
const secretResult = (0, child_process_1.execSync)(`az keyvault secret show --vault-name ${vaultName} --name ${secretProperties.name} --output json`, {
|
|
125
|
+
encoding: 'utf8',
|
|
126
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
127
|
+
});
|
|
128
|
+
const secretResponse = JSON.parse(secretResult);
|
|
129
|
+
if (secretResponse.value) {
|
|
130
|
+
const tfVarKey = `TF_VAR_${secretProperties.name}`;
|
|
131
|
+
tfVars[tfVarKey] = secretResponse.value;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
// Skip secrets we can't read (might not have permissions)
|
|
136
|
+
logger_1.Logger.debug(`Skipping secret "${secretProperties.name}": ${error instanceof Error ? error.message : String(error)}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
143
|
+
if (errorMessage.includes('(VaultNotFound)') || errorMessage.includes('404')) {
|
|
144
|
+
throw new errors_1.ConfigError(`Azure Key Vault "${vaultName}" not found.`);
|
|
145
|
+
}
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
logger_1.Logger.info(`✅ Loaded ${Object.keys(tfVars).length} Terraform variables from Azure Key Vault`);
|
|
150
|
+
return tfVars;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
// Handle specific Azure CLI errors
|
|
154
|
+
if (error instanceof errors_1.ConfigError) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
if (error instanceof Error) {
|
|
158
|
+
const errorMessage = error.message;
|
|
159
|
+
if (errorMessage.includes('(Unauthorized)') || errorMessage.includes('401')) {
|
|
160
|
+
throw new errors_1.ConfigError(`Authentication failed when accessing Azure Key Vault "${vaultName}". Ensure you have logged in with 'az login' and have valid permissions.`);
|
|
161
|
+
}
|
|
162
|
+
if (errorMessage.includes('(Forbidden)') || errorMessage.includes('403')) {
|
|
163
|
+
throw new errors_1.ConfigError(`Access denied when accessing Azure Key Vault "${vaultName}". Ensure your Azure credentials have permission to read secrets from this vault.`);
|
|
164
|
+
}
|
|
165
|
+
if (errorMessage.includes('(VaultNotFound)')) {
|
|
166
|
+
throw new errors_1.ConfigError(`Azure Key Vault "${vaultName}" not found.`);
|
|
167
|
+
}
|
|
168
|
+
if (errorMessage.includes('(SecretNotFound)') ||
|
|
169
|
+
(errorMessage.includes('404') && secretName)) {
|
|
170
|
+
throw new errors_1.ConfigError(`Secret "${secretName}" not found in Azure Key Vault "${vaultName}".`);
|
|
171
|
+
}
|
|
172
|
+
throw new errors_1.ConfigError(`Failed to fetch secrets from Azure Key Vault "${vaultName}": ${errorMessage}. Ensure Azure CLI is installed, you are logged in with 'az login', and have valid permissions.`);
|
|
173
|
+
}
|
|
174
|
+
throw new errors_1.ConfigError(`Failed to fetch secrets from Azure Key Vault "${vaultName}": ${String(error)}`);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=azure-keyvault.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment secrets plugin
|
|
3
|
+
* No-op plugin for when users manage secrets via .env files
|
|
4
|
+
*
|
|
5
|
+
* This plugin does not fetch secrets from an external service.
|
|
6
|
+
* Instead, it allows users to manage TF_VAR_* environment variables
|
|
7
|
+
* directly in their .env file or existing environment variables.
|
|
8
|
+
*
|
|
9
|
+
* The .env file is loaded by EnvironmentSetup.loadEnvFile() which
|
|
10
|
+
* does NOT automatically convert variables to TF_VAR_* prefix.
|
|
11
|
+
* Users must manage the TF_VAR_* prefix themselves in .env files.
|
|
12
|
+
*
|
|
13
|
+
* This plugin returns an empty object because:
|
|
14
|
+
* - Secrets come from .env file (loaded separately)
|
|
15
|
+
* - Secrets come from existing environment variables (already available)
|
|
16
|
+
* - No automatic conversion or fetching is performed
|
|
17
|
+
*/
|
|
18
|
+
import type { SecretsPlugin } from '../../types';
|
|
19
|
+
/**
|
|
20
|
+
* Environment secrets plugin
|
|
21
|
+
* Default and simplest secrets provider - no-op
|
|
22
|
+
*/
|
|
23
|
+
export declare const envSecrets: SecretsPlugin;
|
|
24
|
+
//# sourceMappingURL=env.d.ts.map
|