4
4
*/
5
5
import * as vscode from 'vscode'
6
6
import { Session } from 'aws-sdk/clients/ssm'
7
- import { AWSError , IAM } from 'aws-sdk'
7
+ import { IAM } from 'aws-sdk'
8
8
import { Ec2Selection } from './utils'
9
9
import { getOrInstallCli } from '../shared/utilities/cliUtils'
10
10
import { isCloud9 } from '../shared/extensionUtilities'
11
- import { ToolkitError , isAwsError } from '../shared/errors'
11
+ import { ToolkitError } from '../shared/errors'
12
12
import { SsmClient } from '../shared/clients/ssmClient'
13
13
import { Ec2Client } from '../shared/clients/ec2Client'
14
14
15
15
export type Ec2ConnectErrorCode = 'EC2SSMStatus' | 'EC2SSMPermission' | 'EC2SSMConnect'
16
16
17
17
import { openRemoteTerminal } from '../shared/remoteSession'
18
18
import { DefaultIamClient } from '../shared/clients/iamClient'
19
+ import { ErrorInformation } from '../shared/errors'
19
20
20
21
export class Ec2ConnectionManager {
21
22
private ssmClient : SsmClient
@@ -41,56 +42,51 @@ export class Ec2ConnectionManager {
41
42
}
42
43
43
44
protected async getAttachedPolicies ( instanceId : string ) : Promise < IAM . attachedPoliciesListType > {
44
- try {
45
- const IamRole = await this . ec2Client . getAttachedIamRole ( instanceId )
46
- const iamResponse = await this . iamClient . listAttachedRolePolicies ( IamRole ! . Arn ! )
47
- return iamResponse . AttachedPolicies !
48
- } catch ( err ) {
45
+ const IamRole = await this . ec2Client . getAttachedIamRole ( instanceId )
46
+ if ( ! IamRole ) {
49
47
return [ ]
50
48
}
49
+ const iamResponse = await this . iamClient . listAttachedRolePolicies ( IamRole ! . Arn ! )
50
+
51
+ return iamResponse . AttachedPolicies ?? [ ]
51
52
}
52
53
53
54
public async hasProperPolicies ( instanceId : string ) : Promise < boolean > {
54
55
const attachedPolicies = ( await this . getAttachedPolicies ( instanceId ) ) . map ( policy => policy . PolicyName ! )
55
56
const requiredPolicies = [ 'AmazonSSMManagedInstanceCore' , 'AmazonSSMManagedEC2InstanceDefaultPolicy' ]
56
57
57
- return requiredPolicies . every ( policy => attachedPolicies . includes ( policy ) )
58
+ return requiredPolicies . length !== 0 && requiredPolicies . every ( policy => attachedPolicies . includes ( policy ) )
58
59
}
59
60
60
- public async handleStartSessionError ( err : AWSError , selection : Ec2Selection ) : Promise < Error > {
61
- const isInstanceRunning = ( await this . ec2Client . getInstanceStatus ( selection . instanceId ) ) == 'running'
61
+ public async isInstanceRunning ( instanceId : string ) : Promise < boolean > {
62
+ const instanceStatus = await this . ec2Client . getInstanceStatus ( instanceId )
63
+ return instanceStatus == 'running'
64
+ }
65
+
66
+ private throwConnectionError ( message : string , selection : Ec2Selection , errorInfo : ErrorInformation ) {
62
67
const generalErrorMessage = `Unable to connect to target instance ${ selection . instanceId } on region ${ selection . region } . `
68
+ throw new ToolkitError ( generalErrorMessage + message , errorInfo )
69
+ }
70
+
71
+ public async checkForStartSessionError ( selection : Ec2Selection ) : Promise < void > {
72
+ const isInstanceRunning = await this . isInstanceRunning ( selection . instanceId )
63
73
const hasProperPolicies = await this . hasProperPolicies ( selection . instanceId )
64
74
65
75
if ( ! isInstanceRunning ) {
66
- throw new ToolkitError (
67
- generalErrorMessage +
68
- 'Ensure the target instance is running and not currently starting, stopping, or stopped.' ,
69
- { code : 'EC2SSMStatus' }
70
- )
76
+ const message = 'Ensure the target instance is running and not currently starting, stopping, or stopped.'
77
+ this . throwConnectionError ( message , selection , { code : 'EC2SSMStatus' } )
71
78
}
72
79
73
80
if ( ! hasProperPolicies ) {
74
- throw new ToolkitError (
75
- generalErrorMessage + 'Ensure the IAM role attached to the instance has the required policies.' ,
76
- {
77
- code : 'EC2SSMPermission' ,
78
- documentationUri : vscode . Uri . parse (
79
- 'https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-instance-profile.html'
80
- ) ,
81
- }
81
+ const message = 'Ensure the IAM role attached to the instance has the required policies.'
82
+ const documentationUri = vscode . Uri . parse (
83
+ 'https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-instance-profile.html'
82
84
)
85
+ this . throwConnectionError ( message , selection , {
86
+ code : 'EC2SSMPermission' ,
87
+ documentationUri : documentationUri ,
88
+ } )
83
89
}
84
-
85
- throw new ToolkitError (
86
- 'Ensure SSM is running on target instance. For more information see the documentation.' ,
87
- {
88
- code : 'EC2SSMConnect' ,
89
- documentationUri : vscode . Uri . parse (
90
- 'https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started.html'
91
- ) ,
92
- }
93
- )
94
90
}
95
91
96
92
private async openSessionInTerminal ( session : Session , selection : Ec2Selection ) {
@@ -108,15 +104,16 @@ export class Ec2ConnectionManager {
108
104
}
109
105
110
106
public async attemptEc2Connection ( selection : Ec2Selection ) : Promise < void > {
107
+ await this . checkForStartSessionError ( selection )
111
108
try {
112
109
const response = await this . ssmClient . startSession ( selection . instanceId )
113
110
await this . openSessionInTerminal ( response , selection )
114
- } catch ( err ) {
115
- if ( isAwsError ( err ) ) {
116
- await this . handleStartSessionError ( err , selection )
117
- } else {
118
- throw err
119
- }
111
+ } catch ( err : unknown ) {
112
+ // Default error if pre-check fails.
113
+ this . throwConnectionError ( 'Check that the SSM Agent is running on target instance' , selection , {
114
+ code : 'EC2SSMConnect' ,
115
+ cause : err as Error ,
116
+ } )
120
117
}
121
118
}
122
119
}
0 commit comments