Skip to content

Commit

Permalink
Introduce a new helper command cleanup to delete unused resources c…
Browse files Browse the repository at this point in the history
…reated by aws-simple.

Currently, this command is used to find and delete REST-API CloudWatch roles that are no longer associated to a stack (see issue #169). In addition, the CDK is now required in version `^2.38.0`.
  • Loading branch information
clebert committed Dec 14, 2022
1 parent 3190deb commit 7c43379
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 17 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,19 @@ npx aws-simple start
Usage: aws-simple <command> [options]
Commands:
aws-simple synthesize [options] Synthesize the configured stack using the CDK.
[aliases: synth]
aws-simple synthesize [options] Synthesize the configured stack using the CDK. [aliases: synth]
aws-simple upload [options] Upload all referenced files to the S3 bucket of the configured stack.
aws-simple list [options] List all deployed stacks filtered by the specified hosted zone name.
aws-simple delete [options] Delete the specified stack.
aws-simple purge [options] Delete all expired stacks filtered by the specified hosted zone name.
aws-simple flush-cache [options] Flush the REST API cache of the specified stack.
aws-simple redeploy [options] Redeploy the REST API of the specified stack.
aws-simple cleanup [options] Deletes unused account-wide resources created by aws-simple.
aws-simple start [options] Start a local DEV server.
Options:
--version Show version number [boolean]
-h, --help Show help [boolean]
--version Show version number [boolean]
-h, --help Show help [boolean]
```

## Configuration
Expand Down
158 changes: 147 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"dependencies": {
"@aws-sdk/client-api-gateway": "^3.229.0",
"@aws-sdk/client-cloudformation": "^3.229.0",
"@aws-sdk/client-iam": "^3.229.0",
"@aws-sdk/client-s3": "^3.229.0",
"chalk": "^5.2.0",
"chokidar": "^3.5.3",
Expand Down Expand Up @@ -73,6 +74,6 @@
"typescript": "^4.9.4"
},
"peerDependencies": {
"aws-cdk-lib": "^2.24.0"
"aws-cdk-lib": "^2.38.0"
}
}
103 changes: 103 additions & 0 deletions src/cleanup-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import {APIGatewayClient, GetAccountCommand} from '@aws-sdk/client-api-gateway';
import type {CommandModule} from 'yargs';
import {deleteRole} from './sdk/delete-role.js';
import {findRoles} from './sdk/find-roles.js';
import {findStackResourceIds} from './sdk/find-stack-resource-ids.js';
import {findStacks} from './sdk/find-stacks.js';
import {print} from './utils/print.js';

const commandName = `cleanup`;

export const cleanupCommand: CommandModule<{}, {readonly yes: boolean}> = {
command: `${commandName} [options]`,
describe: `Deletes unused account-wide resources created by aws-simple.`,

builder: (argv) =>
argv
.options(`yes`, {
describe: `Confirm the deletion of unused account-wide resources`,
boolean: true,
default: false,
})
.example([[`npx $0 ${commandName}`], [`npx $0 ${commandName} --yes`]]),

handler: async (args): Promise<void> => {
print.info(`Searching unused account-wide resources...`);

const stacks = await findStacks();
const allResourceIds = new Set<string>();

for (const stack of stacks) {
if (stack.StackName) {
const resourceIds = await findStackResourceIds(stack.StackName);

for (const resourceId of resourceIds) {
allResourceIds.add(resourceId);
}
}
}

const client = new APIGatewayClient({});
const {cloudwatchRoleArn} = await client.send(new GetAccountCommand({}));

const roleNames = (await findRoles())
.filter(
(role) =>
role.RoleName?.startsWith(`aws-simple-`) &&
role.Arn?.includes(`RestApiCloudWatchRole`) &&
role.Arn !== cloudwatchRoleArn &&
!allResourceIds.has(role.RoleName),
)
.map((role) => role.RoleName!);

if (roleNames.length === 0) {
print.success(`No unused account-wide resources found.`);

return;
}

for (const roleName of roleNames) {
print.listItem(0, {
type: `entry`,
key: `REST-API CloudWatch role`,
value: roleName,
});
}

if (args.yes) {
print.warning(
`The found account-wide resources will be deleted automatically.`,
);
} else {
const confirmed = await print.confirmation(
`Confirm to delete the found account-wide resources.`,
);

if (!confirmed) {
return;
}
}

print.info(`Deleting the found account-wide resources...`);

const results = await Promise.allSettled(
roleNames.map(async (roleName) => deleteRole(roleName)),
);

const rejectedResults = results.filter(
(result): result is PromiseRejectedResult => result.status === `rejected`,
);

if (rejectedResults.length > 0) {
for (const {reason} of rejectedResults) {
print.error(String(reason));
}

process.exit(1);
} else {
print.success(
`The found account-wide resources have been successfully deleted.`,
);
}
},
};
Loading

0 comments on commit 7c43379

Please sign in to comment.