rds_ssm_connect 1.0.4 → 1.0.6
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/.eslintrc.yml +8 -0
- package/.github/workflows/npm-publish.yml +2 -0
- package/connect.js +131 -125
- package/package.json +13 -4
package/.eslintrc.yml
ADDED
package/connect.js
CHANGED
|
@@ -1,138 +1,144 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// Import necessary modules
|
|
4
|
-
import { spawn } from 'child_process'
|
|
5
|
-
import inquirer from 'inquirer'
|
|
6
|
-
import fs from 'fs'
|
|
7
|
-
import os from 'os'
|
|
8
|
-
import path from 'path'
|
|
4
|
+
import { spawn } from 'child_process' // For spawning child processes
|
|
5
|
+
import inquirer from 'inquirer' // For prompting the user for input
|
|
6
|
+
import fs from 'fs' // For reading files
|
|
7
|
+
import os from 'os' // For getting the user's home directory
|
|
8
|
+
import path from 'path' // For working with file paths
|
|
9
9
|
|
|
10
10
|
// Get the path to the AWS config file
|
|
11
|
-
const awsConfigPath = path.join(os.homedir(), '.aws', 'config')
|
|
11
|
+
const awsConfigPath = path.join(os.homedir(), '.aws', 'config')
|
|
12
12
|
|
|
13
13
|
// Read the contents of the AWS config file
|
|
14
|
-
const awsConfig = fs.readFileSync(awsConfigPath, 'utf-8')
|
|
14
|
+
const awsConfig = fs.readFileSync(awsConfigPath, 'utf-8')
|
|
15
15
|
|
|
16
16
|
// Extract environments from AWS config file
|
|
17
17
|
const ENVS = awsConfig
|
|
18
18
|
.split('\n')
|
|
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
|
-
|
|
19
|
+
.filter(line => line.startsWith('[') && line.endsWith(']'))
|
|
20
|
+
.map(line => line.slice(1, -1))
|
|
21
|
+
.map(line => line.replace('profile ', ''))
|
|
22
|
+
|
|
23
|
+
// Define a mapping of environment suffixes to port numbers
|
|
24
|
+
const envPortMapping = {
|
|
25
|
+
dev: '5433',
|
|
26
|
+
stage: '5434',
|
|
27
|
+
'pre-prod': '5435',
|
|
28
|
+
prod: '5436'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Define the table name
|
|
32
|
+
const TABLE_NAME = 'emr'
|
|
33
|
+
|
|
34
|
+
// Define the AWS region
|
|
35
|
+
const REGION = 'us-east-2'
|
|
36
|
+
|
|
37
|
+
// Prompt the user to select an environment
|
|
38
|
+
inquirer
|
|
39
|
+
.prompt([
|
|
40
|
+
{
|
|
41
|
+
type: 'list',
|
|
42
|
+
name: 'ENV',
|
|
43
|
+
message: 'Please select the environment:',
|
|
44
|
+
choices: ENVS
|
|
45
|
+
}
|
|
46
|
+
])
|
|
47
|
+
|
|
48
|
+
.then((answers) => {
|
|
49
|
+
const ENV = answers.ENV // Get the selected environment from the user's answers
|
|
50
|
+
console.log(`You selected: ${ENV}`)
|
|
51
|
+
|
|
52
|
+
// Extract the environment suffix from the selected environment
|
|
53
|
+
const envSuffix = ENV.split('-').pop()
|
|
54
|
+
|
|
55
|
+
// Get the corresponding port number for the environment
|
|
56
|
+
let portNumber = envPortMapping[envSuffix] // Declare portNumber as a let variable
|
|
57
|
+
|
|
58
|
+
// If no port number is found for the environment, default to 5432
|
|
59
|
+
if (!portNumber) {
|
|
60
|
+
console.error(`No port number found for environment: ${ENV}. Defaulting to 5432.`)
|
|
61
|
+
portNumber = '5432'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Set up the commands to run inside the aws-vault environment
|
|
65
|
+
const awsVaultExecCommand = ['aws-vault', 'exec', ENV, '--']
|
|
66
|
+
const ssmDescribeCommand = `aws ssm describe-parameters --region ${REGION} --query "Parameters[?ends_with(Name, '/rds/rds-aurora-password')].Name" --output text | head -n 1`
|
|
67
|
+
|
|
68
|
+
// Run the commands inside aws-vault environment
|
|
69
|
+
const ssmDescribeProcess = spawn('sh', ['-c', `${awsVaultExecCommand.join(' ')} ${ssmDescribeCommand}`])
|
|
70
|
+
|
|
71
|
+
// Get the name of the parameter containing the RDS password
|
|
72
|
+
ssmDescribeProcess.stdout.on('data', (data) => {
|
|
73
|
+
const PARAM_NAME = data.toString().trim()
|
|
74
|
+
|
|
75
|
+
// Get the RDS credentials
|
|
76
|
+
const ssmGetCommand = `aws ssm get-parameter --region ${REGION} --name '${PARAM_NAME}' --with-decryption --query Parameter.Value --output text`
|
|
77
|
+
const ssmGetProcess = spawn('sh', ['-c', `${awsVaultExecCommand.join(' ')} ${ssmGetCommand}`])
|
|
78
|
+
|
|
79
|
+
// Parse the JSON output of the ssm get-parameter command to get the RDS credentials
|
|
80
|
+
ssmGetProcess.stdout.on('data', (data) => {
|
|
81
|
+
const CREDENTIALS = JSON.parse(data.toString())
|
|
82
|
+
const USERNAME = CREDENTIALS.user // Get the RDS username from the credentials
|
|
83
|
+
const PASSWORD = CREDENTIALS.password // Get the RDS password from the credentials
|
|
84
|
+
|
|
85
|
+
// Display connection credentials and connection string
|
|
86
|
+
console.log(`Your connection string is: psql -h localhost -p ${portNumber} -U ${USERNAME} -d ${TABLE_NAME}`)
|
|
87
|
+
console.log(`Use the password: ${PASSWORD}`)
|
|
88
|
+
|
|
89
|
+
// Get the ID of the bastion instance
|
|
90
|
+
const instanceIdCommand = `aws ec2 describe-instances --region ${REGION} --filters "Name=tag:Name,Values='*bastion*'" --query "Reservations[].Instances[].[InstanceId]" --output text`
|
|
91
|
+
const instanceIdProcess = spawn('sh', ['-c', `${awsVaultExecCommand.join(' ')} ${instanceIdCommand}`])
|
|
92
|
+
|
|
93
|
+
instanceIdProcess.stdout.on('data', (data) => {
|
|
94
|
+
const INSTANCE_ID = data.toString().trim()
|
|
95
|
+
|
|
96
|
+
if (!INSTANCE_ID) {
|
|
97
|
+
console.error('Failed to find the instance with tag Name=*bastion*.')
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Get the endpoint of the RDS cluster
|
|
102
|
+
const rdsEndpointCommand = `aws rds describe-db-clusters --region ${REGION} --query "DBClusters[?contains(DBClusterIdentifier, 'rds-aurora')].Endpoint" --output text`
|
|
103
|
+
const rdsEndpointProcess = spawn('sh', ['-c', `${awsVaultExecCommand.join(' ')} ${rdsEndpointCommand}`])
|
|
104
|
+
|
|
105
|
+
rdsEndpointProcess.stdout.on('data', (data) => {
|
|
106
|
+
const RDS_ENDPOINT = data.toString().trim()
|
|
107
|
+
|
|
108
|
+
if (!RDS_ENDPOINT) {
|
|
109
|
+
console.error('Failed to find the RDS endpoint.')
|
|
110
|
+
return
|
|
93
111
|
}
|
|
94
112
|
|
|
95
|
-
//
|
|
96
|
-
const
|
|
97
|
-
const
|
|
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
|
-
ssmGetProcess.stderr.on('data', (data) => {
|
|
131
|
-
console.error(`Command execution error: ${data.toString()}`);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
ssmDescribeProcess.stderr.on('data', (data) => {
|
|
136
|
-
console.error(`Command execution error: ${data.toString()}`);
|
|
137
|
-
});
|
|
138
|
-
});
|
|
113
|
+
// Start a port forwarding session to the RDS cluster
|
|
114
|
+
const portForwardingCommand = `aws ssm start-session --target ${INSTANCE_ID} --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters "host=${RDS_ENDPOINT},portNumber='5432',localPortNumber='${portNumber}'" --cli-connect-timeout 0`
|
|
115
|
+
const portForwardingProcess = spawn('sh', ['-c', `${awsVaultExecCommand.join(' ')} ${portForwardingCommand}`])
|
|
116
|
+
|
|
117
|
+
portForwardingProcess.stdout.on('data', (data) => {
|
|
118
|
+
console.log(data.toString().trim())
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
portForwardingProcess.stderr.on('data', (data) => {
|
|
122
|
+
console.error(`Command execution error: ${data.toString()}`)
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
rdsEndpointProcess.stderr.on('data', (data) => {
|
|
127
|
+
console.error(`Command execution error: ${data.toString()}`)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
instanceIdProcess.stderr.on('data', (data) => {
|
|
132
|
+
console.error(`Command execution error: ${data.toString()}`)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
ssmGetProcess.stderr.on('data', (data) => {
|
|
137
|
+
console.error(`Command execution error: ${data.toString()}`)
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
ssmDescribeProcess.stderr.on('data', (data) => {
|
|
142
|
+
console.error(`Command execution error: ${data.toString()}`)
|
|
143
|
+
})
|
|
144
|
+
})
|
package/package.json
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rds_ssm_connect",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aws-sdk/client-ec2": "^3.363.0",
|
|
7
7
|
"@aws-sdk/client-rds": "^3.363.0",
|
|
8
8
|
"@aws-sdk/client-ssm": "^3.363.0",
|
|
9
|
-
"
|
|
10
|
-
"inquirer": "^8.2.5",
|
|
11
|
-
"node-jq": "^2.3.5"
|
|
9
|
+
"inquirer": "^8.2.5"
|
|
12
10
|
},
|
|
13
11
|
"bin": {
|
|
14
12
|
"rds_ssm_connect": "./connect.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"lint": "eslint .",
|
|
16
|
+
"lint:fix": "eslint . --fix"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"eslint": "^8.44.0",
|
|
20
|
+
"eslint-config-standard": "^17.1.0",
|
|
21
|
+
"eslint-plugin-import": "^2.27.5",
|
|
22
|
+
"eslint-plugin-n": "^16.0.1",
|
|
23
|
+
"eslint-plugin-promise": "^6.1.1"
|
|
15
24
|
}
|
|
16
25
|
}
|