@infrascan/aws-ecs-scanner
Version:
Infrascan scanner definition for AWS ECS
831 lines (825 loc) • 24.7 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
default: () => index_default
});
module.exports = __toCommonJS(index_exports);
// src/generated/client.ts
var import_client_ecs = require("@aws-sdk/client-ecs");
var import_debug = __toESM(require("debug"));
var clientDebug = (0, import_debug.default)("ecs:client");
function getClient(credentials, context, retryStrategy) {
clientDebug("Creating instance with context", context);
return new import_client_ecs.ECSClient({ credentials, region: context.region, retryStrategy });
}
// src/generated/getters.ts
var import_core = require("@infrascan/core");
var import_client_ecs2 = require("@aws-sdk/client-ecs");
var import_debug2 = __toESM(require("debug"));
async function ListClusters(client, stateConnector, context) {
const getterDebug = (0, import_debug2.default)("ecs:ListClusters");
const state = [];
getterDebug("ListClusters");
const preparedParams = {};
try {
const cmd = new import_client_ecs2.ListClustersCommand(preparedParams);
const result = await client.send(cmd);
state.push({
_metadata: {
account: context.account,
region: context.region,
timestamp: Date.now()
},
_parameters: preparedParams,
_result: result
});
} catch (err) {
if (err instanceof import_client_ecs2.ECSServiceException) {
if (err?.$retryable) {
console.log("Encountered retryable error", err);
} else {
console.log("Encountered unretryable error", err);
}
} else {
console.log("Encountered unexpected error", err);
}
}
getterDebug("Recording state");
await stateConnector.onServiceScanCompleteCallback(
context.account,
context.region,
"ECS",
"ListClusters",
state
);
}
async function DescribeClusters(client, stateConnector, context) {
const getterDebug = (0, import_debug2.default)("ecs:DescribeClusters");
const state = [];
getterDebug("Fetching state");
const resolvers = [
{
Key: "clusters",
Selector: "ECS|ListClusters|[]._result.clusterArns | [?length(@)>`0`]"
},
{
Key: "include",
Value: [
"ATTACHMENTS",
"SETTINGS",
"CONFIGURATIONS",
"STATISTICS",
"TAGS"
]
}
];
const parameterQueue = await (0, import_core.resolveFunctionCallParameters)(
context.account,
context.region,
resolvers,
stateConnector
);
for (const parameters of parameterQueue) {
const preparedParams = parameters;
try {
const cmd = new import_client_ecs2.DescribeClustersCommand(preparedParams);
const result = await client.send(cmd);
state.push({
_metadata: {
account: context.account,
region: context.region,
timestamp: Date.now()
},
_parameters: preparedParams,
_result: result
});
} catch (err) {
if (err instanceof import_client_ecs2.ECSServiceException) {
if (err?.$retryable) {
console.log("Encountered retryable error", err);
} else {
console.log("Encountered unretryable error", err);
}
} else {
console.log("Encountered unexpected error", err);
}
}
}
getterDebug("Recording state");
await stateConnector.onServiceScanCompleteCallback(
context.account,
context.region,
"ECS",
"DescribeClusters",
state
);
}
async function ListServices(client, stateConnector, context) {
const getterDebug = (0, import_debug2.default)("ecs:ListServices");
const state = [];
getterDebug("Fetching state");
const resolvers = [
{ Key: "cluster", Selector: "ECS|ListClusters|[]._result.clusterArns[]" },
{ Key: "maxResults", Value: 100 }
];
const parameterQueue = await (0, import_core.resolveFunctionCallParameters)(
context.account,
context.region,
resolvers,
stateConnector
);
for (const parameters of parameterQueue) {
const preparedParams = parameters;
try {
const cmd = new import_client_ecs2.ListServicesCommand(preparedParams);
const result = await client.send(cmd);
state.push({
_metadata: {
account: context.account,
region: context.region,
timestamp: Date.now()
},
_parameters: preparedParams,
_result: result
});
} catch (err) {
if (err instanceof import_client_ecs2.ECSServiceException) {
if (err?.$retryable) {
console.log("Encountered retryable error", err);
} else {
console.log("Encountered unretryable error", err);
}
} else {
console.log("Encountered unexpected error", err);
}
}
}
getterDebug("Recording state");
await stateConnector.onServiceScanCompleteCallback(
context.account,
context.region,
"ECS",
"ListServices",
state
);
}
async function DescribeServices(client, stateConnector, context) {
const getterDebug = (0, import_debug2.default)("ecs:DescribeServices");
const state = [];
getterDebug("Fetching state");
const resolvers = [
{ Key: "cluster", Selector: "ECS|ListServices|[]._parameters.cluster" },
{
Key: "services",
Selector: "ECS|ListServices|[]._result.serviceArns | [?length(@)>`0`]"
},
{ Key: "include", Value: ["TAGS"] }
];
const parameterQueue = await (0, import_core.resolveFunctionCallParameters)(
context.account,
context.region,
resolvers,
stateConnector
);
for (const parameters of parameterQueue) {
const preparedParams = parameters;
try {
const cmd = new import_client_ecs2.DescribeServicesCommand(preparedParams);
const result = await client.send(cmd);
state.push({
_metadata: {
account: context.account,
region: context.region,
timestamp: Date.now()
},
_parameters: preparedParams,
_result: result
});
} catch (err) {
if (err instanceof import_client_ecs2.ECSServiceException) {
if (err?.$retryable) {
console.log("Encountered retryable error", err);
} else {
console.log("Encountered unretryable error", err);
}
} else {
console.log("Encountered unexpected error", err);
}
}
}
getterDebug("Recording state");
await stateConnector.onServiceScanCompleteCallback(
context.account,
context.region,
"ECS",
"DescribeServices",
state
);
}
async function ListTasks(client, stateConnector, context) {
const getterDebug = (0, import_debug2.default)("ecs:ListTasks");
const state = [];
getterDebug("Fetching state");
const resolvers = [
{ Key: "cluster", Selector: "ECS|ListClusters|[]._result.clusterArns[]" }
];
const parameterQueue = await (0, import_core.resolveFunctionCallParameters)(
context.account,
context.region,
resolvers,
stateConnector
);
for (const parameters of parameterQueue) {
const preparedParams = parameters;
try {
const cmd = new import_client_ecs2.ListTasksCommand(preparedParams);
const result = await client.send(cmd);
state.push({
_metadata: {
account: context.account,
region: context.region,
timestamp: Date.now()
},
_parameters: preparedParams,
_result: result
});
} catch (err) {
if (err instanceof import_client_ecs2.ECSServiceException) {
if (err?.$retryable) {
console.log("Encountered retryable error", err);
} else {
console.log("Encountered unretryable error", err);
}
} else {
console.log("Encountered unexpected error", err);
}
}
}
getterDebug("Recording state");
await stateConnector.onServiceScanCompleteCallback(
context.account,
context.region,
"ECS",
"ListTasks",
state
);
}
async function DescribeTasks(client, stateConnector, context) {
const getterDebug = (0, import_debug2.default)("ecs:DescribeTasks");
const state = [];
getterDebug("Fetching state");
const resolvers = [
{ Key: "cluster", Selector: "ECS|ListTasks|[]._parameters.cluster" },
{
Key: "tasks",
Selector: "ECS|ListTasks|[]._result.taskArns | [?length(@)>`0`]"
}
];
const parameterQueue = await (0, import_core.resolveFunctionCallParameters)(
context.account,
context.region,
resolvers,
stateConnector
);
for (const parameters of parameterQueue) {
const preparedParams = parameters;
try {
const cmd = new import_client_ecs2.DescribeTasksCommand(preparedParams);
const result = await client.send(cmd);
state.push({
_metadata: {
account: context.account,
region: context.region,
timestamp: Date.now()
},
_parameters: preparedParams,
_result: result
});
} catch (err) {
if (err instanceof import_client_ecs2.ECSServiceException) {
if (err?.$retryable) {
console.log("Encountered retryable error", err);
} else {
console.log("Encountered unretryable error", err);
}
} else {
console.log("Encountered unexpected error", err);
}
}
}
getterDebug("Recording state");
await stateConnector.onServiceScanCompleteCallback(
context.account,
context.region,
"ECS",
"DescribeTasks",
state
);
}
async function DescribeTaskDefinition(client, stateConnector, context) {
const getterDebug = (0, import_debug2.default)("ecs:DescribeTaskDefinition");
const state = [];
getterDebug("Fetching state");
const resolvers = [
{
Key: "taskDefinition",
Selector: "ECS|DescribeTasks|[]._result.tasks[].taskDefinitionArn"
},
{ Key: "include", Value: ["TAGS"] }
];
const parameterQueue = await (0, import_core.resolveFunctionCallParameters)(
context.account,
context.region,
resolvers,
stateConnector
);
for (const parameters of parameterQueue) {
const preparedParams = parameters;
try {
const cmd = new import_client_ecs2.DescribeTaskDefinitionCommand(preparedParams);
const result = await client.send(
cmd
);
state.push({
_metadata: {
account: context.account,
region: context.region,
timestamp: Date.now()
},
_parameters: preparedParams,
_result: result
});
} catch (err) {
if (err instanceof import_client_ecs2.ECSServiceException) {
if (err?.$retryable) {
console.log("Encountered retryable error", err);
} else {
console.log("Encountered unretryable error", err);
}
} else {
console.log("Encountered unexpected error", err);
}
}
}
getterDebug("Recording state");
await stateConnector.onServiceScanCompleteCallback(
context.account,
context.region,
"ECS",
"DescribeTaskDefinition",
state
);
}
async function getIamRoles(stateConnector) {
const iamDebug = (0, import_debug2.default)("ecs:iam");
iamDebug("Pulling IAM roles from state");
const state = [];
const DescribeTaskDefinitionRoleState = await (0, import_core.evaluateSelectorGlobally)(
"ECS|DescribeTaskDefinition|[]._result.taskDefinition | [].{roleArn:taskRoleArn,executor:taskDefinitionArn}",
stateConnector
);
state.push(...DescribeTaskDefinitionRoleState);
const DescribeTaskDefinitionRoleState1 = await (0, import_core.evaluateSelectorGlobally)(
"ECS|DescribeTaskDefinition|[]._result.taskDefinition | [].{roleArn:executionRoleArn,executor:taskDefinitionArn}",
stateConnector
);
state.push(...DescribeTaskDefinitionRoleState1);
return state;
}
// src/graph.ts
var import_core2 = require("@infrascan/core");
function mapKvPair(kvPair) {
return { key: kvPair.name, value: kvPair.value };
}
var ClusterEntity = {
version: "0.1.0",
debugLabel: "ecs-clusters",
provider: "aws",
command: "DescribeClusters",
category: "ecs",
subcategory: "clusters",
nodeType: "ecs-cluster",
selector: "ECS|DescribeClusters|[]",
getState(stateConnector, context) {
return (0, import_core2.evaluateSelector)(
context.account,
context.region,
ClusterEntity.selector,
stateConnector
);
},
translate(val) {
return (val._result.clusters ?? []).map(
(cluster) => Object.assign(cluster, {
$parameters: val._parameters,
$metadata: val._metadata
})
);
},
components: {
$metadata(val) {
return {
version: ClusterEntity.version,
timestamp: val.$metadata.timestamp
};
},
$graph(val) {
return {
id: val.clusterArn,
label: val.clusterName,
nodeType: ClusterEntity.nodeType,
parent: `${val.$metadata.account}-${val.$metadata.region}`
};
},
$source(val) {
return {
command: ClusterEntity.command,
parameters: val.$parameters
};
},
tenant(val) {
return {
tenantId: val.$metadata.account,
provider: ClusterEntity.provider,
partition: val.$metadata.partition
};
},
location(val) {
return {
code: val.$metadata.region
};
},
resource(val) {
return {
id: val.clusterArn,
name: val.clusterName,
category: ClusterEntity.category,
subcategory: ClusterEntity.subcategory
};
},
ecs(val) {
return {
cluster: {
executeCommandConfiguration: val.configuration?.executeCommandConfiguration,
managedStorageConfiguration: val.configuration?.managedStorageConfiguration,
registeredContainerInstancesCount: val.registeredContainerInstancesCount,
runningTasksCount: val.runningTasksCount,
pendingTasksCount: val.pendingTasksCount,
activeServicesCount: val.activeServicesCount,
statistics: val.statistics?.map(mapKvPair),
settings: val.settings?.map(mapKvPair),
capacityProviders: val.capacityProviders,
defaultCapacityProviderStrategy: val.defaultCapacityProviderStrategy,
attachments: val.attachments,
attachmentStatus: val.attachmentsStatus,
serviceConnectDefaults: val.serviceConnectDefaults,
status: val.status
}
};
},
tags(val) {
return val.tags;
}
}
};
var ServiceEntity = {
version: "0.1.0",
debugLabel: "ecs-services",
provider: "aws",
command: "DescribeServices",
category: "ecs",
subcategory: "services",
nodeType: "ecs-service",
selector: "ECS|DescribeServices|[]",
async getState(stateConnector, context) {
return (0, import_core2.evaluateSelector)(
context.account,
context.region,
ServiceEntity.selector,
stateConnector
);
},
translate(val) {
return (val._result.services ?? []).map(
(service) => Object.assign(service, {
$parameters: val._parameters,
$metadata: val._metadata
})
);
},
components: {
$graph(val) {
return {
id: val.serviceArn,
label: val.serviceName,
parent: val.clusterArn,
nodeType: ServiceEntity.nodeType
};
},
$metadata(val) {
return {
version: ServiceEntity.version,
timestamp: val.$metadata.timestamp
};
},
$source(val) {
return {
command: ServiceEntity.command,
parameters: val.$parameters
};
},
tenant(val) {
return {
tenantId: val.$metadata.account,
partition: val.$metadata.partition,
provider: ServiceEntity.provider
};
},
location(val) {
return {
code: val.$metadata.region
};
},
resource(val) {
return {
id: val.serviceArn,
name: val.serviceName,
category: ServiceEntity.category,
subcategory: ServiceEntity.subcategory
};
},
ecs(val) {
return {
platform: {
version: val.platformVersion,
family: val.platformFamily
},
service: {
serviceRegistries: val.serviceRegistries,
status: val.status,
launchType: val.launchType,
capacityProviderStrategy: val.capacityProviderStrategy,
taskDefinition: val.taskDefinition,
desiredCount: val.desiredCount,
runningCount: val.runningCount,
pendingCount: val.pendingCount,
placement: {
constraints: val.placementConstraints,
strategy: val.placementStrategy
},
schedulingStrategy: val.schedulingStrategy ? (0, import_core2.toLowerCase)(val.schedulingStrategy) : void 0
},
deployments: {
circuitBreaker: val.deploymentConfiguration?.deploymentCircuitBreaker,
alarms: val.deploymentConfiguration?.alarms,
rollout: {
minimumHealthyPct: val.deploymentConfiguration?.minimumHealthyPercent,
maximumHealthyPct: val.deploymentConfiguration?.maximumPercent
},
controller: val.deploymentController
},
loadBalancers: val.loadBalancers
};
},
network(val) {
const publicIpStatus = val.networkConfiguration?.awsvpcConfiguration?.assignPublicIp === "ENABLED" ? "enabled" : "disabled";
return {
publicIp: {
status: publicIpStatus
},
securityGroups: val.networkConfiguration?.awsvpcConfiguration?.securityGroups,
targetSubnets: val.networkConfiguration?.awsvpcConfiguration?.subnets
};
},
audit(val) {
return {
createdAt: val.createdAt,
createdBy: val.createdBy
};
},
iam(val) {
return {
roles: val.roleArn != null ? [{ label: "service-role", arn: val.roleArn }] : []
};
},
tags(val) {
return val.tags;
},
healthcheck(val) {
if (val.healthCheckGracePeriodSeconds == null) {
return void 0;
}
return {
gracePeriod: {
value: val.healthCheckGracePeriodSeconds,
unit: import_core2.Time.Seconds
}
};
}
}
};
var TaskEntity = {
version: "0.1.0",
debugLabel: "ecs-tasks",
provider: "aws",
command: "DescribeTasks",
category: "ecs",
subcategory: "tasks",
nodeType: "ecs-task",
selector: "ECS|DescribeTasks|[]",
getState(stateConnector, context) {
return (0, import_core2.evaluateSelector)(
context.account,
context.region,
TaskEntity.selector,
stateConnector
);
},
translate(val) {
return (val._result.tasks ?? []).map(
(task) => Object.assign(task, {
$parameters: val._parameters,
$metadata: val._metadata
})
);
},
components: {
$metadata(val) {
return {
version: TaskEntity.version,
timestamp: val.$metadata.timestamp
};
},
$graph(val) {
let parent = val.clusterArn;
if (val.group?.startsWith("service:") && val.clusterArn != null) {
const clusterName = val.clusterArn.slice(
val.clusterArn.lastIndexOf("/") + 1
);
const serviceName = val.group.slice(8);
parent = `arn:aws:ecs:${val.$metadata.region}:${val.$metadata.account}:service/${clusterName}/${serviceName}`;
}
const startOfTaskDefName = val.taskDefinitionArn?.lastIndexOf("/");
const label = startOfTaskDefName ? val.taskDefinitionArn?.slice(startOfTaskDefName + 1) : val.taskArn;
return {
id: val.taskDefinitionArn ?? val.taskArn,
label,
parent,
nodeType: TaskEntity.nodeType
};
},
$source(val) {
return {
command: TaskEntity.command,
parameters: val.$parameters
};
},
tenant(val) {
return {
tenantId: val.$metadata.account,
provider: TaskEntity.provider,
partition: val.$metadata.partition
};
},
location(val) {
return {
code: val.$metadata.region,
zone: val.availabilityZone ? [val.availabilityZone] : []
};
},
resource(val) {
const startOfTaskDefName = val.taskDefinitionArn?.lastIndexOf("/");
const label = startOfTaskDefName ? val.taskDefinitionArn?.slice(startOfTaskDefName + 1) : val.taskArn;
return {
id: val.taskDefinitionArn ?? val.taskArn,
name: label,
category: TaskEntity.category,
subcategory: TaskEntity.subcategory
};
},
ecs(val) {
return {
platform: {
version: val.platformVersion,
family: val.platformFamily
},
task: {
version: val.version,
attachments: val.attachments,
attributes: val.attributes,
capacityProviderName: val.capacityProviderName,
connectivity: val.connectivity,
connectivityAt: val.connectivityAt,
containerInstanceArn: val.containerInstanceArn,
containers: val.containers,
cpu: val.cpu,
desiredStatus: val.desiredStatus,
enableExecuteCommand: val.enableExecuteCommand,
executionStoppedAt: val.executionStoppedAt,
group: val.group,
healthStatus: val.healthStatus,
inferenceAccelerators: val.inferenceAccelerators,
lastStatus: val.lastStatus,
launchType: val.launchType,
memory: val.memory,
overrides: val.overrides,
pullStartedAt: val.pullStartedAt,
pullStoppedAt: val.pullStoppedAt,
startedAt: val.startedAt,
startedBy: val.startedBy,
stopCode: val.stopCode,
stoppedAt: val.stoppedAt,
stoppedReason: val.stoppedReason,
stoppingAt: val.stoppingAt,
tags: val.tags,
taskArn: val.taskArn,
taskDefinitionArn: val.taskDefinitionArn,
ephemeralStorage: val.ephemeralStorage,
fargateEphemeralStorage: val.fargateEphemeralStorage
}
};
},
audit(val) {
return {
createdAt: val.createdAt
};
},
tags(val) {
return val.tags;
},
healthcheck(val) {
return {
status: val.healthStatus
};
},
network(val) {
const eniAttachment = val.attachments?.find(
({ type }) => type === "ElasticNetworkInterface"
);
const eniDetails = Object.fromEntries(
eniAttachment?.details?.filter(
(entry) => entry.name != null && entry.value != null
)?.map(({ name, value }) => [name, value]) ?? []
);
return {
assignedAddresses: {
ipv4: eniDetails.privateIPv4Address
},
targetSubnets: eniDetails.subnetId ? [eniDetails.subnetId] : [],
attachedEni: {
interfaceId: eniDetails.networkInterfaceId,
macAddress: eniDetails.macAddress
}
};
}
}
};
var entities = [ClusterEntity, ServiceEntity, TaskEntity];
// src/index.ts
var ECSScanner = {
provider: "aws",
service: "ecs",
key: "ECS",
getClient,
callPerRegion: true,
getters: [
ListClusters,
DescribeClusters,
ListServices,
DescribeServices,
ListTasks,
DescribeTasks,
DescribeTaskDefinition
],
getIamRoles,
entities
};
var index_default = ECSScanner;