@@ -79,6 +79,12 @@ pub enum AwsAuthentication {
79
79
#[ configurable( metadata( docs:: examples = "arn:aws:iam::123456789098:role/my_role" ) ) ]
80
80
assume_role : Option < String > ,
81
81
82
+ /// The optional unique external ID in conjunction with role to assume.
83
+ ///
84
+ /// [external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
85
+ #[ configurable( metadata( docs:: examples = "randomEXAMPLEidString" ) ) ]
86
+ external_id : Option < String > ,
87
+
82
88
/// The [AWS region][aws_region] to send STS requests to.
83
89
///
84
90
/// If not set, this will default to the configured region
@@ -115,6 +121,12 @@ pub enum AwsAuthentication {
115
121
#[ configurable( metadata( docs:: examples = "arn:aws:iam::123456789098:role/my_role" ) ) ]
116
122
assume_role : String ,
117
123
124
+ /// The optional unique external ID in conjunction with role to assume.
125
+ ///
126
+ /// [external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
127
+ #[ configurable( metadata( docs:: examples = "randomEXAMPLEidString" ) ) ]
128
+ external_id : Option < String > ,
129
+
118
130
/// Timeout for assuming the role, in seconds.
119
131
///
120
132
/// Relevant when the default credentials chain or `assume_role` is used.
@@ -199,6 +211,7 @@ impl AwsAuthentication {
199
211
access_key_id,
200
212
secret_access_key,
201
213
assume_role,
214
+ external_id,
202
215
region,
203
216
} => {
204
217
let provider = SharedCredentialsProvider :: new ( Credentials :: from_keys (
@@ -208,8 +221,10 @@ impl AwsAuthentication {
208
221
) ) ;
209
222
if let Some ( assume_role) = assume_role {
210
223
let auth_region = region. clone ( ) . map ( Region :: new) . unwrap_or ( service_region) ;
224
+ let auth_external_id = external_id. clone ( ) . unwrap ( ) ;
211
225
let provider = AssumeRoleProviderBuilder :: new ( assume_role)
212
226
. region ( auth_region)
227
+ . external_id ( auth_external_id)
213
228
. build ( provider) ;
214
229
return Ok ( SharedCredentialsProvider :: new ( provider) ) ;
215
230
}
@@ -232,15 +247,17 @@ impl AwsAuthentication {
232
247
}
233
248
AwsAuthentication :: Role {
234
249
assume_role,
250
+ external_id,
235
251
imds,
236
252
region,
237
253
..
238
254
} => {
239
255
let auth_region = region. clone ( ) . map ( Region :: new) . unwrap_or ( service_region) ;
256
+ let auth_external_id = external_id. clone ( ) . unwrap ( ) ;
240
257
let provider = AssumeRoleProviderBuilder :: new ( assume_role)
241
258
. region ( auth_region. clone ( ) )
259
+ . external_id ( auth_external_id)
242
260
. build ( default_credentials_provider ( auth_region, * imds) . await ?) ;
243
-
244
261
Ok ( SharedCredentialsProvider :: new ( provider) )
245
262
}
246
263
AwsAuthentication :: Default { imds, region, .. } => Ok ( SharedCredentialsProvider :: new (
@@ -259,6 +276,7 @@ impl AwsAuthentication {
259
276
access_key_id : "dummy" . to_string ( ) . into ( ) ,
260
277
secret_access_key : "dummy" . to_string ( ) . into ( ) ,
261
278
assume_role : None ,
279
+ external_id : None ,
262
280
region : None ,
263
281
}
264
282
}
@@ -295,6 +313,7 @@ mod tests {
295
313
#[ derive( Serialize , Deserialize , Clone , Debug ) ]
296
314
struct ComponentConfig {
297
315
assume_role : Option < String > ,
316
+ external_id : Option < String > ,
298
317
#[ serde( default ) ]
299
318
auth : AwsAuthentication ,
300
319
}
@@ -396,6 +415,20 @@ mod tests {
396
415
assert ! ( matches!( config. auth, AwsAuthentication :: Role { .. } ) ) ;
397
416
}
398
417
418
+ #[ test]
419
+ fn parsing_external_id_with_assume_role ( ) {
420
+ let config = toml:: from_str :: < ComponentConfig > (
421
+ r#"
422
+ auth.assume_role = "root"
423
+ auth.external_id = "id"
424
+ auth.load_timeout_secs = 10
425
+ "# ,
426
+ )
427
+ . unwrap ( ) ;
428
+
429
+ assert ! ( matches!( config. auth, AwsAuthentication :: Role { .. } ) ) ;
430
+ }
431
+
399
432
#[ test]
400
433
fn parsing_assume_role_with_imds_client ( ) {
401
434
let config = toml:: from_str :: < ComponentConfig > (
@@ -411,11 +444,13 @@ mod tests {
411
444
match config. auth {
412
445
AwsAuthentication :: Role {
413
446
assume_role,
447
+ external_id,
414
448
load_timeout_secs,
415
449
imds,
416
450
region,
417
451
} => {
418
452
assert_eq ! ( & assume_role, "root" ) ;
453
+ assert_eq ! ( external_id, None ) ;
419
454
assert_eq ! ( load_timeout_secs, None ) ;
420
455
assert ! ( matches!(
421
456
imds,
@@ -446,11 +481,13 @@ mod tests {
446
481
match config. auth {
447
482
AwsAuthentication :: Role {
448
483
assume_role,
484
+ external_id,
449
485
load_timeout_secs,
450
486
imds,
451
487
region,
452
488
} => {
453
489
assert_eq ! ( & assume_role, "auth.root" ) ;
490
+ assert_eq ! ( external_id, None ) ;
454
491
assert_eq ! ( load_timeout_secs, Some ( 10 ) ) ;
455
492
assert ! ( matches!( imds, ImdsAuthentication { .. } ) ) ;
456
493
assert_eq ! ( region. unwrap( ) , "us-west-2" ) ;
@@ -501,6 +538,38 @@ mod tests {
501
538
}
502
539
}
503
540
541
+ #[ test]
542
+ fn parsing_static_with_assume_role_and_external_id ( ) {
543
+ let config = toml:: from_str :: < ComponentConfig > (
544
+ r#"
545
+ auth.access_key_id = "key"
546
+ auth.secret_access_key = "other"
547
+ auth.assume_role = "root"
548
+ auth.external_id = "id"
549
+ "# ,
550
+ )
551
+ . unwrap ( ) ;
552
+
553
+ match config. auth {
554
+ AwsAuthentication :: AccessKey {
555
+ access_key_id,
556
+ secret_access_key,
557
+ assume_role,
558
+ external_id,
559
+ ..
560
+ } => {
561
+ assert_eq ! ( & access_key_id, & SensitiveString :: from( "key" . to_string( ) ) ) ;
562
+ assert_eq ! (
563
+ & secret_access_key,
564
+ & SensitiveString :: from( "other" . to_string( ) )
565
+ ) ;
566
+ assert_eq ! ( & assume_role, & Some ( "root" . to_string( ) ) ) ;
567
+ assert_eq ! ( & external_id, & Some ( "id" . to_string( ) ) ) ;
568
+ }
569
+ _ => panic ! ( ) ,
570
+ }
571
+ }
572
+
504
573
#[ test]
505
574
fn parsing_file ( ) {
506
575
let config = toml:: from_str :: < ComponentConfig > (
0 commit comments