Click-ops doesn't scale
Building infrastructure by clicking in the console can't be reviewed, repeated or undone. With infrastructure as code (CloudFormation, CDK, Terraform) you describe resources in a template. The tool works out the order — a function needs its role and table first — and before changing anything shows a plan of what it will create, update and delete.
The deployment engine
function dependencies(resource) {
const found = new Set([].concat(resource.DependsOn ?? []));
JSON.stringify(resource.Properties ?? {}, (key, value) => {
if (value && typeof value === "object" && typeof value.Ref === "string") found.add(value.Ref); // { Ref: "Table" }
return value;
});
return [...found];
}
function deployOrder(template) {
const resources = template.Resources;
const order = [];
const state = {};
function visit(name, path) {
if (!resources[name]) throw new Error(`Unknown resource ${name}`);
if (state[name] === "done") return;
if (state[name] === "visiting") throw new Error(`Cycle: ${[...path, name].join(" -> ")}`);
state[name] = "visiting";
for (const dep of dependencies(resources[name])) visit(dep, [...path, name]);
state[name] = "done";
order.push(name); // after everything it depends on
}
for (const name of Object.keys(resources)) visit(name, []);
return order;
}
function plan(current, desired) {
const before = current.Resources;
const after = desired.Resources;
const order = deployOrder(desired);
return {
create: order.filter((n) => !(n in before)),
update: order.filter((n) => n in before && JSON.stringify(before[n]) !== JSON.stringify(after[n])),
delete: deployOrder(current).filter((n) => !(n in after)).reverse(), // dependents go first
};
}