aws-amplify / aws-amplify/amplify-cli
Expose Custom Resources Outputs to aws-exports.js and other resources
- Dominant language
- TypeScript
- Stars
- 2.9k
- Forks
- 825
- Avg merge
- 11d 23h
- Merged PRs (30d)
- 2
Description
### 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 https://github.com/aws-amplify/amplify-cli/issues/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 https://github.com/aws-amplify/amplify-cli/issues/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`](https://docs.amplify.aws/cli/usage/command-hooks), but is not it:
```js
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:
```js
...
"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`](https://docs.amplify.aws/cli/usage/command-hooks) don't work in a console environment.
### Additional context
See https://github.com/aws-amplify/amplify-cli/issues/8886
### Is this something that you'd be interested in working on?
- [X] 👋 I may be able to implement this feature request
### Would this feature include a breaking change?
- [ ] ⚠️ This feature might incur a breaking change
Contributor guide
Research direction
Start by reviewing issue #8886, the command-hooks documentation, and the mentioned amplify push flow. Trace how amplify-meta.json, team-provider-info.json, and aws-exports.js are generated, then define completion as custom resource stack outputs being available after amplify push without requiring hooks.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, javascript, typescript
- Domain
- cli, cloud, infrastructure
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100