Skip to content

[Draft] feat: add support for accountId in imds #1621

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft

Conversation

smilkuri
Copy link
Contributor

@smilkuri smilkuri commented Jun 16, 2025

Issue #, if available:
Internal JS-5966

Description of changes:

This PR adds support for version 2.1 of the IMDS credentials provider. With this update, the provider can now retrieve credentials that include an account ID.

To support this functionality, the credentials provider first attempts to access the extended API endpoint, which ends with -extended. If this endpoint returns a 404, the provider falls back to the legacy API endpoint—the same one used in the IMDS credentials provider v2.0.

This "try extended API, then fall back to legacy API" pattern is applied to both retrieving the IMDS instance profile name and fetching credentials.
Only the extended API can return credentials that include an account ID.

The PR also made the following IMDS credentials providers options configurable:

  • Disable IMDS credentials fetching
  • IMDS instance profile name

Both options can be configured via environment variables or a shared config file, with environment variables taking precedence over the config file.

ToDo:

  • Add additional testcases for unstable profile and fallback to legacy
  • Add support for feature Id tracking

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

import { CredentialsProviderError } from "@smithy/property-provider";
import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest";

import { InstanceMetadataV1FallbackError } from "./error/InstanceMetadataV1FallbackError";
import { fromInstanceMetadata } from "./fromInstanceMetadata";
import { checkIfImdsDisabled, fromInstanceMetadata } from "./fromInstanceMetadata";
import * as fromInstanceMetadataModule from "./fromInstanceMetadata";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use explicit imports instead of *

});

beforeEach(() => {
vi.mocked(staticStabilityProvider).mockImplementation((input) => input);
vi.mocked(getInstanceMetadataEndpoint).mockResolvedValue({ hostname } as any);
vi.mocked(loadConfig).mockReturnValue(() => Promise.resolve(false));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is hard to understand because the mock is too broad. What config property is being targeted by this mock return value?

Can the mock be made more specific so we can tell what config property it is trying to intercept?

@@ -72,6 +79,65 @@ describe("fromInstanceMetadata", () => {
vi.resetAllMocks();
});

it("returns no credentials when AWS_EC2_METADATA_DISABLED=true", async () => {
vi.mocked(loadConfig).mockReturnValueOnce(() => Promise.resolve(true));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above: mock is too broad

vi.mocked(retry).mockImplementation((fn: any) => fn());
vi.mocked(fromImdsCredentials).mockReturnValue(mockCreds);

const result = await fromInstanceMetadata({ ec2InstanceProfileName: profileName })();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variable name too generic

is this "credentials"?


const provider = fromInstanceMetadata({});

const result = await provider();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result should be called credentials

.mockRejectedValueOnce(Object.assign(new Error(), { statusCode: 404 }))
.mockResolvedValueOnce(legacyProfileName as any);

result = await fromInstanceMetadataModule.getImdsProfileHelper(options, mockMaxRetries, {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename all variables called "result" to be more specific

* @internal
* Gets IMDS profile with proper error handling and retries
*/
export const getImdsProfileHelper = async (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getImdsProfile

}
}
} catch (err) {
throw err;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no point in catching an error just to throw it, remove the outer try-catch

* @internal
* Checks if IMDS credential fetching is disabled through configuration
*/
export const checkIfImdsDisabled = async (profile?: string, logger?: any): Promise<void> => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function name should be throwIfImdsTurnedOff

"check" doesn't imply it will do anything

)();

// Check runtime config (highest priority)
const name = init.ec2InstanceProfileName || profileName;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const name = init.ec2InstanceProfileName || profileName;
const name = init.ec2InstanceProfileName ?? profileName;

@kuhe
Copy link
Contributor

kuhe commented Jun 18, 2025

  • create e2e test suite runnable in an EC2 instance. Extra testing is important for credentials-related code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants