Search code examples
node.jsaws-sdkarrow-functions

Returning value of key value pair from array/json output from an arrow function in node js


I have an arrow function written in JavaScript, running in Node.js, that gets EC2 instance information based on an EC2 instance id in AWS. Seems to me that in the function below InstanceIds is a parameter passed in from the CloudTrail event when the instance is created.

const AWS = require('aws-sdk');

exports.handler = async (event) => {
    /** find instance Operating System for OS tag */
    var instances = await Promise.all(event.detail.responseElements.instancesSet.items.map(x => getInstanceInfo({ InstanceIds: [x.instanceId] })));
    var newTags = [];
    /** 
    *if (event.detail.userIdentity.type === 'Root') {
    *    newTags.push({ Key: 'Owner', Value: 'Root' });
    *} else {
    *    newTags.push({ Key: 'Owner', Value: event.detail.userIdentity.arn.split('/').pop() });
    *};
    */
    newTags.push({ Key: 'Created', Value: event.detail.eventTime });
    //newTags.push({ Key: 'OS', Value: instanceos });
    //newTags.push({ Key: 'ImageId', Value: instance.OS });

    /** handling our logic specific to ec2 and run instances. We can add in other logic here */
    if (event.source === 'aws.ec2' && event.detail.eventName === 'RunInstances') {
        /** calling the update function for each instance */
        var instances = await Promise.all(event.detail.responseElements.instancesSet.items.map(x => getInstanceInfo({ InstanceIds: [x.instanceId] })));
        
        /** calling the update function for each instance */
        await Promise.all(instances.map(x => ec2Update(x, newTags)));

    } else if (event.source === '') {
        // place logic for other service here!
    }
    //console.log(instanceos)
    return "tags have been set!";
};

var ec2Update = (instance, tags) => new Promise(async (resolve) => {
    /** creating a new list of myTags since they are unique to this instance */
    var myTags = JSON.parse(JSON.stringify(tags));

    /** setting tag keys that we want to ignore and setting all to lowercase */
    var ignoreKeysLowerCase = ['name','aws:cloudformation:stack-name','aws:cloudformation:stack-id','aws:cloudformation:logical-id'];

    /** Getting the tags from the VPC to pass onto the instance */
    var vpc = await getVpc({ VpcIds: [instance.VpcId] });

    /** Getting Ignoring keys that we do not want to passed to the instance */
    vpc.Tags.map(x => { if (!ignoreKeysLowerCase.includes(x.Key.toLowerCase())) { myTags.push(x) } });

    /** Getting Ignoring keys that we do not want to passed to the instance */
    await setTags(instance.InstanceId, myTags);
    resolve();
});

var InstanceIds = (params) => new Promise((resolve) => {
    console.log(params)
    var aws = new AWS.EC2({ region: process.env.AWS_REGION }).describeInstances(params);
    aws.on('success', r => {
        resolve(r.data.Reservations[0].Instances[0]);
    }).on('error', e => {
        console.log('error in describeInstances', e.message);
    }).send();
});

var getVpc = (params) => new Promise((resolve) => {
    var aws = new AWS.EC2({ region: process.env.AWS_REGION }).describeVpcs(params);
    aws.on('success', r => {
        resolve(r.data.Vpcs[0])
    }).on('error', e => {
        console.log('error in describeVpcs', e.message);
    }).send();
});

var setTags = (instanceId, tags) => new Promise((resolve) => {
    var params = { Resources: [instanceId], Tags: tags };
    console.log('setting the following tags', params);
    var aws = new AWS.EC2({ region: process.env.AWS_REGION }).createTags(params);
    aws.on('success', r => {
        resolve(r.data);
    }).on('error', e => {
        console.log('error in setTags', e.message);
    }).send();
});

"x" in x.instanceId returns the following array and while I know that the getInstanceInfo portion is filtering on Instanceid, I want to return the Platform value from the output to be stored in the variable.


  2022-12-16T05:54:10.721Z  d8de6c17-3152-463c-8b84-69b30bd740f5    INFO    {
  InstanceIds: [
    {
      instanceId: 'i-02bfe033b04b2af74',
      imageId: 'ami-0e863061578d3e9fb',
      currentInstanceBootMode: 'bios',
      instanceState: [Object],
      privateDnsName: 'ip-10-100-11-217.us-west-2.compute.internal',
      amiLaunchIndex: 0,
      productCodes: {},
      instanceType: 't2.micro',
      launchTime: 1671170046000,
      placement: [Object],
      platform: 'windows',
      monitoring: [Object],
      subnetId: 'subnet-0b401fb2972ca8fda',
      vpcId: 'vpc-02854f577f73bb8de',
      privateIpAddress: '10.100.11.217',
      stateReason: [Object],
      architecture: 'x86_64',
      rootDeviceType: 'ebs',
      rootDeviceName: '/dev/sda1',
      blockDeviceMapping: {},
      virtualizationType: 'hvm',
      hypervisor: 'xen',
      tagSet: [Object],
      clientToken: '098b4f57-1a79-46a8-ac63-d0cb6d7fa618',
      groupSet: [Object],
      sourceDestCheck: true,
      networkInterfaceSet: [Object],
      ebsOptimized: false,
      enaSupport: true,
      cpuOptions: [Object],
      capacityReservationSpecification: [Object],
      enclaveOptions: [Object],
      metadataOptions: [Object],
      maintenanceOptions: [Object],
      privateDnsNameOptions: [Object]
    }
  ]
}

I've tried adding a return within the arrow function which I was sure wouldn't work.

var instances = await Promise.all(event.detail.responseElements.instancesSet.items.map(x => getInstanceInfo({ InstanceIds: [x.instanceId] }, x.platform )));

I've tried taking the instances and trying to pull only instances.platform which is undefined. (I expected that)

I know that I can use:

var instances = await Promise.all(event.detail.responseElements.instancesSet.items.map(x => getInstanceInfo({ InstanceIds: [x.platform] })));

and it returns the output "windows" but then the function fails because the filter is looking for InstanceId.

I want to filter on InstanceId to find the value of platform and store it to a variable.

Any ideas?

Sorry if this isn't formatted correctly or I didn't provide enough info in advance. While I use Stack Overflow a lot, I haven't posted any questions.


Solution

  • I was able to resolve this by creating a variation of the function that was being called. Simply by modifying the resolve section to get a specific object from the array.

    var InstanceIds = (params) => new Promise((resolve) => {
        console.log(params)
        var aws = new AWS.EC2({ region: process.env.AWS_REGION }).describeInstances(params);
        aws.on('success', r => {
            **resolve(r.data.Reservations[0].Instances[0].Platform)**;
        }).on('error', e => {
            console.log('error in describeInstances', e.message);
        }).send();
    });
    

    I thought this should work but for some reason initially I had trouble actually implementing it on my first go. Perhaps the array element was case-sensitive or something. Thanks for all of the help.