@@ -9,7 +9,7 @@ import { IAM, SSM } from 'aws-sdk'
9
9
import { Ec2Selection } from './utils'
10
10
import { getOrInstallCli } from '../shared/utilities/cliUtils'
11
11
import { isCloud9 } from '../shared/extensionUtilities'
12
- import { ToolkitError , isAwsError } from '../shared/errors'
12
+ import { ToolkitError } from '../shared/errors'
13
13
import { SsmClient } from '../shared/clients/ssmClient'
14
14
import { Ec2Client } from '../shared/clients/ec2Client'
15
15
import { VscodeRemoteConnection , ensureDependencies , openRemoteTerminal } from '../shared/remoteSession'
@@ -42,6 +42,10 @@ export class Ec2ConnectionManager {
42
42
'https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-instance-profile.html'
43
43
)
44
44
45
+ private ssmAgentDocumentationUri = vscode . Uri . parse (
46
+ 'https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html'
47
+ )
48
+
45
49
public constructor ( readonly regionCode : string ) {
46
50
this . ssmClient = this . createSsmSdkClient ( )
47
51
this . ec2Client = this . createEc2SdkClient ( )
@@ -60,31 +64,18 @@ export class Ec2ConnectionManager {
60
64
return new DefaultIamClient ( this . regionCode )
61
65
}
62
66
63
- protected async getAttachedPolicies ( instanceId : string ) : Promise < IAM . AttachedPolicy [ ] > {
64
- const IamRole = await this . ec2Client . getAttachedIamRole ( instanceId )
65
- if ( ! IamRole ?. Arn ) {
66
- return [ ]
67
- }
68
- try {
69
- const attachedPolicies = await this . iamClient . listAttachedRolePolicies ( IamRole . Arn )
70
- return attachedPolicies
71
- } catch ( e ) {
72
- if ( isAwsError ( e ) && e . code == 'NoSuchEntity' ) {
73
- const errorMessage = `Attached role does not exist in IAM: ${ IamRole . Arn } .`
74
- getLogger ( ) . error ( `ec2: ${ errorMessage } ` )
75
- throw ToolkitError . chain ( e , errorMessage , {
76
- code : e . code ,
77
- documentationUri : this . policyDocumentationUri ,
78
- } )
79
- }
80
- throw ToolkitError . chain ( e as Error , `Failed to check policies for EC2 instance: ${ instanceId } ` , {
81
- code : 'PolicyCheck' ,
82
- } )
67
+ public async getAttachedIamRole ( instanceId : string ) : Promise < IAM . Role | undefined > {
68
+ const IamInstanceProfile = await this . ec2Client . getAttachedIamInstanceProfile ( instanceId )
69
+ if ( IamInstanceProfile && IamInstanceProfile . Arn ) {
70
+ const IamRole = await this . iamClient . getIAMRoleFromInstanceProfile ( IamInstanceProfile . Arn )
71
+ return IamRole
83
72
}
84
73
}
85
74
86
- public async hasProperPolicies ( instanceId : string ) : Promise < boolean > {
87
- const attachedPolicies = ( await this . getAttachedPolicies ( instanceId ) ) . map ( policy => policy . PolicyName ! )
75
+ public async hasProperPolicies ( IamRoleArn : string ) : Promise < boolean > {
76
+ const attachedPolicies = ( await this . iamClient . listAttachedRolePolicies ( IamRoleArn ) ) . map (
77
+ policy => policy . PolicyName !
78
+ )
88
79
const requiredPolicies = [ 'AmazonSSMManagedInstanceCore' , 'AmazonSSMManagedEC2InstanceDefaultPolicy' ]
89
80
90
81
return requiredPolicies . length !== 0 && requiredPolicies . every ( policy => attachedPolicies . includes ( policy ) )
@@ -100,42 +91,43 @@ export class Ec2ConnectionManager {
100
91
throw new ToolkitError ( generalErrorMessage + message , errorInfo )
101
92
}
102
93
103
- protected async throwPolicyError ( selection : Ec2Selection ) {
104
- const role = await this . ec2Client . getAttachedIamRole ( selection . instanceId )
105
-
106
- const baseMessage = 'Ensure an IAM role with the required policies is attached to the instance.'
107
- const messageExtension =
108
- role && role . Arn
109
- ? `Found attached role ${ role . Arn } .`
110
- : `Failed to find role attached to ${ selection . instanceId } `
111
- const fullMessage = `${ baseMessage } ${ messageExtension } `
112
-
113
- this . throwConnectionError ( fullMessage , selection , {
114
- code : 'EC2SSMPermission' ,
115
- documentationUri : this . policyDocumentationUri ,
116
- } )
117
- }
118
-
119
- public async checkForStartSessionError ( selection : Ec2Selection ) : Promise < void > {
94
+ private async checkForInstanceStatusError ( selection : Ec2Selection ) : Promise < void > {
120
95
const isInstanceRunning = await this . isInstanceRunning ( selection . instanceId )
121
- const hasProperPolicies = await this . hasProperPolicies ( selection . instanceId )
122
- const isSsmAgentRunning = ( await this . ssmClient . getInstanceAgentPingStatus ( selection . instanceId ) ) == 'Online'
123
96
124
97
if ( ! isInstanceRunning ) {
125
98
const message = 'Ensure the target instance is running and not currently starting, stopping, or stopped.'
126
99
this . throwConnectionError ( message , selection , { code : 'EC2SSMStatus' } )
127
100
}
101
+ }
102
+
103
+ private async checkForInstancePermissionsError ( selection : Ec2Selection ) : Promise < void > {
104
+ const IamRole = await this . getAttachedIamRole ( selection . instanceId )
105
+
106
+ if ( ! IamRole ) {
107
+ const message = `No IAM role attached to instance: ${ selection . instanceId } `
108
+ this . throwConnectionError ( message , selection , { code : 'EC2SSMPermission' } )
109
+ }
110
+
111
+ const hasProperPolicies = await this . hasProperPolicies ( IamRole ! . Arn )
128
112
129
113
if ( ! hasProperPolicies ) {
130
- await this . throwPolicyError ( selection )
114
+ const message = `Ensure an IAM role with the required policies is attached to the instance. Found attached role: ${
115
+ IamRole ! . Arn
116
+ } `
117
+ this . throwConnectionError ( message , selection , {
118
+ code : 'EC2SSMPermission' ,
119
+ documentationUri : this . policyDocumentationUri ,
120
+ } )
131
121
}
122
+ }
123
+
124
+ private async checkForInstanceSsmError ( selection : Ec2Selection ) : Promise < void > {
125
+ const isSsmAgentRunning = ( await this . ssmClient . getInstanceAgentPingStatus ( selection . instanceId ) ) == 'Online'
132
126
133
127
if ( ! isSsmAgentRunning ) {
134
128
this . throwConnectionError ( 'Is SSM Agent running on the target instance?' , selection , {
135
129
code : 'EC2SSMAgentStatus' ,
136
- documentationUri : vscode . Uri . parse (
137
- 'https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html'
138
- ) ,
130
+ documentationUri : this . ssmAgentDocumentationUri ,
139
131
} )
140
132
}
141
133
}
@@ -147,6 +139,14 @@ export class Ec2ConnectionManager {
147
139
} )
148
140
}
149
141
142
+ public async checkForStartSessionError ( selection : Ec2Selection ) : Promise < void > {
143
+ await this . checkForInstanceStatusError ( selection )
144
+
145
+ await this . checkForInstancePermissionsError ( selection )
146
+
147
+ await this . checkForInstanceSsmError ( selection )
148
+ }
149
+
150
150
private async openSessionInTerminal ( session : Session , selection : Ec2Selection ) {
151
151
const ssmPlugin = await getOrInstallCli ( 'session-manager-plugin' , ! isCloud9 )
152
152
const shellArgs = [ JSON . stringify ( session ) , selection . region , 'StartSession' ]
0 commit comments