@@ -100,6 +100,9 @@ pub mod external_account {
100100 #[ derive( Clone , Deserialize , Debug ) ]
101101 #[ serde( untagged) ]
102102 pub enum Source {
103+ /// AWS provider-specific credential source.
104+ #[ serde( rename_all = "snake_case" ) ]
105+ Aws ( AwsSource ) ,
103106 /// URL-based credential source.
104107 #[ serde( rename_all = "snake_case" ) ]
105108 Url ( UrlSource ) ,
@@ -133,6 +136,22 @@ pub mod external_account {
133136 pub format : Format ,
134137 }
135138
139+ /// Configuration for AWS provider-specific workload identity federation.
140+ #[ derive( Clone , Deserialize , Debug ) ]
141+ #[ serde( rename_all = "snake_case" ) ]
142+ pub struct AwsSource {
143+ /// The environment identifier, currently `aws1`.
144+ pub environment_id : String ,
145+ /// Metadata URL used to derive the region when env vars are absent.
146+ pub region_url : Option < String > ,
147+ /// Metadata URL used to retrieve the role name and credentials.
148+ pub url : Option < String > ,
149+ /// Regional GetCallerIdentity verification URL template.
150+ pub regional_cred_verification_url : String ,
151+ /// Optional IMDSv2 token URL.
152+ pub imdsv2_session_token_url : Option < String > ,
153+ }
154+
136155 /// Configuration for executing a command to load credentials.
137156 #[ derive( Clone , Deserialize , Debug ) ]
138157 #[ serde( rename_all = "snake_case" ) ]
@@ -407,6 +426,48 @@ mod tests {
407426 let cred = CredentialFile :: from_slice ( ea_json. as_bytes ( ) ) . unwrap ( ) ;
408427 assert ! ( matches!( cred, CredentialFile :: ExternalAccount ( _) ) ) ;
409428
429+ let aws_ea_json = r#"{
430+ "type": "external_account",
431+ "audience": "test_audience",
432+ "subject_token_type": "urn:ietf:params:aws:token-type:aws4_request",
433+ "token_url": "https://example.com/token",
434+ "credential_source": {
435+ "environment_id": "aws1",
436+ "region_url": "http://169.254.169.254/latest/meta-data/placement/availability-zone",
437+ "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials",
438+ "regional_cred_verification_url": "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
439+ "imdsv2_session_token_url": "http://169.254.169.254/latest/api/token"
440+ }
441+ }"# ;
442+ let cred = CredentialFile :: from_slice ( aws_ea_json. as_bytes ( ) ) . unwrap ( ) ;
443+ match cred {
444+ CredentialFile :: ExternalAccount ( external_account) => match external_account
445+ . credential_source
446+ {
447+ external_account:: Source :: Aws ( source) => {
448+ assert_eq ! ( source. environment_id, "aws1" ) ;
449+ assert_eq ! (
450+ source. region_url. as_deref( ) ,
451+ Some ( "http://169.254.169.254/latest/meta-data/placement/availability-zone" )
452+ ) ;
453+ assert_eq ! (
454+ source. url. as_deref( ) ,
455+ Some ( "http://169.254.169.254/latest/meta-data/iam/security-credentials" )
456+ ) ;
457+ assert_eq ! (
458+ source. regional_cred_verification_url,
459+ "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"
460+ ) ;
461+ assert_eq ! (
462+ source. imdsv2_session_token_url. as_deref( ) ,
463+ Some ( "http://169.254.169.254/latest/api/token" )
464+ ) ;
465+ }
466+ _ => panic ! ( "Expected Aws source" ) ,
467+ } ,
468+ _ => panic ! ( "Expected ExternalAccount" ) ,
469+ }
470+
410471 let exec_ea_json = r#"{
411472 "type": "external_account",
412473 "audience": "test_audience",
0 commit comments