Skip to content

Commit d1b3c81

Browse files
authored
fix(cli): region specified in ~/.aws/credentials is ignored (#32133)
We just didn't consider the shared credentials file as returned by the SDK when loading configuration. Closes #32130. ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 6b0b1e1 commit d1b3c81

File tree

2 files changed

+305
-1
lines changed

2 files changed

+305
-1
lines changed

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

+19-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,25 @@ export class AwsCliCompatible {
149149
*/
150150
async function getRegionFromIni(profile: string): Promise<string | undefined> {
151151
const sharedFiles = await loadSharedConfigFiles({ ignoreCache: true });
152-
return sharedFiles?.configFile?.[profile]?.region || sharedFiles?.configFile?.default?.region;
152+
153+
// Priority:
154+
//
155+
// credentials come before config because aws-cli v1 behaves like that.
156+
//
157+
// 1. profile-region-in-credentials
158+
// 2. profile-region-in-config
159+
// 3. default-region-in-credentials
160+
// 4. default-region-in-config
161+
162+
return getRegionFromIniFile(profile, sharedFiles.credentialsFile)
163+
?? getRegionFromIniFile(profile, sharedFiles.configFile)
164+
?? getRegionFromIniFile('default', sharedFiles.credentialsFile)
165+
?? getRegionFromIniFile('default', sharedFiles.configFile);
166+
167+
}
168+
169+
function getRegionFromIniFile(profile: string, data?: any) {
170+
return data?.[profile]?.region;
153171
}
154172

155173
function tryGetCACert(bundlePath?: string) {

Diff for: packages/aws-cdk/test/api/aws-auth/awscli-compatible.test.ts

+286
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,291 @@
1+
import * as os from 'os';
2+
import * as path from 'path';
3+
import * as fs from 'fs-extra';
14
import { AwsCliCompatible } from '../../../lib/api/aws-auth/awscli-compatible';
25

6+
describe('AwsCliCompatible.region', () => {
7+
8+
beforeEach(() => {
9+
10+
// make sure we don't mistakenly point to an unrelated file
11+
process.env.AWS_CONFIG_FILE = '/dev/null';
12+
process.env.AWS_SHARED_CREDENTIALS_FILE = '/dev/null';
13+
14+
// these take precedence over the ini files so we need to disable them for
15+
// the test to invoke the right function
16+
delete process.env.AWS_REGION;
17+
delete process.env.AMAZON_REGION;
18+
delete process.env.AWS_DEFAULT_REGION;
19+
delete process.env.AMAZON_DEFAULT_REGION;
20+
21+
});
22+
23+
test('default region can be specified in config', async () => {
24+
25+
const config = `
26+
[default]
27+
region=region-in-config
28+
`;
29+
30+
await expect(region({ configFile: config })).resolves.toBe('region-in-config');
31+
});
32+
33+
test('default region can be specified in credentials', async () => {
34+
35+
const creds = `
36+
[default]
37+
region=region-in-credentials
38+
`;
39+
40+
await expect(region({ credentialsFile: creds })).resolves.toBe('region-in-credentials');
41+
42+
});
43+
44+
test('profile region can be specified in config', async () => {
45+
46+
const config = `
47+
[profile user1]
48+
region=region-in-config
49+
`;
50+
51+
await expect(region({ configFile: config, profile: 'user1' })).resolves.toBe('region-in-config');
52+
53+
});
54+
55+
test('profile region can be specified in credentials', async () => {
56+
57+
const creds = `
58+
[user1]
59+
region=region-in-credentials
60+
`;
61+
62+
await expect(region({ credentialsFile: creds, profile: 'user1' })).resolves.toBe('region-in-credentials');
63+
64+
});
65+
66+
test('with profile | profile-region-in-credentials is priority 1', async () => {
67+
68+
const config = `
69+
[default]
70+
region=default-region-in-config
71+
72+
[profile user]
73+
region=profile-region-in-config
74+
75+
`;
76+
77+
const creds = `
78+
[default]
79+
region=default-region-in-credentials
80+
81+
[user]
82+
region=profile-region-in-credentials
83+
`;
84+
85+
await expect(region({ credentialsFile: creds, configFile: config, profile: 'user' })).resolves.toBe('profile-region-in-credentials');
86+
});
87+
88+
test('with profile | profile-region-in-config is priority 2', async () => {
89+
90+
const config = `
91+
[default]
92+
region=default-region-in-config
93+
94+
[profile user]
95+
region=profile-region-in-config
96+
97+
`;
98+
99+
const creds = `
100+
[default]
101+
region=default-region-in-credentials
102+
103+
[user]
104+
`;
105+
106+
await expect(region({ credentialsFile: creds, configFile: config, profile: 'user' })).resolves.toBe('profile-region-in-config');
107+
});
108+
109+
test('with profile | default-region-in-credentials is priority 3', async () => {
110+
111+
const config = `
112+
[default]
113+
region=default-region-in-config
114+
115+
[profile user]
116+
117+
`;
118+
119+
const creds = `
120+
[default]
121+
region=default-region-in-credentials
122+
123+
[user]
124+
`;
125+
126+
await expect(region({ credentialsFile: creds, configFile: config, profile: 'user' })).resolves.toBe('default-region-in-credentials');
127+
});
128+
129+
test('with profile | default-region-in-config is priority 4', async () => {
130+
131+
const config = `
132+
[default]
133+
region=default-region-in-config
134+
135+
[profile user]
136+
137+
`;
138+
139+
const creds = `
140+
[default]
141+
142+
[user]
143+
`;
144+
145+
await expect(region({ credentialsFile: creds, configFile: config, profile: 'user' })).resolves.toBe('default-region-in-config');
146+
});
147+
148+
test('with profile | us-east-1 is priority 5', async () => {
149+
150+
const config = `
151+
[default]
152+
153+
[profile user]
154+
155+
`;
156+
157+
const creds = `
158+
[default]
159+
160+
[user]
161+
`;
162+
163+
await expect(region({ credentialsFile: creds, configFile: config, profile: 'user' })).resolves.toBe('us-east-1');
164+
});
165+
166+
test('without profile | default-region-in-credentials is priority 1', async () => {
167+
168+
const config = `
169+
[default]
170+
region=default-region-in-config
171+
172+
`;
173+
174+
const creds = `
175+
[default]
176+
region=default-region-in-credentials
177+
178+
`;
179+
180+
await expect(region({ credentialsFile: creds, configFile: config })).resolves.toBe('default-region-in-credentials');
181+
});
182+
183+
test('without profile | default-region-in-config is priority 2', async () => {
184+
185+
const config = `
186+
[default]
187+
region=default-region-in-config
188+
189+
`;
190+
191+
const creds = `
192+
[default]
193+
194+
`;
195+
196+
await expect(region({ credentialsFile: creds, configFile: config })).resolves.toBe('default-region-in-config');
197+
});
198+
199+
test('without profile | us-east-1 is priority 3', async () => {
200+
201+
const config = `
202+
[default]
203+
204+
`;
205+
206+
const creds = `
207+
[default]
208+
209+
`;
210+
211+
await expect(region({ credentialsFile: creds, configFile: config })).resolves.toBe('us-east-1');
212+
});
213+
214+
});
215+
216+
async function region(opts: {
217+
readonly configFile?: string;
218+
readonly credentialsFile?: string;
219+
readonly profile?: string;
220+
}) {
221+
222+
const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'awscli-compatible.test'));
223+
224+
try {
225+
226+
if (opts.configFile) {
227+
const configPath = path.join(workdir, 'config');
228+
fs.writeFileSync(configPath, opts.configFile);
229+
process.env.AWS_CONFIG_FILE = configPath;
230+
}
231+
232+
if (opts.credentialsFile) {
233+
const credentialsPath = path.join(workdir, 'credentials');
234+
fs.writeFileSync(credentialsPath, opts.credentialsFile);
235+
process.env.AWS_SHARED_CREDENTIALS_FILE = credentialsPath;
236+
}
237+
238+
return await AwsCliCompatible.region(opts.profile);
239+
240+
} finally {
241+
fs.removeSync(workdir);
242+
}
243+
}
244+
245+
describe('Session token', () => {
246+
beforeEach(() => {
247+
process.env.AWS_ACCESS_KEY_ID = 'foo';
248+
process.env.AWS_SECRET_ACCESS_KEY = 'bar';
249+
});
250+
251+
test('does not mess up with session token env variables if they are undefined', async () => {
252+
// Making sure these variables are not defined
253+
delete process.env.AWS_SESSION_TOKEN;
254+
delete process.env.AMAZON_SESSION_TOKEN;
255+
256+
await AwsCliCompatible.credentialChainBuilder();
257+
258+
expect(process.env.AWS_SESSION_TOKEN).toBeUndefined();
259+
});
260+
261+
test('preserves AWS_SESSION_TOKEN if it is defined', async () => {
262+
process.env.AWS_SESSION_TOKEN = 'aaa';
263+
delete process.env.AMAZON_SESSION_TOKEN;
264+
265+
await AwsCliCompatible.credentialChainBuilder();
266+
267+
expect(process.env.AWS_SESSION_TOKEN).toEqual('aaa');
268+
});
269+
270+
test('assigns AWS_SESSION_TOKEN if it is not defined but AMAZON_SESSION_TOKEN is', async () => {
271+
delete process.env.AWS_SESSION_TOKEN;
272+
process.env.AMAZON_SESSION_TOKEN = 'aaa';
273+
274+
await AwsCliCompatible.credentialChainBuilder();
275+
276+
expect(process.env.AWS_SESSION_TOKEN).toEqual('aaa');
277+
});
278+
279+
test('preserves AWS_SESSION_TOKEN if both are defined', async () => {
280+
process.env.AWS_SESSION_TOKEN = 'aaa';
281+
process.env.AMAZON_SESSION_TOKEN = 'bbb';
282+
283+
await AwsCliCompatible.credentialChainBuilder();
284+
285+
expect(process.env.AWS_SESSION_TOKEN).toEqual('aaa');
286+
});
287+
});
288+
3289
describe('Session token', () => {
4290
beforeEach(() => {
5291
process.env.AWS_ACCESS_KEY_ID = 'foo';

0 commit comments

Comments
 (0)