aws-sdk-js-v3
aws-sdk-js-v3 copied to clipboard
Provide utilities for accessing EC2 Instance Metadata Service
Describe the feature
I'd like an easy way to access information from the EC2 Instance Metadata Service, similar to the MetadataService
that's available in the v2 SDK: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/MetadataService.html.
Use Case
I'd rather not re-implement the logic for picking the appropriate endpoint, refreshing the token, refetching the token, etc.
Proposed Solution
No response
Other Information
It looks like credential-provider-imds
has already implemented a lot of the token fetching, error handling, etc. Maybe that can be refactored into a shared place?
SDKs for other languages already implement this feature, including Go (https://docs.aws.amazon.com/sdk-for-go/api/aws/ec2metadata/).
Acknowledgements
- [X] I may be able to implement this feature request
- [ ] This feature might incur a breaking change
SDK version used
3.178.0
Environment details (OS name and version, etc.)
macOS 12.5.1
@nwalters512 thanks for opening this issue, I see it is implemented in go-v2 https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/ec2metadata as well, I'll discuss the feature with the team and update you soon.
+1
I have aborted the migration to v3 because of this -- it does not make sense to migrate all my clients but still keep aws-sdk
v2 around solely for MetaDataService
:(
Similar, I have also aborted upgrade to v3, this is an extremely critical piece of our infrastructure as we use it to determine which of our instances in the elastic beanstalk environment is the 'first' one as it does the cron jobs.
Please, @RanVaknin escalate this as a priority for the team as there is already signs this part of your SDK has been neglected. See https://github.com/aws/aws-sdk-js/issues/3584
@ajredniwja It's been almost 5 months, can you share an update from your team? I'd be happy to work on PR for this if it's something that y'all would accept.
@kuhe pulling in another amazon employee here. Considering that these irritating 4-line warnings are now spamming my dev team's console on every run, yet we are literally unable to migrate due to an incomplete v3 offering, this kind of unresponsiveness is very concerning and now reaching levels of being unacceptable.
Hi all,
I apologize for the long wait. I'll try to get someone to take a look, but since we have some higher priority issues they will probably take precedence.
I'll do my best. Ran~
Hi @nwalters512 ,
I've discussed it with the team. Each SDK has implemented this in a different fashion. This likely will not be transformed into its own IMDS Client
like in the Golang SDK. If there are customizations needed for the provider can you please elaborate the use cases?
Additionally, if you'd like to take a stab at cutting a PR, I'll have someone review it.
Thanks! Ran~
@RanVaknin I'm not looking to customize @aws-sdk/credential-provider-imds
per se - I just want some abstraction for "make an arbitrary request to the IMDS" just like what was available in the v2 SDK. As I mentioned in the original issue, I don't want to have to duplicate code for handling token fetching, expiration, refreshing, and so on. Most of that logic is already implemented in @aws-sdk/credential-provider-imds
, just not in a way that I can reuse it for generalized IMDS requests.
If you want a specific use case: I want to be able to discover the instance ID of the host on which my code is running. But in general, I want to be able to fetch any of the categories of information listed here: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html.
If you're not willing to implement this as a separate client package (e.g. @aws-sdk/client-ec2-metadata
), I'm not sure where I'd start with a PR. Are you thinking this would be a function exposed from @aws-sdk/client-ec2
? IIRC most of the clients are code-genned, so I'd appreciate some guidance on how to work with that.
As I'm becoming unsure of whether this will be providing similar functionality to v2, below is my use case in TypeScript:
The code is basically saying, 'is this current instance the first instance in the array of instances in my elastic beanstalk environment? if so, I consider this to be the 'master' instance, and I want this to be my Cron job runner.'. It is incomplete in the first 10 lines as I just cut and paste some bits together to tell the story.
Notice the casting of MetadataService to the any
type in order to be able to access the fetchMetadataToken
property. It was reported here (https://github.com/aws/aws-sdk-js/issues/3584), and was never resolved, just auto-closed. It would be nice for this typing issue to be fixed as part of this issue as well.
import AWS from 'aws-sdk';
const ElasticBeanstalk = new AWS.ElasticBeanstalk(opts);
const EC2 = new AWS.EC2(opts);
const MetadataService = new AWS.MetadataService(opts);
const elasticbeanstalk = ElasticBeanstalk;
const ec2 = EC2;
const metadata = MetadataService;
let masterInstanceInitialized = false;
async function cronChecker() {
const token: string = await promisify(
(metadata as any).fetchMetadataToken.bind(metadata)
)();
const currentInstanceId = await promisify(metadata.request.bind(metadata))(
'/latest/meta-data/instance-id',
{
headers: { 'x-aws-ec2-metadata-token': token },
}
);
console.info('InstanceId', currentInstanceId);
const params = {
Filters: [
{
Name: 'resource-id',
Values: [currentInstanceId],
},
],
};
const data = await promisify(ec2.describeTags.bind(ec2))(params);
const envIdTag = data.Tags.find(
(t) => t.Key === 'elasticbeanstalk:environment-id'
);
if (envIdTag === null) {
throw new Error(
'Failed to find the value of "elasticbeanstalk:environment-id" tag.'
);
}
const ebData = await elasticbeanstalk
.describeEnvironmentResources({ EnvironmentId: envIdTag.Value })
.promise();
const isMaster = !(
currentInstanceId !== ebData.EnvironmentResources.Instances[0].Id
);
const health = await elasticbeanstalk
.describeEnvironmentHealth({
AttributeNames: ['HealthStatus'],
EnvironmentId: envIdTag.Value,
})
.promise();
console.warn('Environment health is ' + health.HealthStatus + '.');
if (isMaster) {
initCronJobs();
} else {
killJobs();
}
}
I'm now receiving warnings about the impending deprecation of the v2 SDK (https://github.com/aws/aws-sdk-js/issues/4354):
(node:50693) NOTE: We are formalizing our plans to enter AWS SDK for JavaScript (v2) into maintenance mode in 2023.
Please migrate your code to use AWS SDK for JavaScript (v3).
For more information, check the migration guide at https://a.co/7PzMCcy
AWS.MetadataService
isn't mentioned anywhere on https://github.com/aws/aws-sdk-js-v3/blob/main/UPGRADING.md, nor is it handled by https://github.com/awslabs/aws-sdk-js-codemod. It feels very irresponsible to be so insistently pushing for a migration to a new package if that package is lacking necessary features.
I'm using the below js v3 sdk version
"@aws-sdk/client-secrets-manager": "^3.14.0", "@aws-sdk/client-sts": "^3.282.0", "@aws-sdk/credential-provider-imds": "^3.272.0",
Environment : EKS Pod
EC2 - worker
aws ec2 modify-instance-metadata-options --instance-id <instance_id> --http-tokens required --http-endpoint enabled --http-put-response-hop-limit 1
Pod is getting the role from service account the complete process of assuming role to service account is followed from below link https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html
This part of the code is assuming the pod role
const command = new GetCallerIdentityCommand({});
const data = await client1.send(command);
console.log(":get_caller_identity:", data)
Response
:get_caller_identity: {
'$metadata': {
httpStatusCode: 200,
requestId: 'requestId',
extendedRequestId: undefined,
cfId: undefined,
attempts: 1,
totalRetryDelay: 0
},
UserId: 'EXAMPLEID:aws-sdk-js-session-EXAMPLE_SESSION',
Account: 'ACCOUNT_ID',
Arn: 'arn:aws:sts::<ACCOUNT_ID>:assumed-role/<role_name_from_service_account>/aws-sdk-js-session-EXAMPLE_SESSION'
}
const client = new SecretsManagerClient({
region: process.env.REGION,
});
response = await client.send(
new GetSecretValueCommand({
SecretId: process.env.APP_SECRET_NAME,
VersionStage: "AWSCURRENT",
})
);
This above code returns the following response
[ProviderError: Error response received from instance metadata service] {
tryNextLink: true,
statusCode: 401
}
Complete code snippet
const {
SecretsManagerClient,
GetSecretValueCommand,
} = require("@aws-sdk/client-secrets-manager");
const { STSClient, GetCallerIdentityCommand } = require("@aws-sdk/client-sts");
const getSecret = async () => {
return new Promise(async (resolve, reject) => {
try {
const client1 = new STSClient({ region: process.env.REGION});
const command = new GetCallerIdentityCommand({});
const data = await client1.send(command);
console.log(":get_caller_identity:", data)
} catch (error) {
console.log("getting sts failed ")
return reject(error);
}
let response;
try {
const client = new SecretsManagerClient({
region: process.env.REGION,
});
response = await client.send(
new GetSecretValueCommand({
SecretId: process.env.APP_SECRET_NAME,
VersionStage: "AWSCURRENT",
})
);
} catch (error) {
console.log("Secret fetching failed ", error)
return reject(error);
}
const secret_string = response.SecretString;
const secrets = JSON.parse(secret_string);
resolve(secrets);
})
}
module.exports = { getSecret };
@psk200 that looks unrelated to this issue, which is specifically about making requests directly to the metadata service. As far as I can tell, your comment is just about obtaining credentials from the metadata service, which indeed works correctly. If you're experiencing any problems with that, please open a separate issue.
It's now over 2 months since the last comment. Is there an ETA on this?
What are the minimal changes I am supposed to make to the existing code above (https://github.com/aws/aws-sdk-js-v3/issues/4004#issuecomment-1442768967)?
+1
I have aborted the migration to v3 because of this -- it does not make sense to migrate all my clients but still keep
aws-sdk
v2 around solely forMetaDataService
:(
Same here
I ultimately built and published a small package to make talking to IMDS easier: @prairielearn/aws-imds
. Source and minimal documentation are available here: https://github.com/PrairieLearn/PrairieLearn/tree/master/packages/aws-imds
Hi this is also blocking our migration to v3.
@RanVaknin I elaborated on the use case as requested back in February, and it's now almost 6 months later, in Q2 of 2023, when v2 is supposed to be dropped in favor of v3. Zero follow up, zero alternative method or code examples provided, just putting things off over and over. This is placing my team in an increasingly risky scenario as year end approaches, and I think that's it's fair that some sort of conclusion is brought to this issue.
If not, please tell me the appropriate person to escalate this issue to.
Hi there. I'd like to add on that the lack of replacement for MetadataService
in the v3 SDK is blocking our team from migrating as well. Any guidance on alternative methods to get an instance's Id, Tags, Role, etc. would be helpful.
Hi @nwalters512, could I ask you to clarify what exactly the "easy way to access information from the EC2 Instance Metadata Service" might look like for you? Are you specifically requesting a client that can access things like, say instanceID? Would using a (possibly external) HTTP client in conjunction with the instancedata URL (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html) be satisfactory?
You mention credential-provider-imds
has a lot of functionality that you might require, do you just want a straightforward exposure to a functionality specifically like fetching (like in the package you published) or is the requirement "make an arbitrary request to the IMDS" mean the request()
method from v2 be ported to v3 as is?
I don't want to prescribe any particular solution here. At a high level, I just don't want to have to worry about IMDS tokens: fetching, refreshing, TTLs, error handling, retries, etc. If that manifests as an almost-direct port of v2's request()
, hopefully with Promise support, great! If you want to copy what Golang did and provide specific functions with well-defined return types like EC2Metadata.GetInstanceIdentityDocument
, even better (though the JS SDK's stubborn insistence on typing everything as potentially undefined would make this less-useful than in Golang).
Gone are the days when AWS focused on reducing undifferentiated heavy lifting. Today, AWS creates problems for us developers that we didn't have in the past.
@siddsriv has created https://www.npmjs.com/package/@aws-sdk/ec2-metadata-service.
import { MetadataService } from "@aws-sdk/ec2-metadata-service";
const metadataService = new MetadataService({
ec2MetadataV1Disabled: true,
});
const token = await metadataService.fetchMetadataToken();
const metadata = await metadataService.request("/latest/meta-data/", {});
console.log({
metadata,
});
Hi @nwalters512 and everyone else on the thread!
Please check out the last comment where we created the package that provides utils to access EC2 Instance Metadata Service (IMDS).
Let us know if you have any questions!
@aBurmeseDev @kuhe this is great, thanks for getting this done! Unfortunately the package isn't being built/published correctly; the file dist-types/MetadataService.d.ts
tries to read from dist-types/MetadataServiceOptions
, which does not exist. This causes a build error for tsc
in projects with "skipLibCheck": false
in their tsconfig.json
:
../../node_modules/@aws-sdk/ec2-metadata-service/dist-types/MetadataService.d.ts:1:40 - error TS2307: Cannot find module './MetadataServiceOptions' or its corresponding type declarations.
1 import { MetadataServiceOptions } from "./MetadataServiceOptions";
~~~~~~~~~~~~~~~~~~~~~~~~~~
Found 1 error in ../../node_modules/@aws-sdk/ec2-metadata-service/dist-types/MetadataService.d.ts:1
thank you for reporting this, we just put a fix out that will be released later today. let us know if you encounter any other issues or have questions.
@aBurmeseDev @siddsriv Thank you for pushing this out!
Can you point me to the SDK docs for the package? All good if they're not out yet, didn't know if I wasn't looking in the right place.
https://www.npmjs.com/package/@aws-sdk/ec2-metadata-service has basic instructions on how to make a request, which is most of what the package does.
You can then use the general documentation for IMDS https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html and take the URLs in those and input them to the JavaScript package for the same effect.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread.