Skip to content
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

Expose Custom Resources Outputs to aws-exports.js and other resources #11113

Open
1 of 2 tasks
convexset opened this issue Oct 5, 2022 · 6 comments
Open
1 of 2 tasks
Labels
documentation Add or update documentation extensibility Issues related to expand or customize current configuration feature-request Request a new feature p3 platform Issues tied to the general CLI platform

Comments

@convexset
Copy link

convexset commented Oct 5, 2022

Is this feature request related to a new or existing Amplify category?

New category

Is this related to another service?

No response

Describe the feature you'd like to request

I'd like to reprise the request of #8886 but to caveat that it be without requiring the use of hooks.

With Amplify CLI Customers can define custom resources via CloudFormation & AWS CDK and these resources can reference the ones created by Amplify. At the moment though, interacting with these resources from the application requires Customers to pass the name/identifier of that resource somewhat manually.
Similarly to what already happens for resources created via Amplify, the CLI could add to the aws-exports.js file all the outputs of the custom resources stacks, so that the application can be aware of them.

Describe the solution you'd like

Refer to #8886 for an option, but here is part of what I have done:

The idea being that this could be easily rolled into amplify push.

My on-machine deployment flow includes:

    amplify env checkout $1
    amplify push --yes
    node post-publish.js # this does the copying of CFN outputs
    amplify publish --yes --invalidateCloudFront

Here is an abbreviated version, which looks like command-hooks, but is not it:

const fs = require('fs');

const AWS_EXPORTS_PATH = './src/aws-exports.js';
const TEAM_PROVIDER_INFO_PATH = './amplify/team-provider-info.json';
const AMPLIFY_META_PATH = './amplify/backend/amplify-meta.json';

/**
 * @param data { { amplify: { environment: { envName: string, projectPath: string, defaultEditor: string }, command: string, subCommand: string, argv: string[] } } }
 * @param error { { message: string, stack: string } }
 */
const hookHandler = async function handler(data) {
    console.log();
    console.log(`EXPOSING CUSTOM RESOURCE OUTPUTS in ${AWS_EXPORTS_PATH}`);
    console.log();
    console.log(`env: ${data.env}`)
    console.log();

    const customResourceNames = Object.keys(data.teamProviderInfo.categories.custom)
    if (customResourceNames.length > 0) {
        data.awsExports.aws_custom_resources = {}
        console.log(`Adding Stack Outputs for Custom Resources to ${AWS_EXPORTS_PATH}:`)
        customResourceNames.forEach(name => {
            console.log(` - ${name}`)
            data.awsExports.aws_custom_resources[name] = data.amplifyMeta.custom[name].output;
        });
        console.log();
    }

    console.log(`Writing ${AWS_EXPORTS_PATH}...`);
    const newAwsExports = [
        '/* eslint-disable */',
        '// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.',
        '// Regenerated by post-publish.js',
        '',
        '',
        `const awsmobile = ${JSON.stringify(data.awsExports, null, 4)};`,
        '',
        '',
        'export default awsmobile;',
        ''
    ].join('\n');
    fs.writeFileSync(AWS_EXPORTS_PATH, newAwsExports)
};

const getParameters = async () => {
    const env = process.env.AMPLIFY_PROJECT_ENV;

    const teamProviderInfo = JSON.parse(fs.readFileSync(TEAM_PROVIDER_INFO_PATH, {
        encoding: 'utf8'
    }));
    const amplifyMeta = JSON.parse(fs.readFileSync(AMPLIFY_META_PATH, {
        encoding: 'utf8'
    }));

    const awsExportsRaw = fs.readFileSync(AWS_EXPORTS_PATH, {
        encoding: 'utf8'
    });
    const awsExportsRawParts = awsExportsRaw.split('\n')
    const awsExportsCleaned = awsExportsRawParts.slice(
        awsExportsRawParts.indexOf('const awsmobile = {') + 1,
        awsExportsRawParts.indexOf('};')
    )
    awsExportsCleaned.unshift('{');
    awsExportsCleaned.push('}');
    const awsExports = JSON.parse(awsExportsCleaned.join('\n'))

    return {
        env,
        awsExports,
        amplifyMeta,
        teamProviderInfo: teamProviderInfo[env],
    }
};

getParameters()
    .then(data => hookHandler(data))
    .catch((err) => {
        console.error(err);
        process.exitCode = 1;
    });

What it adds is the CFN outputs to aws-exports.js, with the end of the file now looking like:

