-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
179 lines (152 loc) · 3.72 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#!/usr/bin/env node
'use strict';
const process = require('process');
const rimraf = require('rimraf');
const child_process = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const argv = require('yargs')
.usage('Usage: $0 [options]\n\nDestroys an AWS Terraform deployment from an S3 state file')
.help('help')
.alias('help', 'h')
.option('profile', {
describe: 'AWS profile to use from your credential file',
nargs: 1
})
.option('region', {
describe: 'AWS region to use',
nargs: 1,
default: 'us-east-1'
})
.option('bucket', {
describe: 'Name of S3 bucket containing Terraform state file',
nargs: 1,
demandOption: true
})
.option('key', {
describe: 'Key of Terraform state file in S3 bucket',
type: 'array'
})
.option('pattern', {
describe: 'Regex for keys of Terraform state files in S3 bucket',
type: 'array',
coerce: (input) => input.map(p => new RegExp(p))
})
.option('delete-state', {
describe: 'Delete Terraform state file from S3 when complete',
type: 'boolean',
default: false
})
.option('auto-approve', {
describe: 'Do not ask for confirmation',
type: 'boolean',
default: false
})
.option('dry-run', {
describe: 'Do not destroy anything, just show what would be done',
type: 'boolean',
default: false
})
.check(argv => {
if (!argv.key && !argv.pattern) {
throw new Error('Must specify either --key or --pattern');
}
return true;
})
.strict()
// not strictly necessary but makes testing easier
.fail((msg, err, yargs) => {
console.log(yargs.help());
console.log(msg);
process.exit(1);
})
.parse(process.argv);
// apply profile to all subsequent commands - must do this before loading AWS SDK
if (argv.profile) {
process.env.AWS_PROFILE = argv.profile;
}
const AWS = require('aws-sdk');
const s3 = new AWS.S3({region: argv.region});
function matchPatterns(key) {
for (const pattern of argv.pattern) {
if (pattern.test(key)) {
return true;
}
}
return false;
}
async function deleteState(key) {
await s3.deleteObject({
Bucket: argv.bucket,
Key: key
}).promise();
}
function exec(command) {
child_process.execSync(command, {stdio: 'inherit'});
}
async function main() {
const keys = argv.key || [];
if (argv.pattern) {
let continuationToken = undefined;
do {
const result = await s3.listObjectsV2({
Bucket: argv.bucket,
ContinuationToken: continuationToken
}).promise();
result.Contents.map(obj => obj.Key).filter(matchPatterns).forEach(k => keys.push(k));
continuationToken = result.NextContinuationToken;
} while (continuationToken);
}
for (const key of keys) {
if (argv['dry-run']) {
console.log(`\nWould destroy resources for ${key}...\n`);
} else {
console.log(`\nDestroying resources for ${key}...\n`);
}
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'terraform-s3-nuke-'));
try {
process.chdir(tempDir);
fs.writeFileSync('main.tf.json', JSON.stringify({
provider: {
aws: {
region: argv.region
}
},
terraform: {
backend: {
s3: {
region: argv.region,
bucket: argv.bucket,
key
}
}
}
}));
exec('terraform init');
if (argv['dry-run']) {
exec('terraform plan -destroy');
} else {
if (argv['auto-approve']) {
exec('terraform destroy -auto-approve');
} else {
exec('terraform destroy');
}
}
if (argv['delete-state']) {
if (argv['dry-run']) {
console.log(`\nWould delete state file ${key}\n`);
} else {
await deleteState(key);
console.log(`\nDeleted state file ${key}\n`);
}
}
} finally {
rimraf.sync(tempDir);
}
}
}
module.exports = main().catch(err => {
console.error(err);
process.exitCode = 1;
});