@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.
Files changed (131) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +278 -0
  3. package/RELEASE_SUMMARY.md +53 -0
  4. package/STANDARDS_COMPLIANCE.md +85 -0
  5. package/bin/terraflow.js +3 -0
  6. package/bin/tf.js +3 -0
  7. package/dist/commands/apply.d.ts +7 -0
  8. package/dist/commands/apply.js +12 -0
  9. package/dist/commands/base.d.ts +7 -0
  10. package/dist/commands/base.js +12 -0
  11. package/dist/commands/config.d.ts +25 -0
  12. package/dist/commands/config.js +354 -0
  13. package/dist/commands/destroy.d.ts +7 -0
  14. package/dist/commands/destroy.js +12 -0
  15. package/dist/commands/init.d.ts +68 -0
  16. package/dist/commands/init.js +131 -0
  17. package/dist/commands/plan.d.ts +7 -0
  18. package/dist/commands/plan.js +12 -0
  19. package/dist/core/backend-state.d.ts +25 -0
  20. package/dist/core/backend-state.js +77 -0
  21. package/dist/core/config.d.ts +83 -0
  22. package/dist/core/config.js +295 -0
  23. package/dist/core/context.d.ts +52 -0
  24. package/dist/core/context.js +192 -0
  25. package/dist/core/environment.d.ts +62 -0
  26. package/dist/core/environment.js +205 -0
  27. package/dist/core/errors.d.ts +22 -0
  28. package/dist/core/errors.js +36 -0
  29. package/dist/core/plugin-loader.d.ts +21 -0
  30. package/dist/core/plugin-loader.js +136 -0
  31. package/dist/core/terraform.d.ts +45 -0
  32. package/dist/core/terraform.js +247 -0
  33. package/dist/core/validator.d.ts +103 -0
  34. package/dist/core/validator.js +304 -0
  35. package/dist/index.d.ts +7 -0
  36. package/dist/index.js +184 -0
  37. package/dist/plugins/auth/aws-assume-role.d.ts +10 -0
  38. package/dist/plugins/auth/aws-assume-role.js +110 -0
  39. package/dist/plugins/auth/azure-service-principal.d.ts +10 -0
  40. package/dist/plugins/auth/azure-service-principal.js +99 -0
  41. package/dist/plugins/auth/gcp-service-account.d.ts +10 -0
  42. package/dist/plugins/auth/gcp-service-account.js +105 -0
  43. package/dist/plugins/backends/azurerm.d.ts +10 -0
  44. package/dist/plugins/backends/azurerm.js +117 -0
  45. package/dist/plugins/backends/gcs.d.ts +10 -0
  46. package/dist/plugins/backends/gcs.js +75 -0
  47. package/dist/plugins/backends/local.d.ts +11 -0
  48. package/dist/plugins/backends/local.js +37 -0
  49. package/dist/plugins/backends/s3.d.ts +10 -0
  50. package/dist/plugins/backends/s3.js +185 -0
  51. package/dist/plugins/secrets/aws-secrets.d.ts +12 -0
  52. package/dist/plugins/secrets/aws-secrets.js +125 -0
  53. package/dist/plugins/secrets/azure-keyvault.d.ts +12 -0
  54. package/dist/plugins/secrets/azure-keyvault.js +178 -0
  55. package/dist/plugins/secrets/env.d.ts +24 -0
  56. package/dist/plugins/secrets/env.js +62 -0
  57. package/dist/plugins/secrets/gcp-secret-manager.d.ts +12 -0
  58. package/dist/plugins/secrets/gcp-secret-manager.js +157 -0
  59. package/dist/templates/application/go/go.mod.template +4 -0
  60. package/dist/templates/application/go/main.template +8 -0
  61. package/dist/templates/application/go/test.template +11 -0
  62. package/dist/templates/application/javascript/main.template +14 -0
  63. package/dist/templates/application/javascript/test.template +8 -0
  64. package/dist/templates/application/python/main.template +13 -0
  65. package/dist/templates/application/python/requirements.txt.template +3 -0
  66. package/dist/templates/application/python/test.template +8 -0
  67. package/dist/templates/application/typescript/main.template +14 -0
  68. package/dist/templates/application/typescript/test.template +8 -0
  69. package/dist/templates/application/typescript/tsconfig.json.template +20 -0
  70. package/dist/templates/config/README.md.template +82 -0
  71. package/dist/templates/config/env.example.template +22 -0
  72. package/dist/templates/config/gitignore.template +40 -0
  73. package/dist/templates/config/tfwconfig.yml.template +69 -0
  74. package/dist/templates/templates/application/go/go.mod.template +4 -0
  75. package/dist/templates/templates/application/go/main.template +8 -0
  76. package/dist/templates/templates/application/go/test.template +11 -0
  77. package/dist/templates/templates/application/javascript/main.template +14 -0
  78. package/dist/templates/templates/application/javascript/test.template +8 -0
  79. package/dist/templates/templates/application/python/main.template +13 -0
  80. package/dist/templates/templates/application/python/requirements.txt.template +3 -0
  81. package/dist/templates/templates/application/python/test.template +8 -0
  82. package/dist/templates/templates/application/typescript/main.template +14 -0
  83. package/dist/templates/templates/application/typescript/test.template +8 -0
  84. package/dist/templates/templates/application/typescript/tsconfig.json.template +20 -0
  85. package/dist/templates/templates/config/README.md.template +82 -0
  86. package/dist/templates/templates/config/env.example.template +22 -0
  87. package/dist/templates/templates/config/gitignore.template +40 -0
  88. package/dist/templates/templates/config/tfwconfig.yml.template +69 -0
  89. package/dist/templates/templates/terraform/aws/_init.tf.template +24 -0
  90. package/dist/templates/templates/terraform/aws/inputs.tf.template +11 -0
  91. package/dist/templates/templates/terraform/azure/_init.tf.template +19 -0
  92. package/dist/templates/templates/terraform/azure/inputs.tf.template +11 -0
  93. package/dist/templates/templates/terraform/gcp/_init.tf.template +20 -0
  94. package/dist/templates/templates/terraform/gcp/inputs.tf.template +16 -0
  95. package/dist/templates/templates/terraform/locals.tf.template +9 -0
  96. package/dist/templates/templates/terraform/main.tf.template +8 -0
  97. package/dist/templates/templates/terraform/modules/inputs.tf.template +5 -0
  98. package/dist/templates/templates/terraform/modules/main.tf.template +2 -0
  99. package/dist/templates/templates/terraform/modules/outputs.tf.template +2 -0
  100. package/dist/templates/templates/terraform/outputs.tf.template +6 -0
  101. package/dist/templates/terraform/aws/_init.tf.template +24 -0
  102. package/dist/templates/terraform/aws/inputs.tf.template +11 -0
  103. package/dist/templates/terraform/azure/_init.tf.template +19 -0
  104. package/dist/templates/terraform/azure/inputs.tf.template +11 -0
  105. package/dist/templates/terraform/gcp/_init.tf.template +20 -0
  106. package/dist/templates/terraform/gcp/inputs.tf.template +16 -0
  107. package/dist/templates/terraform/locals.tf.template +9 -0
  108. package/dist/templates/terraform/main.tf.template +8 -0
  109. package/dist/templates/terraform/modules/inputs.tf.template +5 -0
  110. package/dist/templates/terraform/modules/main.tf.template +2 -0
  111. package/dist/templates/terraform/modules/outputs.tf.template +2 -0
  112. package/dist/templates/terraform/outputs.tf.template +6 -0
  113. package/dist/types/config.d.ts +92 -0
  114. package/dist/types/config.js +6 -0
  115. package/dist/types/context.d.ts +59 -0
  116. package/dist/types/context.js +6 -0
  117. package/dist/types/index.d.ts +7 -0
  118. package/dist/types/index.js +23 -0
  119. package/dist/types/plugins.d.ts +77 -0
  120. package/dist/types/plugins.js +6 -0
  121. package/dist/utils/cloud.d.ts +43 -0
  122. package/dist/utils/cloud.js +150 -0
  123. package/dist/utils/git.d.ts +88 -0
  124. package/dist/utils/git.js +258 -0
  125. package/dist/utils/logger.d.ts +67 -0
  126. package/dist/utils/logger.js +121 -0
  127. package/dist/utils/scaffolding.d.ts +92 -0
  128. package/dist/utils/scaffolding.js +338 -0
  129. package/dist/utils/templates.d.ts +25 -0
  130. package/dist/utils/templates.js +70 -0
  131. 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,10 @@
1
+ /**
2
+ * S3 backend plugin
3
+ * Handles AWS S3 Terraform state backend
4
+ */
5
+ import type { BackendPlugin } from '../../types';
6
+ /**
7
+ * S3 backend plugin
8
+ */
9
+ export declare const s3Backend: BackendPlugin;
10
+ //# sourceMappingURL=s3.d.ts.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