...

    "aws_content_delivery_bucket_region": "rr-lllllll-1",
    "aws_content_delivery_url": "https://abcdefg123.cloudfront.net",
    "aws_custom_resources": {
        "cfnResources": {
            "env": "dev",
            "SomeOverPermissiveFunctionRoleARN": "arn:aws:iam::123456789:role/amplify-abc-dev-12345-SomeOverPermissiveFuncti-9A9999AAAA99A"
        },
        "moreCfnResources": {
            "env": "dev",
            "SomeOverPermissiveFunctionRoleARN": "arn:aws:iam::123456789:role/amplify-abc-dev-12345-SomeOverPermissiveFuncti-8B8888BBBB88B"
        }
    }
};


export default awsmobile;

... which comes from a two of the same template that each containing just a Lambda role with overly broad privileges.

Describe alternatives you've considered

The above was my alternative, because command-hooks don't work in a console environment.

Additional context

See #8886

Is this something that you'd be interested in working on?

  • 👋 I may be able to implement this feature request

Would this feature include a breaking change?

  • ⚠️ This feature might incur a breaking change
@convexset
Copy link
Author

Caveat: I may be willing to implement this, but a pointer to the general area where this is implemented in the repository would help save a lot of time.

@josefaidt josefaidt added platform Issues tied to the general CLI platform pending-triage Issue is pending triage labels Oct 5, 2022
@josefaidt
Copy link
Contributor

Hey @convexset 👋 thanks for raising this! This feels similar to #9087, and I think it would be great to co-opt this issue as a feature request for general exposure of custom resource outputs, both to the aws-exports.js file and to other resources

@josefaidt josefaidt added feature-request Request a new feature extensibility Issues related to expand or customize current configuration and removed pending-triage Issue is pending triage labels Oct 5, 2022
@josefaidt josefaidt changed the title Revisiting "Expose Custom Resources Outputs into aws-exports.js #8886" without Hooks Expose Custom Resources Outputs to aws-exports.js and other resources Oct 5, 2022
@convexset
Copy link
Author

Related certainly, but quite different. I’ve not actually solved that other problem in a manner that is as clean as this. (Record as a JSON field in a DDB configuration table that is read once on Lambda instantiation or when needed.)

@convexset
Copy link
Author

… a full solution would probably have a config file for determining which specific outputs can be exposed in aws-exports.js. Exposure of every output for Lambdas should be alright.

@josefaidt josefaidt added p3 documentation Add or update documentation labels Oct 10, 2022
@convexset
Copy link
Author

convexset commented Oct 11, 2022

Another thing I do is to link this to an AWS Chalice API:

const CHALICE_PATH = '../backend';
    console.log();
    console.log(`EXPOSING CHALICE OUTPUTS in ${AWS_EXPORTS_PATH}`);
    console.log(` - env: ${data.env}`)
    try {
        const chaliceDeploymentsPath = `${CHALICE_PATH}/.chalice/deployed`;
        const files = fs.readdirSync(chaliceDeploymentsPath);
        const filename = `${data.env}.json`;
        if (!files.includes(filename)) {
            throw new Error('deployment-record-not-found');
        }

        const chaliceDeploymentData = JSON.parse(fs.readFileSync(`${chaliceDeploymentsPath}/${filename}`, {
            encoding: 'utf8'
        }));

        const chaliceAPIResources = chaliceDeploymentData.resources.filter(r => r.resource_type.endsWith('_api'))
        if (chaliceAPIResources.length === 0) {
            throw new Error('no-api-resources-available');
        }
        data.awsExports.aws_chalice_api_resources = {}
        chaliceAPIResources.forEach(r => {
            console.log(` - ${r.resource_type}`)
            data.awsExports.aws_chalice_api_resources[r.resource_type] = r;
        })
    } catch (e) {
        console.log(` - Unable to expose Chalice API outputs (${e.message})`)
    }
    console.log();

Which adds a section like this to aws-exports.js:

{
    ...
    "aws_chalice_api_resources": {
        "rest_api": {
            "name": "rest_api",
            "resource_type": "rest_api",
            "rest_api_id": "api123",
            "rest_api_url": "https://api123.execute-api.ap-southeast-1.amazonaws.com/api-dev/"
        },
        "websocket_api": {
            "name": "websocket_api",
            "resource_type": "websocket_api",
            "websocket_api_url": "wss://api789.execute-api.ap-southeast-1.amazonaws.com/api-dev/",
            "websocket_api_id": "api789"
        }
    }
}

@jay-herrera
Copy link

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Add or update documentation extensibility Issues related to expand or customize current configuration feature-request Request a new feature p3 platform Issues tied to the general CLI platform
Projects
None yet
Development

No branches or pull requests

3 participants