Skip to content

Commit 957a12e

Browse files
authored
fix(cli): --profile is ignored if AWS_ variables are set (#10362)
Change behavior to match the behavior of the AWS CLI. If `--profile` is set, the given profile from the INI files will be used to the exclusion of everything else. If `--profile` is not set, the regular ambient credential process is followed. `AWS_PROFILE` is part of ambient credentials, and setting it does not lead to triggering the new behavior. This is consistent with how the AWS CLI behaves. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 25256d0 commit 957a12e

File tree

3 files changed

+49
-39
lines changed

3 files changed

+49
-39
lines changed

packages/aws-cdk/lib/api/aws-auth/awscli-compatible.ts

+20-7
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,16 @@ export class AwsCliCompatible {
3434
*/
3535
public static async credentialChain(options: CredentialChainOptions = {}) {
3636

37-
const profile = options.profile || process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default';
37+
// To match AWS CLI behavior, if a profile is explicitly given using --profile,
38+
// we use that to the exclusion of everything else (note: this does not apply
39+
// to AWS_PROFILE, environment credentials still take precedence over AWS_PROFILE)
40+
if (options.profile) {
41+
await forceSdkToReadConfigIfPresent();
42+
const theProfile = options.profile;
43+
return new AWS.CredentialProviderChain([() => profileCredentials(theProfile)]);
44+
}
45+
46+
const implicitProfile = process.env.AWS_PROFILE || process.env.AWS_DEFAULT_PROFILE || 'default';
3847

3948
const sources = [
4049
() => new AWS.EnvironmentCredentials('AWS'),
@@ -45,12 +54,7 @@ export class AwsCliCompatible {
4554
// Force reading the `config` file if it exists by setting the appropriate
4655
// environment variable.
4756
await forceSdkToReadConfigIfPresent();
48-
sources.push(() => new PatchedSharedIniFileCredentials({
49-
profile,
50-
filename: credentialsFileName(),
51-
httpOptions: options.httpOptions,
52-
tokenCodeFn,
53-
}));
57+
sources.push(() => profileCredentials(implicitProfile));
5458
}
5559

5660
if (options.containerCreds ?? hasEcsCredentials()) {
@@ -63,6 +67,15 @@ export class AwsCliCompatible {
6367
}
6468

6569
return new AWS.CredentialProviderChain(sources);
70+
71+
function profileCredentials(profileName: string) {
72+
return new PatchedSharedIniFileCredentials({
73+
profile: profileName,
74+
filename: credentialsFileName(),
75+
httpOptions: options.httpOptions,
76+
tokenCodeFn,
77+
});
78+
}
6679
}
6780

6881
/**

packages/aws-cdk/test/api/sdk-provider.test.ts

+20-21
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,22 @@ describe('with default config files', () => {
152152
expect(sdkConfig(sdk).region).toEqual('eu-bla-5');
153153
});
154154

155+
test('passing profile does not use EnvironmentCredentials', async () => {
156+
// GIVEN
157+
const provider = await SdkProvider.withAwsCliCompatibleDefaults({ ...defaultCredOptions, profile: 'foo' });
158+
159+
const environmentCredentialsPrototype = (new AWS.EnvironmentCredentials('AWS')).constructor.prototype;
160+
161+
await withMocked(environmentCredentialsPrototype, 'refresh', async (refresh) => {
162+
refresh.mockImplementation((callback: (err?: Error) => void) => callback(new Error('This function should not have been called')));
163+
164+
// WHEN
165+
await provider.defaultAccount();
166+
167+
expect(refresh).not.toHaveBeenCalled();
168+
});
169+
});
170+
155171
test('mixed profile credentials', async () => {
156172
// WHEN
157173
const provider = await SdkProvider.withAwsCliCompatibleDefaults({ ...defaultCredOptions, profile: 'foo' });
@@ -309,9 +325,6 @@ test('can assume role without a [default] profile', async () => {
309325
const provider = await SdkProvider.withAwsCliCompatibleDefaults({
310326
...defaultCredOptions,
311327
profile: 'assumable',
312-
httpOptions: {
313-
proxyAddress: 'http://DOESNTMATTER/',
314-
},
315328
});
316329

317330
const account = await provider.defaultAccount();
@@ -326,8 +339,7 @@ test('can assume role with ecs credentials', async () => {
326339

327340
// GIVEN
328341
bockfs({
329-
'/home/me/.bxt/credentials': dedent(`
330-
`),
342+
'/home/me/.bxt/credentials': '',
331343
'/home/me/.bxt/config': dedent(`
332344
[profile ecs]
333345
role_arn=arn:aws:iam::12356789012:role/Assumable
@@ -343,9 +355,6 @@ test('can assume role with ecs credentials', async () => {
343355
const provider = await SdkProvider.withAwsCliCompatibleDefaults({
344356
...defaultCredOptions,
345357
profile: 'ecs',
346-
httpOptions: {
347-
proxyAddress: 'http://DOESNTMATTER/',
348-
},
349358
});
350359

351360
await provider.defaultAccount();
@@ -364,8 +373,7 @@ test('can assume role with ec2 credentials', async () => {
364373

365374
// GIVEN
366375
bockfs({
367-
'/home/me/.bxt/credentials': dedent(`
368-
`),
376+
'/home/me/.bxt/credentials': '',
369377
'/home/me/.bxt/config': dedent(`
370378
[profile ecs]
371379
role_arn=arn:aws:iam::12356789012:role/Assumable
@@ -381,9 +389,6 @@ test('can assume role with ec2 credentials', async () => {
381389
const provider = await SdkProvider.withAwsCliCompatibleDefaults({
382390
...defaultCredOptions,
383391
profile: 'ecs',
384-
httpOptions: {
385-
proxyAddress: 'http://DOESNTMATTER/',
386-
},
387392
});
388393

389394
await provider.defaultAccount();
@@ -402,8 +407,7 @@ test('can assume role with env credentials', async () => {
402407

403408
// GIVEN
404409
bockfs({
405-
'/home/me/.bxt/credentials': dedent(`
406-
`),
410+
'/home/me/.bxt/credentials': '',
407411
'/home/me/.bxt/config': dedent(`
408412
[profile ecs]
409413
role_arn=arn:aws:iam::12356789012:role/Assumable
@@ -419,9 +423,6 @@ test('can assume role with env credentials', async () => {
419423
const provider = await SdkProvider.withAwsCliCompatibleDefaults({
420424
...defaultCredOptions,
421425
profile: 'ecs',
422-
httpOptions: {
423-
proxyAddress: 'http://DOESNTMATTER/',
424-
},
425426
});
426427

427428
await provider.defaultAccount();
@@ -437,6 +438,7 @@ test('can assume role with env credentials', async () => {
437438
test('assume fails with unsupported credential_source', async () => {
438439
// GIVEN
439440
bockfs({
441+
'/home/me/.bxt/credentials': '',
440442
'/home/me/.bxt/config': dedent(`
441443
[profile assumable]
442444
role_arn=arn:aws:iam::12356789012:role/Assumable
@@ -463,9 +465,6 @@ test('assume fails with unsupported credential_source', async () => {
463465
const provider = await SdkProvider.withAwsCliCompatibleDefaults({
464466
...defaultCredOptions,
465467
profile: 'assumable',
466-
httpOptions: {
467-
proxyAddress: 'http://DOESNTMATTER/',
468-
},
469468
});
470469

471470
const account = await provider.defaultAccount();

packages/aws-cdk/test/util.ts

+9-11
Original file line numberDiff line numberDiff line change
@@ -166,20 +166,18 @@ export function withMocked<A extends object, K extends keyof A, B>(obj: A, key:
166166
const mockFn = jest.fn();
167167
(obj as any)[key] = mockFn;
168168

169-
let ret;
169+
let asyncFinally: boolean = false;
170170
try {
171-
ret = block(mockFn as any);
172-
} catch (e) {
173-
obj[key] = original;
174-
throw e;
175-
}
171+
const ret = block(mockFn as any);
172+
if (!isPromise(ret)) { return ret; }
176173

177-
if (!isPromise(ret)) {
178-
obj[key] = original;
179-
return ret;
174+
asyncFinally = true;
175+
return ret.finally(() => { obj[key] = original; }) as any;
176+
} finally {
177+
if (!asyncFinally) {
178+
obj[key] = original;
179+
}
180180
}
181-
182-
return ret.finally(() => { obj[key] = original; }) as any;
183181
}
184182

185183
function isPromise<A>(object: any): object is Promise<A> {

0 commit comments

Comments
 (0)