@@ -8,7 +8,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
@@ -17,7 +17,6 @@ export type Ec2ConnectErrorCode = 'EC2SSMStatus' | 'EC2SSMPermission' | 'EC2SSMC
17
17
import { openRemoteTerminal } from '../shared/remoteSession'
18
18
import { DefaultIamClient } from '../shared/clients/iamClient'
19
19
import { ErrorInformation } from '../shared/errors'
20
- import { getLogger } from '../shared/logger'
21
20
22
21
export class Ec2ConnectionManager {
23
22
private ssmClient : SsmClient
@@ -28,6 +27,10 @@ export class Ec2ConnectionManager {
28
27
'https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-instance-profile.html'
29
28
)
30
29
30
+ private ssmAgentDocumentationUri = vscode . Uri . parse (
31
+ 'https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html'
32
+ )
33
+
31
34
public constructor ( readonly regionCode : string ) {
32
35
this . ssmClient = this . createSsmSdkClient ( )
33
36
this . ec2Client = this . createEc2SdkClient ( )
@@ -46,31 +49,18 @@ export class Ec2ConnectionManager {
46
49
return new DefaultIamClient ( this . regionCode )
47
50
}
48
51
49
- protected async getAttachedPolicies ( instanceId : string ) : Promise < IAM . AttachedPolicy [ ] > {
50
- const IamRole = await this . ec2Client . getAttachedIamRole ( instanceId )
51
- if ( ! IamRole ?. Arn ) {
52
- return [ ]
53
- }
54
- try {
55
- const attachedPolicies = await this . iamClient . listAttachedRolePolicies ( IamRole . Arn )
56
- return attachedPolicies
57
- } catch ( e ) {
58
- if ( isAwsError ( e ) && e . code == 'NoSuchEntity' ) {
59
- const errorMessage = `Attached role does not exist in IAM: ${ IamRole . Arn } .`
60
- getLogger ( ) . error ( `ec2: ${ errorMessage } ` )
61
- throw ToolkitError . chain ( e , errorMessage , {
62
- code : e . code ,
63
- documentationUri : this . policyDocumentationUri ,
64
- } )
65
- }
66
- throw ToolkitError . chain ( e as Error , `Failed to check policies for EC2 instance: ${ instanceId } ` , {
67
- code : 'PolicyCheck' ,
68
- } )
52
+ public async getAttachedIamRole ( instanceId : string ) : Promise < IAM . Role | undefined > {
53
+ const IamInstanceProfile = await this . ec2Client . getAttachedIamInstanceProfile ( instanceId )
54
+ if ( IamInstanceProfile && IamInstanceProfile . Arn ) {
55
+ const IamRole = await this . iamClient . getIAMRoleFromInstanceProfile ( IamInstanceProfile . Arn )
56
+ return IamRole
69
57
}
70
58
}
71
59
72
- public async hasProperPolicies ( instanceId : string ) : Promise < boolean > {
73
- const attachedPolicies = ( await this . getAttachedPolicies ( instanceId ) ) . map ( policy => policy . PolicyName ! )
60
+ public async hasProperPolicies ( IamRoleArn : string ) : Promise < boolean > {
61
+ const attachedPolicies = ( await this . iamClient . listAttachedRolePolicies ( IamRoleArn ) ) . map (
62
+ policy => policy . PolicyName !
63
+ )
74
64
const requiredPolicies = [ 'AmazonSSMManagedInstanceCore' , 'AmazonSSMManagedEC2InstanceDefaultPolicy' ]
75
65
76
66
return requiredPolicies . length !== 0 && requiredPolicies . every ( policy => attachedPolicies . includes ( policy ) )
@@ -86,46 +76,55 @@ export class Ec2ConnectionManager {
86
76
throw new ToolkitError ( generalErrorMessage + message , errorInfo )
87
77
}
88
78
89
- protected async throwPolicyError ( selection : Ec2Selection ) {
90
- const role = await this . ec2Client . getAttachedIamRole ( selection . instanceId )
91
-
92
- const baseMessage = 'Ensure an IAM role with the required policies is attached to the instance.'
93
- const messageExtension =
94
- role && role . Arn
95
- ? `Found attached role ${ role . Arn } .`
96
- : `Failed to find role attached to ${ selection . instanceId } `
97
- const fullMessage = `${ baseMessage } ${ messageExtension } `
98
-
99
- this . throwConnectionError ( fullMessage , selection , {
100
- code : 'EC2SSMPermission' ,
101
- documentationUri : this . policyDocumentationUri ,
102
- } )
103
- }
104
-
105
- public async checkForStartSessionError ( selection : Ec2Selection ) : Promise < void > {
79
+ private async checkForInstanceStatusError ( selection : Ec2Selection ) : Promise < void > {
106
80
const isInstanceRunning = await this . isInstanceRunning ( selection . instanceId )
107
- const hasProperPolicies = await this . hasProperPolicies ( selection . instanceId )
108
- const isSsmAgentRunning = ( await this . ssmClient . getInstanceAgentPingStatus ( selection . instanceId ) ) == 'Online'
109
81
110
82
if ( ! isInstanceRunning ) {
111
- const message = 'Ensure the target instance is running and not currently starting, stopping, or stopped .'
83
+ const message = 'Ensure the target instance is running.'
112
84
this . throwConnectionError ( message , selection , { code : 'EC2SSMStatus' } )
113
85
}
86
+ }
87
+
88
+ private async checkForInstancePermissionsError ( selection : Ec2Selection ) : Promise < void > {
89
+ const IamRole = await this . getAttachedIamRole ( selection . instanceId )
90
+
91
+ if ( ! IamRole ) {
92
+ const message = `No IAM role attached to instance: ${ selection . instanceId } `
93
+ this . throwConnectionError ( message , selection , { code : 'EC2SSMPermission' } )
94
+ }
95
+
96
+ const hasProperPolicies = await this . hasProperPolicies ( IamRole ! . Arn )
114
97
115
98
if ( ! hasProperPolicies ) {
116
- await this . throwPolicyError ( selection )
99
+ const message = `Ensure an IAM role with the required policies is attached to the instance. Found attached role: ${
100
+ IamRole ! . Arn
101
+ } `
102
+ this . throwConnectionError ( message , selection , {
103
+ code : 'EC2SSMPermission' ,
104
+ documentationUri : this . policyDocumentationUri ,
105
+ } )
117
106
}
107
+ }
108
+
109
+ private async checkForInstanceSsmError ( selection : Ec2Selection ) : Promise < void > {
110
+ const isSsmAgentRunning = ( await this . ssmClient . getInstanceAgentPingStatus ( selection . instanceId ) ) == 'Online'
118
111
119
112
if ( ! isSsmAgentRunning ) {
120
113
this . throwConnectionError ( 'Is SSM Agent running on the target instance?' , selection , {
121
114
code : 'EC2SSMAgentStatus' ,
122
- documentationUri : vscode . Uri . parse (
123
- 'https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html'
124
- ) ,
115
+ documentationUri : this . ssmAgentDocumentationUri ,
125
116
} )
126
117
}
127
118
}
128
119
120
+ public async checkForStartSessionError ( selection : Ec2Selection ) : Promise < void > {
121
+ await this . checkForInstanceStatusError ( selection )
122
+
123
+ await this . checkForInstancePermissionsError ( selection )
124
+
125
+ await this . checkForInstanceSsmError ( selection )
126
+ }
127
+
129
128
private async openSessionInTerminal ( session : Session , selection : Ec2Selection ) {
130
129
const ssmPlugin = await getOrInstallCli ( 'session-manager-plugin' , ! isCloud9 )
131
130
const shellArgs = [ JSON . stringify ( session ) , selection . region , 'StartSession' ]
0 commit comments