multiple accounts

This commit is contained in:
Peter Howell 2025-10-08 22:20:31 +00:00
parent d4e143e06c
commit a3093e1c8b
4 changed files with 1622 additions and 1138 deletions

View File

@ -149,6 +149,16 @@ const cy = cytoscape({
'text-halign': 'left', 'text-halign': 'left',
'text-opacity': 0 /* hide built-in labels; we render HTML labels */ 'text-opacity': 0 /* hide built-in labels; we render HTML labels */
}}, }},
{ selector: 'node[type = "account"]', style: {
'background-color': '#0b1220',
'background-opacity': 0.45,
'border-color': 'data(color)',
'border-width': 2,
'padding': '90px',
'shape': 'round-rectangle',
'text-opacity': 0,
'z-compound-depth': 'bottom'
}},
{ selector: 'node[lx]', style: { 'text-margin-x': 'data(lx)' }}, { selector: 'node[lx]', style: { 'text-margin-x': 'data(lx)' }},
{ selector: 'node[ly]', style: { 'text-margin-y': 'data(ly)' }}, { selector: 'node[ly]', style: { 'text-margin-y': 'data(ly)' }},
{ selector: 'node[label]', style: { 'label': 'data(label)' }}, { selector: 'node[label]', style: { 'label': 'data(label)' }},
@ -234,6 +244,38 @@ async function loadGraphAndBuildElements(region = 'us-west-1') {
const g = (graph.regions && graph.regions[region]) || {}; const g = (graph.regions && graph.regions[region]) || {};
const elements = []; const elements = [];
const accountPalette = ['#7c3aed','#0ea5e9','#f97316','#10b981','#facc15','#f472b6'];
const accountMeta = new Map();
function canonicalAccountId(value, fallbackIndex){
const raw = (value ?? '').toString().trim();
return raw ? raw.replace(/\s+/g,'-') : `account-${fallbackIndex}`;
}
function pickAccountColor(index){
return accountPalette[index % accountPalette.length];
}
function registerAccount(rawId, label, color, alias){
const info = { id: rawId, label, color, nodeId: `account:${rawId}`, alias: alias || null };
accountMeta.set(rawId, info);
elements.push({ data: { id: info.nodeId, type:'account', label, color, rawId, alias: info.alias } });
return info;
}
// Accounts are optional; gather them from the top-level array and from any
// `accountId` fields present on VPC records so multiple AWS accounts can share one canvas.
const accounts = Array.isArray(graph.accounts) ? graph.accounts : [];
accounts.forEach((acct, index) => {
const rawId = canonicalAccountId(acct.id || acct.accountId || acct.alias, index + 1);
const color = acct.color || acct.colour || acct.accent || pickAccountColor(index);
const label = acct.label || acct.name || rawId;
const alias = acct.alias || acct.accountAlias || null;
registerAccount(rawId, label, color, alias);
});
function ensureAccount(raw){
if (raw == null) return null;
const canonical = canonicalAccountId(raw, accountMeta.size + 1);
if (accountMeta.has(canonical)) return accountMeta.get(canonical);
return registerAccount(canonical, canonical, pickAccountColor(accountMeta.size), null);
}
const vpcs = g.vpcs || []; const vpcs = g.vpcs || [];
const subnets = g.subnets || []; const subnets = g.subnets || [];
const sgs = g.sgs || []; const sgs = g.sgs || [];
@ -241,22 +283,34 @@ async function loadGraphAndBuildElements(region = 'us-west-1') {
const rds = g.rds || []; const rds = g.rds || [];
const lbs = g.lbs || []; const lbs = g.lbs || [];
const vpcAccountMeta = new Map();
const subnetAccountMeta = new Map();
const sgById = Object.fromEntries(sgs.map(s => [s.id, s])); const sgById = Object.fromEntries(sgs.map(s => [s.id, s]));
// VPCs // VPCs
for (const v of vpcs) { for (const v of vpcs) {
elements.push({ data: { id: v.id, type: 'vpc', label: `${v.name}\n${v.cidr}`, cidr: v.cidr, name: v.name } }); const acctInfo = ensureAccount(v.accountId || v.account || v.account_id || null);
if (acctInfo) vpcAccountMeta.set(v.id, acctInfo);
const parent = acctInfo ? acctInfo.nodeId : undefined;
elements.push({ data: { id: v.id, type: 'vpc', label: `${v.name}\n${v.cidr}`, cidr: v.cidr, name: v.name, accountId: acctInfo ? acctInfo.id : null, accountLabel: acctInfo ? acctInfo.label : null, parent } });
} }
// Subnets // Subnets
for (const s of subnets) { for (const s of subnets) {
const label = `${(s.name||s.id)}\n${s.cidr}\n${s.az}`; const label = `${(s.name||s.id)}\n${s.cidr}\n${s.az}`;
elements.push({ data: { id: s.id, type: 'subnet', label, cidr: s.cidr, az: s.az, public: !!s.public, parent: s.vpc } }); const fallbackAcct = vpcAccountMeta.get(s.vpc || '') || null;
const acctInfo = ensureAccount(s.accountId || (fallbackAcct && fallbackAcct.id) || null) || fallbackAcct;
if (acctInfo) subnetAccountMeta.set(s.id, acctInfo);
elements.push({ data: { id: s.id, type: 'subnet', label, cidr: s.cidr, az: s.az, public: !!s.public, parent: s.vpc, accountId: acctInfo ? acctInfo.id : null } });
} }
// SGs // SGs
for (const sg of sgs) { for (const sg of sgs) {
const rin = sg.rules_in || [], rout = sg.rules_out || []; const rin = sg.rules_in || [], rout = sg.rules_out || [];
const label = `${sg.name || sg.id}\ningress: ${(rin[0]||'—')}${rin.length>1?'…':''}`; const label = `${sg.name || sg.id}\ningress: ${(rin[0]||'—')}${rin.length>1?'…':''}`;
elements.push({ data: { id: sg.id, type: 'sg', label, rules: { in: rin, out: rout } } }); const fallbackAcct = vpcAccountMeta.get(sg.vpc || '') || null;
const acctInfo = ensureAccount(sg.accountId || (fallbackAcct && fallbackAcct.id) || null) || fallbackAcct;
const parent = acctInfo ? acctInfo.nodeId : undefined;
elements.push({ data: { id: sg.id, type: 'sg', label, rules: { in: rin, out: rout }, parent, accountId: acctInfo ? acctInfo.id : null, vpc: sg.vpc || null } });
} }
// SG<->SG edges based on rules referencing other SGs // SG<->SG edges based on rules referencing other SGs
function parseSgRef(rule){ function parseSgRef(rule){
@ -287,16 +341,20 @@ async function loadGraphAndBuildElements(region = 'us-west-1') {
// EC2 // EC2
for (const i of ec2s) { for (const i of ec2s) {
const label = `${i.name || i.id}\n${i.id}\n${i.type || ''}\n${i.privateIp ? ('Private IP: ' + i.privateIp) : ''}`; const label = `${i.name || i.id}\n${i.id}\n${i.type || ''}\n${i.privateIp ? ('Private IP: ' + i.privateIp) : ''}`;
const fallbackAcct = subnetAccountMeta.get(i.subnet || '') || vpcAccountMeta.get(i.vpc || '') || null;
const acctInfo = ensureAccount(i.accountId || (fallbackAcct && fallbackAcct.id) || null) || fallbackAcct;
elements.push({ data: { id: i.id, type: 'ec2', label, elements.push({ data: { id: i.id, type: 'ec2', label,
name: i.name, instanceId: i.id, privateIp: i.privateIp, name: i.name, instanceId: i.id, privateIp: i.privateIp,
publicIp: i.publicIp, state: i.state, parent: i.subnet, sgs: i.sgs || [] } }); publicIp: i.publicIp, state: i.state, parent: i.subnet, sgs: i.sgs || [], accountId: acctInfo ? acctInfo.id : null, vpc: i.vpc || null } });
for (const sgid of (i.sgs || [])) if (sgById[sgid]) elements.push({ data: { id:`sg-${sgid}->${i.id}`, source: sgid, target: i.id, label:'attached', class:'sg-attach' }}); for (const sgid of (i.sgs || [])) if (sgById[sgid]) elements.push({ data: { id:`sg-${sgid}->${i.id}`, source: sgid, target: i.id, label:'attached', class:'sg-attach' }});
} }
// RDS // RDS
for (const db of rds) { for (const db of rds) {
const parentSubnet = (db.subnetGroup && db.subnetGroup[0]) || null; const parentSubnet = (db.subnetGroup && db.subnetGroup[0]) || null;
const label = `${db.engine}\n${db.port}\n${db.publiclyAccessible?'public':'private'}`; const label = `${db.engine}\n${db.port}\n${db.publiclyAccessible?'public':'private'}`;
elements.push({ data: { id: db.id, type:'rds', label, engine:db.engine, port:db.port, publiclyAccessible:!!db.publiclyAccessible, parent: parentSubnet, sgs: db.sgs || [] }}); const fallbackAcct = subnetAccountMeta.get(parentSubnet || '') || null;
const acctInfo = ensureAccount(db.accountId || (fallbackAcct && fallbackAcct.id) || null) || fallbackAcct;
elements.push({ data: { id: db.id, type:'rds', label, engine:db.engine, port:db.port, publiclyAccessible:!!db.publiclyAccessible, parent: parentSubnet, sgs: db.sgs || [], accountId: acctInfo ? acctInfo.id : null } });
for (const sgid of (db.sgs || [])) if (sgById[sgid]) elements.push({ data: { id:`sg-${sgid}->${db.id}`, source: sgid, target: db.id, label:'attached', class:'sg-attach' }}); for (const sgid of (db.sgs || [])) if (sgById[sgid]) elements.push({ data: { id:`sg-${sgid}->${db.id}`, source: sgid, target: db.id, label:'attached', class:'sg-attach' }});
} }
// LBs // LBs
@ -304,7 +362,9 @@ async function loadGraphAndBuildElements(region = 'us-west-1') {
const parent = (lb.subnets && lb.subnets[0]) || (subnets[0] && subnets[0].id) || null; const parent = (lb.subnets && lb.subnets[0]) || (subnets[0] && subnets[0].id) || null;
const lstText = (lb.listeners || []).map(L => L.port).join(','); const lstText = (lb.listeners || []).map(L => L.port).join(',');
const label = `${lb.id}\nlisteners: ${lstText || '—'}\n${lb.scheme}`; const label = `${lb.id}\nlisteners: ${lstText || '—'}\n${lb.scheme}`;
elements.push({ data: { id: lb.id, type: (lb.type==='network'?'nlb':'alb'), label, scheme: lb.scheme, listeners: lb.listeners || [], parent, sgs: lb.securityGroups || [], dns: lb.dns } }); const fallbackAcct = subnetAccountMeta.get(parent || '') || null;
const acctInfo = ensureAccount(lb.accountId || (fallbackAcct && fallbackAcct.id) || null) || fallbackAcct;
elements.push({ data: { id: lb.id, type: (lb.type==='network'?'nlb':'alb'), label, scheme: lb.scheme, listeners: lb.listeners || [], parent, sgs: lb.securityGroups || [], dns: lb.dns, accountId: acctInfo ? acctInfo.id : null } });
for (const sgid of (lb.securityGroups || [])) if (sgById[sgid]) elements.push({ data: { id:`sg-${sgid}->${lb.id}`, source: sgid, target: lb.id, label:'attached', class:'sg-attach' }}); for (const sgid of (lb.securityGroups || [])) if (sgById[sgid]) elements.push({ data: { id:`sg-${sgid}->${lb.id}`, source: sgid, target: lb.id, label:'attached', class:'sg-attach' }});
const tgs = lb.targetGroups || []; const targetToPort = {}; const tgs = lb.targetGroups || []; const targetToPort = {};
for (const tg of tgs) for (const t of (tg.targets || [])) targetToPort[t] = tg.port || targetToPort[t] || ''; for (const tg of tgs) for (const t of (tg.targets || [])) targetToPort[t] = tg.port || targetToPort[t] || '';
@ -335,17 +395,41 @@ async function loadGraphAndBuildElements(region = 'us-west-1') {
default: return '<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="5" y="5" width="14" height="14" rx="2" stroke-width="1.5"/></svg>'; default: return '<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="5" y="5" width="14" height="14" rx="2" stroke-width="1.5"/></svg>';
} }
} }
function vpnBadge(name){ return (name && /vpn/i.test(name)) ? '<span class="badge">VPN</span>' : ''; } function vpnBadge(name){ return (name && /vpn/i.test(name)) ? '<span class="badge type-badge type-vpn">VPN</span>' : ''; }
function typeBadge(type){
switch(type){
case 'nat': return '<span class="badge type-badge type-nat">NAT</span>';
case 'igw': return '<span class="badge type-badge type-igw">IGW</span>';
case 'endpoint':
case 'vpce':
case 'gateway-endpoint':
return '<span class="badge type-badge type-endpoint">VPCE</span>';
case 'sg': return '<span class="badge type-badge">SG</span>';
default: return '';
}
}
function statusDot(state){ const cls=(state==='running')?'run':(state==='stopped'?'stop':'unk'); const txt=state||'unknown'; return `<span class="status"><i class="dot ${cls}"></i>${escapeHtml(txt)}</span>`; } function statusDot(state){ const cls=(state==='running')?'run':(state==='stopped'?'stop':'unk'); const txt=state||'unknown'; return `<span class="status"><i class="dot ${cls}"></i>${escapeHtml(txt)}</span>`; }
function sgRuleCount(d){ const rin=(d.rules&&d.rules.in)?d.rules.in.length:0; const rout=(d.rules&&d.rules.out)?d.rules.out.length:0; return rin+rout; } function sgRuleCount(d){ const rin=(d.rules&&d.rules.in)?d.rules.in.length:0; const rout=(d.rules&&d.rules.out)?d.rules.out.length:0; return rin+rout; }
// Attach HTML labels for SG/ALB/NLB/RDS/EC2 centered inside nodes and Subnet at top-left // Attach HTML labels for SG/ALB/NLB/RDS/EC2 centered inside nodes and Subnet at top-left
if (cy.nodeHtmlLabel) { if (cy.nodeHtmlLabel) {
cy.nodeHtmlLabel([ cy.nodeHtmlLabel([
{ {
query:'node[type = "sg"], node[type = "alb"], node[type = "nlb"], node[type = "rds"], node[type = "ec2"]', query:'node[type = "account"]',
halign:'center', valign:'center', halignBox:'center', valignBox:'center', halign:'left', valign:'top', halignBox:'left', valignBox:'top',
tpl: function(d){ tpl: function(d){
const accent=d.color||'#38bdf8';
const name=d.label||(d.rawId||d.id||'account');
return `<div class="account-card" style="--accent:${escapeHtml(accent)}">
<span class="account-dot"></span>
<span class="account-name">${escapeHtml(name)}</span>
</div>`;
}
},
{
query:'node[type = "sg"], node[type = "alb"], node[type = "nlb"], node[type = "rds"], node[type = "ec2"]',
halign:'center', valign:'center', halignBox:'center', valignBox:'center',
tpl: function(d){
const type=d.type; const name=d.name||(d.label||'').split('\n')[0]||d.id; const type=d.type; const name=d.name||(d.label||'').split('\n')[0]||d.id;
const pub=d.publicIp||d.publicIpAddress||d.public_ip||d.dns||''; const pub=d.publicIp||d.publicIpAddress||d.public_ip||d.dns||'';
const priv=d.privateIp||d.private_ip||''; const priv=d.privateIp||d.private_ip||'';
@ -361,17 +445,78 @@ async function loadGraphAndBuildElements(region = 'us-west-1') {
if (priv) lines.push(`<div class="n-sub">Private IP: ${escapeHtml(priv)}</div>`); if (priv) lines.push(`<div class="n-sub">Private IP: ${escapeHtml(priv)}</div>`);
if (pub) lines.push(`<div class="n-sub">Public: ${escapeHtml(pub)}</div>`); if (pub) lines.push(`<div class="n-sub">Public: ${escapeHtml(pub)}</div>`);
} }
const badge=vpnBadge(name); const badges=[];
const vpnChip=vpnBadge(name); if (vpnChip) badges.push(vpnChip);
const typeChip=typeBadge(type); if (typeChip) badges.push(typeChip);
const badgeHtml=badges.join('');
return `<div class="ncard n-${type}" style="background:none;border:none;box-shadow:none;padding:0;margin:0;display:block;"> return `<div class="ncard n-${type}" style="background:none;border:none;box-shadow:none;padding:0;margin:0;display:block;">
<div class="n-title">${escapeHtml(name)}${badge}</div> <div class="n-title" style="display:flex;align-items:center;gap:8px;">${iconSvg(type)}${escapeHtml(name)}${badgeHtml}</div>
${lines.join('')} ${lines.join('')}
</div>`; </div>`;
} }
}, },
{ {
query:'node[type = "subnet"]', query:'node[type = "alb"], node[type = "nlb"]',
halign:'left', valign:'top', halignBox:'left', valignBox:'top', halign:'center', valign:'center', halignBox:'center', valignBox:'center',
tpl: function(d){ tpl: function(d){
const icon = iconSvg(d.type === 'nlb' ? 'nlb' : 'alb');
const name = (d.label || '').split('\n')[0] || d.id;
const dns = d.dns ? `<div class="n-sub">${escapeHtml(d.dns)}</div>` : '';
const listeners = Array.isArray(d.listeners) ? d.listeners.map(L => L.port).join(', ') : '';
const listenerLine = listeners ? `<div class="n-sub">listeners: ${escapeHtml(listeners)}</div>` : '';
const badges=[];
const typeChip=typeBadge(d.type);
if (typeChip) badges.push(typeChip);
const badgeHtml=badges.join('');
return `<div class="ncard n-${d.type}" style="background:#fee2e2;border:1px solid #ef4444;box-shadow:none;padding:10px 12px;border-radius:10px;">
<div class="n-title" style="display:flex;align-items:center;gap:8px;">
${icon}
${escapeHtml(name)}${badgeHtml}
</div>
${dns}
${listenerLine}
</div>`;
}
},
{
query:'node[type = "sg"]',
halign:'center', valign:'center', halignBox:'center', valignBox:'center',
tpl: function(d){
const rules = sgRuleCount(d);
const badges=[];
const typeChip=typeBadge('sg'); if (typeChip) badges.push(typeChip);
const badgeHtml=badges.join('');
return `<div class="ncard n-sg" style="background:#fff7ed;border:1px solid #f59e0b;box-shadow:none;padding:10px 12px;border-radius:10px;">
<div class="n-title" style="display:flex;align-items:center;gap:8px;">
${iconSvg('sg')}
${escapeHtml(d.name || d.label || d.id)}${badgeHtml}
</div>
<div class="n-sub">${escapeHtml(`${rules} rule${rules===1?'':'s'}`)}</div>
</div>`;
}
},
{
query:'node[type = "nat"], node[type = "igw"], node[type = "vpn"]',
halign:'center', valign:'center', halignBox:'center', valignBox:'center',
tpl: function(d){
const name=d.name||(d.label||'').split('\n')[0]||d.id;
const icon = iconSvg(d.type);
const badges=[];
const vpnChip=vpnBadge(name); if (vpnChip) badges.push(vpnChip);
const typeChip=typeBadge(d.type); if (typeChip) badges.push(typeChip);
const badgeHtml=badges.join('');
return `<div class="ncard n-${d.type}" style="background:#ffe4e6;border:1px solid #fb7185;box-shadow:none;padding:8px 10px;border-radius:10px;">
<div class="n-title" style="display:flex;align-items:center;gap:8px;">
${icon}
${escapeHtml(name)}${badgeHtml}
</div>
</div>`;
}
},
{
query:'node[type = "subnet"]',
halign:'left', valign:'top', halignBox:'left', valignBox:'top',
tpl: function(d){
const name=(d.name||(d.label||'').split('\n')[0]||d.id); const name=(d.name||(d.label||'').split('\n')[0]||d.id);
const cidr=d.cidr||''; const cidr=d.cidr||'';
const az=d.az||''; const az=d.az||'';
@ -496,12 +641,13 @@ async function loadGraphAndBuildElements(region = 'us-west-1') {
if(!ele){ $details.textContent=''; return; } if(!ele){ $details.textContent=''; return; }
const d=ele.data(); const base={ id:d.id, type:d.type, label:d.label }; let extra={}; const d=ele.data(); const base={ id:d.id, type:d.type, label:d.label }; let extra={};
switch(d.type){ switch(d.type){
case 'vpc': extra={ cidr:d.cidr, name:d.name }; break; case 'vpc': extra={ cidr:d.cidr, name:d.name, accountId:d.accountId, accountLabel:d.accountLabel }; break;
case 'account': extra={ label:d.label, color:d.color, rawId:d.rawId, alias:d.alias }; break;
case 'subnet': extra={ cidr:d.cidr, az:d.az, public:d.public }; break; case 'subnet': extra={ cidr:d.cidr, az:d.az, public:d.public }; break;
case 'alb': extra={ scheme:d.scheme, dns:d.dns, listeners:d.listeners, sgs:d.sgs }; break; case 'alb': extra={ scheme:d.scheme, dns:d.dns, listeners:d.listeners, sgs:d.sgs, accountId:d.accountId }; break;
case 'ec2': extra={ name:d.name, instanceId:d.instanceId, privateIp:d.privateIp, publicIp:d.publicIp, state:d.state, sgs:d.sgs }; break; case 'ec2': extra={ name:d.name, instanceId:d.instanceId, privateIp:d.privateIp, publicIp:d.publicIp, state:d.state, sgs:d.sgs, accountId:d.accountId }; break;
case 'rds': extra={ engine:d.engine, port:d.port, publiclyAccessible:d.publiclyAccessible, sgs:d.sgs }; break; case 'rds': extra={ engine:d.engine, port:d.port, publiclyAccessible:d.publiclyAccessible, sgs:d.sgs, accountId:d.accountId }; break;
case 'sg': extra={ inbound_sorted: sortRules(d.rules && d.rules.in), outbound_sorted: sortRules(d.rules && d.rules.out) }; break; case 'sg': extra={ inbound_sorted: sortRules(d.rules && d.rules.in), outbound_sorted: sortRules(d.rules && d.rules.out), accountId: d.accountId }; break;
default: extra=d; break; default: extra=d; break;
} }
$details.textContent=JSON.stringify({ ...base, ...extra }, null, 2); $details.textContent=JSON.stringify({ ...base, ...extra }, null, 2);

323
gather.py
View File

@ -1,114 +1,277 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import boto3, json, os, sys """
Fetch AWS VPC-related metadata and emit a Cytoscape-friendly `graph.json` file.
Adds support for multiple AWS accounts by letting you specify one or more AWS CLI
profiles. Each account is tagged in the output so the front-end can group VPCs
per account.
"""
import argparse
import json
from collections import defaultdict
import boto3
from botocore.exceptions import ClientError
RESOURCE_KEYS = ['vpcs', 'subnets', 'sgs', 'enis', 'ec2', 'lbs', 'rds', 'exposures']
ACCOUNT_COLORS = ['#7c3aed', '#0ea5e9', '#f97316', '#10b981', '#facc15', '#f472b6']
def name_tag(tags): def name_tag(tags):
return next((t['Value'] for t in (tags or []) if t['Key']=='Name'), None) return next((t['Value'] for t in (tags or []) if t['Key'] == 'Name'), None)
def collect(region):
s = boto3.Session(region_name=region)
ec2, elb, rds = s.client('ec2'), s.client('elbv2'), s.client('rds')
out = {k:[] for k in ['vpcs','subnets','sgs','enis','ec2','lbs','rds','exposures']}
# VPCs, Subnets, SGs, ENIs, Instances def empty_region():
return {k: [] for k in RESOURCE_KEYS}
def resolve_account(session, profile_hint=None):
sts = session.client('sts')
identity = sts.get_caller_identity()
account_id = identity['Account']
alias = None
try:
iam = session.client('iam')
aliases = iam.list_account_aliases().get('AccountAliases', [])
alias = aliases[0] if aliases else None
except ClientError:
alias = None
label = alias or profile_hint or account_id
return {'id': account_id, 'label': label, 'alias': alias}
def collect(session, account):
region = session.region_name
out = empty_region()
ec2 = session.client('ec2')
elb = session.client('elbv2')
rds = session.client('rds')
vpcs = ec2.describe_vpcs()['Vpcs'] vpcs = ec2.describe_vpcs()['Vpcs']
subnets = ec2.describe_subnets()['Subnets'] subnets = ec2.describe_subnets()['Subnets']
sgs = ec2.describe_security_groups()['SecurityGroups'] sgs = ec2.describe_security_groups()['SecurityGroups']
enis = ec2.describe_network_interfaces()['NetworkInterfaces'] enis = ec2.describe_network_interfaces()['NetworkInterfaces']
resv = ec2.describe_instances()['Reservations'] reservations = ec2.describe_instances()['Reservations']
for v in vpcs: for v in vpcs:
out['vpcs'].append({'id':v['VpcId'],'cidr':v['CidrBlock'],'name':name_tag(v.get('Tags')) or v['VpcId']}) out['vpcs'].append({
'id': v['VpcId'],
'cidr': v['CidrBlock'],
'name': name_tag(v.get('Tags')) or v['VpcId'],
'accountId': account['id']
})
for sn in subnets: for sn in subnets:
out['subnets'].append({ out['subnets'].append({
'id':sn['SubnetId'],'vpc':sn['VpcId'],'cidr':sn['CidrBlock'], 'id': sn['SubnetId'],
'az':sn['AvailabilityZone'],'public':sn.get('MapPublicIpOnLaunch',False) 'vpc': sn['VpcId'],
'cidr': sn['CidrBlock'],
'az': sn['AvailabilityZone'],
'public': sn.get('MapPublicIpOnLaunch', False),
'accountId': account['id']
}) })
def flatten_rules(perms):
rules = []
for perm in perms:
proto = perm.get('IpProtocol')
frm = perm.get('FromPort')
to = perm.get('ToPort')
if frm is None:
port = 'all'
elif frm == to:
port = f"{frm}"
else:
port = f"{frm}-{to}"
for ipr in perm.get('IpRanges', []) + perm.get('Ipv6Ranges', []):
cidr = ipr.get('CidrIp') or ipr.get('CidrIpv6')
rules.append(f"{proto} {port} from {cidr}")
for sgr in perm.get('UserIdGroupPairs', []):
rules.append(f"{proto} {port} from {sgr['GroupId']}")
return rules
for g in sgs: for g in sgs:
def flatten(perms): out['sgs'].append({
r=[] 'id': g['GroupId'],
for p in perms: 'name': g.get('GroupName'),
proto=p.get('IpProtocol'); frm=p.get('FromPort'); to=p.get('ToPort') 'vpc': g.get('VpcId'),
port = f"{frm}" if frm==to else f"{frm}-{to}" if frm is not None else "all" 'accountId': account['id'],
for ipr in p.get('IpRanges',[]) + p.get('Ipv6Ranges',[]): 'rules_in': flatten_rules(g.get('IpPermissions', [])),
cidr = ipr.get('CidrIp') or ipr.get('CidrIpv6') 'rules_out': flatten_rules(g.get('IpPermissionsEgress', []))
r.append(f"{proto} {port} from {cidr}")
for sgr in p.get('UserIdGroupPairs',[]):
r.append(f"{proto} {port} from {sgr['GroupId']}")
return r
out['sgs'].append({'id':g['GroupId'],'name':g.get('GroupName'),
'rules_in':flatten(g.get('IpPermissions',[])),
'rules_out':flatten(g.get('IpPermissionsEgress',[]))})
for ni in enis:
assoc = ni.get('Association',{})
out['enis'].append({
'id':ni['NetworkInterfaceId'],'subnet':ni['SubnetId'],
'sgs':[sg['GroupId'] for sg in ni.get('Groups',[])],
'privateIp':ni.get('PrivateIpAddress'),
'publicIp':assoc.get('PublicIp')
}) })
for r in resv:
for i in r['Instances']: for ni in enis:
n = name_tag(i.get('Tags')) or i['InstanceId'] assoc = ni.get('Association', {})
eni = (i.get('NetworkInterfaces') or [{}])[0] out['enis'].append({
'id': ni['NetworkInterfaceId'],
'subnet': ni['SubnetId'],
'vpc': ni.get('VpcId'),
'sgs': [sg['GroupId'] for sg in ni.get('Groups', [])],
'privateIp': ni.get('PrivateIpAddress'),
'publicIp': assoc.get('PublicIp'),
'accountId': account['id']
})
for reservation in reservations:
for inst in reservation['Instances']:
inst_name = name_tag(inst.get('Tags')) or inst['InstanceId']
out['ec2'].append({ out['ec2'].append({
'id': i['InstanceId'], 'id': inst['InstanceId'],
'name': n, 'name': inst_name,
'type': i['InstanceType'], 'type': inst['InstanceType'],
'privateIp': i.get('PrivateIpAddress'), 'privateIp': inst.get('PrivateIpAddress'),
'publicIp': i.get('PublicIpAddress'), # shows on label 'publicIp': inst.get('PublicIpAddress'),
'state': i.get('State', {}).get('Name', 'unknown'), # running/stopped 'state': inst.get('State', {}).get('Name', 'unknown'),
'sgs': [g['GroupId'] for g in i.get('SecurityGroups',[])], 'sgs': [g['GroupId'] for g in inst.get('SecurityGroups', [])],
'subnet': i.get('SubnetId') 'subnet': inst.get('SubnetId'),
'vpc': inst.get('VpcId'),
'accountId': account['id']
}) })
# Load balancers (ALB/NLB), listeners, target groups/targets
try: try:
lbs = elb.describe_load_balancers()['LoadBalancers'] lbs = elb.describe_load_balancers()['LoadBalancers']
except elb.exceptions.UnsupportedFeatureException: except (ClientError, elb.exceptions.UnsupportedFeatureException):
lbs = [] lbs = []
for lb in lbs: for lb in lbs:
lbid = lb['LoadBalancerName'] lb_id = lb['LoadBalancerName']
listeners = elb.describe_listeners(LoadBalancerArn=lb['LoadBalancerArn'])['Listeners'] try:
lst = [{'proto':L['Protocol'],'port':L['Port']} for L in listeners] listeners = elb.describe_listeners(LoadBalancerArn=lb['LoadBalancerArn'])['Listeners']
tgs = elb.describe_target_groups(LoadBalancerArn=lb['LoadBalancerArn'])['TargetGroups'] except ClientError:
tga=[] listeners = []
listener_meta = [{'proto': L['Protocol'], 'port': L['Port']} for L in listeners]
try:
tgs = elb.describe_target_groups(LoadBalancerArn=lb['LoadBalancerArn'])['TargetGroups']
except ClientError:
tgs = []
target_groups = []
all_targets = []
for tg in tgs: for tg in tgs:
th = elb.describe_target_health(TargetGroupArn=tg['TargetGroupArn'])['TargetHealthDescriptions'] try:
targets=[thd['Target']['Id'] for thd in th] health = elb.describe_target_health(TargetGroupArn=tg['TargetGroupArn'])['TargetHealthDescriptions']
tga.append({'port':tg.get('Port'),'targets':targets}) except ClientError:
health = []
targets = [desc['Target']['Id'] for desc in health]
target_groups.append({'port': tg.get('Port'), 'targets': targets})
all_targets.extend(targets)
out['lbs'].append({ out['lbs'].append({
'id': lbid, 'id': lb_id,
'scheme': lb['Scheme'], 'scheme': lb['Scheme'],
'type': lb['Type'], 'type': lb['Type'],
'dns': lb.get('DNSName'), # add me (handy on the label) 'dns': lb.get('DNSName'),
'subnets': [z['SubnetId'] for z in lb.get('AvailabilityZones',[])], 'subnets': [az['SubnetId'] for az in lb.get('AvailabilityZones', [])],
'securityGroups': lb.get('SecurityGroups',[]), 'securityGroups': lb.get('SecurityGroups', []),
'listeners': lst, 'targetGroups': tga 'listeners': listener_meta,
}) 'targetGroups': target_groups,
# Simple exposure: internet-facing LB listeners are public surfaces 'accountId': account['id']
if lb['Scheme']=='internet-facing': })
for L in lst:
out['exposures'].append({'surface':f"{lbid}:{L['port']}", if lb['Scheme'] == 'internet-facing':
'world_open':True,'via':lb['Type'].upper(), for listener in listener_meta:
'to': [f"{t}:{next((tg['port'] for tg in tga if t in tg['targets']),None)}" for t in sum([tg['targets'] for tg in tga],[])]}) out['exposures'].append({
'surface': f"{lb_id}:{listener['port']}",
'world_open': True,
'via': lb['Type'].upper(),
'to': [
f"{t}:{next((tg['port'] for tg in target_groups if t in tg['targets']), None)}"
for t in all_targets
],
'accountId': account['id'],
'region': region
})
# RDS
for db in rds.describe_db_instances()['DBInstances']: for db in rds.describe_db_instances()['DBInstances']:
out['rds'].append({ out['rds'].append({
'id':db['DBInstanceIdentifier'],'engine':db['Engine'], 'id': db['DBInstanceIdentifier'],
'port':db['DbInstancePort'] if 'DbInstancePort' in db else db.get('Endpoint',{}).get('Port'), 'engine': db['Engine'],
'publiclyAccessible':db.get('PubliclyAccessible',False), 'port': db.get('DbInstancePort') or db.get('Endpoint', {}).get('Port'),
'sgs':[v['VpcSecurityGroupId'] for v in db.get('VpcSecurityGroups',[])], 'publiclyAccessible': db.get('PubliclyAccessible', False),
'subnetGroup':[s['SubnetIdentifier'] for s in db.get('DBSubnetGroup',{}).get('Subnets',[])] 'sgs': [sg['VpcSecurityGroupId'] for sg in db.get('VpcSecurityGroups', [])],
'subnetGroup': [s['SubnetIdentifier'] for s in db.get('DBSubnetGroup', {}).get('Subnets', [])],
'accountId': account['id']
}) })
return out return out
def main():
regions = sys.argv[1:] or [boto3.Session().region_name or 'us-west-1']
graph = {'regions':{}}
for r in regions:
graph['regions'][r] = collect(r)
with open('graph.json','w') as f: json.dump(graph, f, indent=2)
print('Wrote graph.json for regions:', ', '.join(regions))
if __name__=='__main__': main() def parse_args():
parser = argparse.ArgumentParser(description='Gather AWS VPC graph data into graph.json')
parser.add_argument('regions', nargs='*', help='AWS regions to scan (defaults to your configured region)')
parser.add_argument('-r', '--region', dest='regions_opt', action='append', help='Additional region to scan (repeatable)')
parser.add_argument('-p', '--profile', dest='profiles', action='append', help='AWS CLI profile to use (repeatable)')
parser.add_argument('--account-label', dest='account_labels', action='append', help='Friendly label per profile (matches order of --profile)')
parser.add_argument('-o', '--output', default='graph.json', help='Output file (default: graph.json)')
return parser
def main():
parser = parse_args()
args = parser.parse_args()
regions = []
if args.regions:
regions.extend(args.regions)
if args.regions_opt:
regions.extend(args.regions_opt)
if not regions:
default_region = boto3.Session().region_name
if not default_region:
parser.error('No regions specified and no default region configured.')
regions = [default_region]
regions = sorted(set(regions))
profiles = args.profiles or [None]
account_label_overrides = args.account_labels or []
graph_accounts = []
account_index = {}
region_data = defaultdict(empty_region)
for idx, profile in enumerate(profiles):
profile_label_hint = account_label_overrides[idx] if idx < len(account_label_overrides) else profile
for region in regions:
session = boto3.Session(profile_name=profile, region_name=region)
account = resolve_account(session, profile_label_hint)
account_id = account['id']
if account_id not in account_index:
color = ACCOUNT_COLORS[len(account_index) % len(ACCOUNT_COLORS)]
entry = {
'id': account_id,
'label': account['label'],
'alias': account['alias'],
'profile': profile,
'color': color
}
graph_accounts.append(entry)
account_index[account_id] = entry
else:
entry = account_index[account_id]
data = collect(session, entry)
bucket = region_data[region]
for key in RESOURCE_KEYS:
bucket[key].extend(data[key])
graph = {
'accounts': graph_accounts,
'regions': {region: bucket for region, bucket in region_data.items()}
}
with open(args.output, 'w') as f:
json.dump(graph, f, indent=2)
region_list = ', '.join(regions)
profile_list = ', '.join(p or 'default' for p in profiles)
print(f"Wrote {args.output} for regions: {region_list} (profiles: {profile_list})")
if __name__ == '__main__':
main()

View File

@ -1,31 +1,48 @@
{ {
"accounts": [
{
"id": "acct-shared",
"label": "Shared Services",
"color": "#7c3aed"
},
{
"id": "acct-prod",
"label": "Production Apps",
"color": "#0ea5e9"
}
],
"regions": { "regions": {
"us-west-1": { "us-west-1": {
"vpcs": [ "vpcs": [
{ {
"id": "vpc-87832de2", "id": "vpc-87832de2",
"cidr": "10.4.0.0/16", "cidr": "10.4.0.0/16",
"name": "vault.vpc.01" "name": "vault.vpc.01",
"accountId": "acct-shared"
}, },
{ {
"id": "vpc-c84eabad", "id": "vpc-c84eabad",
"cidr": "172.31.0.0/16", "cidr": "172.31.0.0/16",
"name": "vpc-c84eabad" "name": "vpc-c84eabad",
"accountId": "acct-prod"
}, },
{ {
"id": "vpc-331ad056", "id": "vpc-331ad056",
"cidr": "10.3.0.0/16", "cidr": "10.3.0.0/16",
"name": "efw.vpc.02" "name": "efw.vpc.02",
"accountId": "acct-prod"
}, },
{ {
"id": "vpc-afe722ca", "id": "vpc-afe722ca",
"cidr": "10.2.0.0/16", "cidr": "10.2.0.0/16",
"name": "efw.vpc.01" "name": "efw.vpc.01",
"accountId": "acct-prod"
}, },
{ {
"id": "vpc-03f799f47b766798c", "id": "vpc-03f799f47b766798c",
"cidr": "10.10.0.0/16", "cidr": "10.10.0.0/16",
"name": "ALB-VPC" "name": "ALB-VPC",
"accountId": "acct-shared"
} }
], ],
"subnets": [ "subnets": [
@ -34,98 +51,112 @@
"vpc": "vpc-331ad056", "vpc": "vpc-331ad056",
"cidr": "10.3.0.0/24", "cidr": "10.3.0.0/24",
"az": "us-west-1b", "az": "us-west-1b",
"public": false "public": false,
"accountId": "acct-prod"
}, },
{ {
"id": "subnet-f73afeae", "id": "subnet-f73afeae",
"vpc": "vpc-331ad056", "vpc": "vpc-331ad056",
"cidr": "10.3.1.0/24", "cidr": "10.3.1.0/24",
"az": "us-west-1b", "az": "us-west-1b",
"public": false "public": false,
"accountId": "acct-prod"
}, },
{ {
"id": "subnet-ebc4188e", "id": "subnet-ebc4188e",
"vpc": "vpc-afe722ca", "vpc": "vpc-afe722ca",
"cidr": "10.2.1.0/24", "cidr": "10.2.1.0/24",
"az": "us-west-1c", "az": "us-west-1c",
"public": false "public": false,
"accountId": "acct-prod"
}, },
{ {
"id": "subnet-005bfd58e719e031f", "id": "subnet-005bfd58e719e031f",
"vpc": "vpc-87832de2", "vpc": "vpc-87832de2",
"cidr": "10.4.11.0/24", "cidr": "10.4.11.0/24",
"az": "us-west-1c", "az": "us-west-1c",
"public": false "public": false,
"accountId": "acct-shared"
}, },
{ {
"id": "subnet-035f81142068bf961", "id": "subnet-035f81142068bf961",
"vpc": "vpc-03f799f47b766798c", "vpc": "vpc-03f799f47b766798c",
"cidr": "10.10.2.0/24", "cidr": "10.10.2.0/24",
"az": "us-west-1b", "az": "us-west-1b",
"public": false "public": false,
"accountId": "acct-shared"
}, },
{ {
"id": "subnet-d137fd88", "id": "subnet-d137fd88",
"vpc": "vpc-87832de2", "vpc": "vpc-87832de2",
"cidr": "10.4.0.0/24", "cidr": "10.4.0.0/24",
"az": "us-west-1b", "az": "us-west-1b",
"public": false "public": false,
"accountId": "acct-shared"
}, },
{ {
"id": "subnet-e98e088c", "id": "subnet-e98e088c",
"vpc": "vpc-331ad056", "vpc": "vpc-331ad056",
"cidr": "10.3.2.0/24", "cidr": "10.3.2.0/24",
"az": "us-west-1c", "az": "us-west-1c",
"public": false "public": false,
"accountId": "acct-prod"
}, },
{ {
"id": "subnet-08e40dcdc103438b8", "id": "subnet-08e40dcdc103438b8",
"vpc": "vpc-87832de2", "vpc": "vpc-87832de2",
"cidr": "10.4.2.0/24", "cidr": "10.4.2.0/24",
"az": "us-west-1c", "az": "us-west-1c",
"public": false "public": false,
"accountId": "acct-shared"
}, },
{ {
"id": "subnet-0cb2aa7457413f5fe", "id": "subnet-0cb2aa7457413f5fe",
"vpc": "vpc-03f799f47b766798c", "vpc": "vpc-03f799f47b766798c",
"cidr": "10.10.3.0/24", "cidr": "10.10.3.0/24",
"az": "us-west-1c", "az": "us-west-1c",
"public": true "public": true,
"accountId": "acct-shared"
}, },
{ {
"id": "subnet-247a9341", "id": "subnet-247a9341",
"vpc": "vpc-c84eabad", "vpc": "vpc-c84eabad",
"cidr": "172.31.16.0/20", "cidr": "172.31.16.0/20",
"az": "us-west-1c", "az": "us-west-1c",
"public": true "public": true,
"accountId": "acct-prod"
}, },
{ {
"id": "subnet-34173f72", "id": "subnet-34173f72",
"vpc": "vpc-c84eabad", "vpc": "vpc-c84eabad",
"cidr": "172.31.0.0/20", "cidr": "172.31.0.0/20",
"az": "us-west-1b", "az": "us-west-1b",
"public": true "public": true,
"accountId": "acct-prod"
}, },
{ {
"id": "subnet-05b5dcdc91a8d3da2", "id": "subnet-05b5dcdc91a8d3da2",
"vpc": "vpc-87832de2", "vpc": "vpc-87832de2",
"cidr": "10.4.10.0/24", "cidr": "10.4.10.0/24",
"az": "us-west-1b", "az": "us-west-1b",
"public": false "public": false,
"accountId": "acct-shared"
}, },
{ {
"id": "subnet-16594550", "id": "subnet-16594550",
"vpc": "vpc-afe722ca", "vpc": "vpc-afe722ca",
"cidr": "10.2.2.0/24", "cidr": "10.2.2.0/24",
"az": "us-west-1b", "az": "us-west-1b",
"public": false "public": false,
"accountId": "acct-prod"
}, },
{ {
"id": "subnet-051f6d705e415834d", "id": "subnet-051f6d705e415834d",
"vpc": "vpc-03f799f47b766798c", "vpc": "vpc-03f799f47b766798c",
"cidr": "10.10.1.0/24", "cidr": "10.10.1.0/24",
"az": "us-west-1b", "az": "us-west-1b",
"public": true "public": true,
"accountId": "acct-shared"
} }
], ],
"sgs": [ "sgs": [
@ -151,7 +182,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "sg-00600deefd8e47cd0", "id": "sg-00600deefd8e47cd0",
@ -161,7 +194,8 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"accountId": "acct-shared"
}, },
{ {
"id": "sg-da75adbf", "id": "sg-da75adbf",
@ -176,7 +210,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "sg-c05a2ba5", "id": "sg-c05a2ba5",
@ -186,7 +222,8 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"accountId": "acct-shared"
}, },
{ {
"id": "sg-0c00315bbb744b876", "id": "sg-0c00315bbb744b876",
@ -196,7 +233,8 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"accountId": "acct-shared"
}, },
{ {
"id": "sg-495ab32c", "id": "sg-495ab32c",
@ -208,7 +246,8 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"accountId": "acct-shared"
}, },
{ {
"id": "sg-0f2465c4421c2d5c2", "id": "sg-0f2465c4421c2d5c2",
@ -219,7 +258,8 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"accountId": "acct-shared"
}, },
{ {
"id": "sg-8b746aec", "id": "sg-8b746aec",
@ -233,7 +273,8 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"accountId": "acct-shared"
}, },
{ {
"id": "sg-0a872ac3a6d9132a6", "id": "sg-0a872ac3a6d9132a6",
@ -245,7 +286,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "sg-076d2ea1df0054074", "id": "sg-076d2ea1df0054074",
@ -256,7 +299,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "sg-b45a2bd1", "id": "sg-b45a2bd1",
@ -287,7 +332,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "sg-4235ee27", "id": "sg-4235ee27",
@ -298,7 +345,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "sg-04167d2e18ba15e45", "id": "sg-04167d2e18ba15e45",
@ -308,7 +357,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "sg-06348763", "id": "sg-06348763",
@ -353,7 +404,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "sg-08fb4b412f90d5913", "id": "sg-08fb4b412f90d5913",
@ -361,7 +414,8 @@
"rules_in": [], "rules_in": [],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"accountId": "acct-prod"
}, },
{ {
"id": "sg-09621eafa81d9554d", "id": "sg-09621eafa81d9554d",
@ -372,7 +426,9 @@
], ],
"rules_out": [ "rules_out": [
"-1 None from 0.0.0.0/0" "-1 None from 0.0.0.0/0"
] ],
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
} }
], ],
"enis": [ "enis": [
@ -383,7 +439,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.216", "privateIp": "10.3.0.216",
"publicIp": "52.8.79.80" "publicIp": "52.8.79.80",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0c6e3761c14140e52", "id": "eni-0c6e3761c14140e52",
@ -392,7 +450,9 @@
"sg-076d2ea1df0054074" "sg-076d2ea1df0054074"
], ],
"privateIp": "10.10.1.212", "privateIp": "10.10.1.212",
"publicIp": "50.18.175.227" "publicIp": "50.18.175.227",
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-6a099b32", "id": "eni-6a099b32",
@ -401,7 +461,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.207", "privateIp": "10.3.0.207",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-072ff2545f56240a7", "id": "eni-072ff2545f56240a7",
@ -410,7 +472,9 @@
"sg-4235ee27" "sg-4235ee27"
], ],
"privateIp": "10.2.2.16", "privateIp": "10.2.2.16",
"publicIp": null "publicIp": null,
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-064252b93ca105b7c", "id": "eni-064252b93ca105b7c",
@ -419,7 +483,9 @@
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"privateIp": "10.4.10.82", "privateIp": "10.4.10.82",
"publicIp": null "publicIp": null,
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-08d57de7981ae0020", "id": "eni-08d57de7981ae0020",
@ -429,7 +495,9 @@
"sg-0a872ac3a6d9132a6" "sg-0a872ac3a6d9132a6"
], ],
"privateIp": "10.10.2.106", "privateIp": "10.10.2.106",
"publicIp": null "publicIp": null,
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-00feca9ca48c7ae0c", "id": "eni-00feca9ca48c7ae0c",
@ -438,7 +506,9 @@
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"privateIp": "10.4.10.141", "privateIp": "10.4.10.141",
"publicIp": null "publicIp": null,
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-8041e3da", "id": "eni-8041e3da",
@ -447,7 +517,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.201", "privateIp": "10.3.0.201",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-9d73dbc7", "id": "eni-9d73dbc7",
@ -456,7 +528,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.98", "privateIp": "10.3.0.98",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-04237833786a67013", "id": "eni-04237833786a67013",
@ -465,7 +539,9 @@
"sg-0a872ac3a6d9132a6" "sg-0a872ac3a6d9132a6"
], ],
"privateIp": "10.10.1.166", "privateIp": "10.10.1.166",
"publicIp": "52.53.243.75" "publicIp": "52.53.243.75",
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-084002309358bb545", "id": "eni-084002309358bb545",
@ -474,7 +550,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.1.134", "privateIp": "10.3.1.134",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0827085d571dccab7", "id": "eni-0827085d571dccab7",
@ -483,7 +561,9 @@
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"privateIp": "10.4.0.221", "privateIp": "10.4.0.221",
"publicIp": "52.53.117.111" "publicIp": "52.53.117.111",
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-0e844b9d718f3daa0", "id": "eni-0e844b9d718f3daa0",
@ -492,7 +572,9 @@
"sg-642cf701" "sg-642cf701"
], ],
"privateIp": "10.2.2.226", "privateIp": "10.2.2.226",
"publicIp": "52.8.26.159" "publicIp": "52.8.26.159",
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0d6f412450d0e082b", "id": "eni-0d6f412450d0e082b",
@ -501,7 +583,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.63", "privateIp": "10.3.0.63",
"publicIp": "52.8.7.0" "publicIp": "52.8.7.0",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-05788a45421ce580f", "id": "eni-05788a45421ce580f",
@ -510,7 +594,9 @@
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"privateIp": "10.4.0.197", "privateIp": "10.4.0.197",
"publicIp": "52.8.75.57" "publicIp": "52.8.75.57",
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-0775830b9669fa479", "id": "eni-0775830b9669fa479",
@ -519,7 +605,9 @@
"sg-642cf701" "sg-642cf701"
], ],
"privateIp": "10.2.2.18", "privateIp": "10.2.2.18",
"publicIp": "54.153.101.192" "publicIp": "54.153.101.192",
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0f54a2ee3a5237ae1", "id": "eni-0f54a2ee3a5237ae1",
@ -528,7 +616,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.111", "privateIp": "10.3.0.111",
"publicIp": "54.153.3.41" "publicIp": "54.153.3.41",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-08eb3a38264ccd212", "id": "eni-08eb3a38264ccd212",
@ -537,7 +627,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.73", "privateIp": "10.3.0.73",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0afbe7eb22dd6e8e3", "id": "eni-0afbe7eb22dd6e8e3",
@ -546,7 +638,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.112", "privateIp": "10.3.0.112",
"publicIp": "13.52.49.251" "publicIp": "13.52.49.251",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-018923614c623ef74", "id": "eni-018923614c623ef74",
@ -555,7 +649,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.122", "privateIp": "10.3.0.122",
"publicIp": "52.8.85.37" "publicIp": "52.8.85.37",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0f310dd2e36654ed7", "id": "eni-0f310dd2e36654ed7",
@ -564,14 +660,18 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.120", "privateIp": "10.3.0.120",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-00172626263229e1a", "id": "eni-00172626263229e1a",
"subnet": "subnet-051f6d705e415834d", "subnet": "subnet-051f6d705e415834d",
"sgs": [], "sgs": [],
"privateIp": "10.10.1.37", "privateIp": "10.10.1.37",
"publicIp": "13.57.111.5" "publicIp": "13.57.111.5",
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-0893189c99a73d4f2", "id": "eni-0893189c99a73d4f2",
@ -580,14 +680,18 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.167", "privateIp": "10.3.0.167",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0d0b9801f1e66ec76", "id": "eni-0d0b9801f1e66ec76",
"subnet": "subnet-d137fd88", "subnet": "subnet-d137fd88",
"sgs": [], "sgs": [],
"privateIp": "10.4.0.246", "privateIp": "10.4.0.246",
"publicIp": "184.169.224.203" "publicIp": "184.169.224.203",
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-b574dcef", "id": "eni-b574dcef",
@ -596,7 +700,9 @@
"sg-642cf701" "sg-642cf701"
], ],
"privateIp": "10.2.2.48", "privateIp": "10.2.2.48",
"publicIp": null "publicIp": null,
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0080f66b73a68d1d1", "id": "eni-0080f66b73a68d1d1",
@ -605,7 +711,9 @@
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"privateIp": "10.4.0.117", "privateIp": "10.4.0.117",
"publicIp": "52.8.219.246" "publicIp": "52.8.219.246",
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-02075fc7f6b5ed6f3", "id": "eni-02075fc7f6b5ed6f3",
@ -614,7 +722,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.211", "privateIp": "10.3.0.211",
"publicIp": "13.57.152.11" "publicIp": "13.57.152.11",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-026ab4825bc4dfb8b", "id": "eni-026ab4825bc4dfb8b",
@ -623,7 +733,9 @@
"sg-06348763" "sg-06348763"
], ],
"privateIp": "10.3.0.154", "privateIp": "10.3.0.154",
"publicIp": null "publicIp": null,
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "eni-0ac39f75ad7dc0709", "id": "eni-0ac39f75ad7dc0709",
@ -632,7 +744,9 @@
"sg-076d2ea1df0054074" "sg-076d2ea1df0054074"
], ],
"privateIp": "10.10.3.88", "privateIp": "10.10.3.88",
"publicIp": "52.52.1.145" "publicIp": "52.52.1.145",
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-0015cacdc10d467e8", "id": "eni-0015cacdc10d467e8",
@ -641,7 +755,9 @@
"sg-09621eafa81d9554d" "sg-09621eafa81d9554d"
], ],
"privateIp": "10.4.11.198", "privateIp": "10.4.11.198",
"publicIp": null "publicIp": null,
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-039a605986c15dabf", "id": "eni-039a605986c15dabf",
@ -650,7 +766,9 @@
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"privateIp": "10.4.11.166", "privateIp": "10.4.11.166",
"publicIp": null "publicIp": null,
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-01f9242eb64928de8", "id": "eni-01f9242eb64928de8",
@ -659,7 +777,9 @@
"sg-09621eafa81d9554d" "sg-09621eafa81d9554d"
], ],
"privateIp": "10.4.11.139", "privateIp": "10.4.11.139",
"publicIp": null "publicIp": null,
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-09a54855abf31a012", "id": "eni-09a54855abf31a012",
@ -668,7 +788,9 @@
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"privateIp": "10.4.11.69", "privateIp": "10.4.11.69",
"publicIp": null "publicIp": null,
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "eni-0787462fd9a1d1b6d", "id": "eni-0787462fd9a1d1b6d",
@ -677,7 +799,9 @@
"sg-da75adbf" "sg-da75adbf"
], ],
"privateIp": "10.2.1.156", "privateIp": "10.2.1.156",
"publicIp": "54.241.122.239" "publicIp": "54.241.122.239",
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
} }
], ],
"ec2": [ "ec2": [
@ -690,7 +814,9 @@
"sgs": [ "sgs": [
"sg-06348763" "sg-06348763"
], ],
"subnet": "subnet-260ee27f" "subnet": "subnet-260ee27f",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "i-0997a73b08f6e5862", "id": "i-0997a73b08f6e5862",
@ -701,7 +827,9 @@
"sgs": [ "sgs": [
"sg-642cf701" "sg-642cf701"
], ],
"subnet": "subnet-16594550" "subnet": "subnet-16594550",
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "i-0c8100e3460fa8fd0", "id": "i-0c8100e3460fa8fd0",
@ -712,7 +840,9 @@
"sgs": [ "sgs": [
"sg-06348763" "sg-06348763"
], ],
"subnet": "subnet-260ee27f" "subnet": "subnet-260ee27f",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "i-041021cd89e15282c", "id": "i-041021cd89e15282c",
@ -723,7 +853,9 @@
"sgs": [ "sgs": [
"sg-da75adbf" "sg-da75adbf"
], ],
"subnet": "subnet-ebc4188e" "subnet": "subnet-ebc4188e",
"vpc": "vpc-afe722ca",
"accountId": "acct-prod"
}, },
{ {
"id": "i-073b97cbda2b200c3", "id": "i-073b97cbda2b200c3",
@ -734,7 +866,9 @@
"sgs": [ "sgs": [
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"subnet": "subnet-d137fd88" "subnet": "subnet-d137fd88",
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "i-0669f35ab2d0fc444", "id": "i-0669f35ab2d0fc444",
@ -745,7 +879,9 @@
"sgs": [ "sgs": [
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"subnet": "subnet-d137fd88" "subnet": "subnet-d137fd88",
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "i-01b3f3cd57976bdf3", "id": "i-01b3f3cd57976bdf3",
@ -756,7 +892,9 @@
"sgs": [ "sgs": [
"sg-b45a2bd1" "sg-b45a2bd1"
], ],
"subnet": "subnet-d137fd88" "subnet": "subnet-d137fd88",
"vpc": "vpc-87832de2",
"accountId": "acct-shared"
}, },
{ {
"id": "i-0272763b46610ac1b", "id": "i-0272763b46610ac1b",
@ -767,7 +905,9 @@
"sgs": [ "sgs": [
"sg-06348763" "sg-06348763"
], ],
"subnet": "subnet-260ee27f" "subnet": "subnet-260ee27f",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "i-0c82adf476c7c5e32", "id": "i-0c82adf476c7c5e32",
@ -778,7 +918,9 @@
"sgs": [ "sgs": [
"sg-06348763" "sg-06348763"
], ],
"subnet": "subnet-260ee27f" "subnet": "subnet-260ee27f",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "i-0636fd0b033c9b32a", "id": "i-0636fd0b033c9b32a",
@ -789,7 +931,9 @@
"sgs": [ "sgs": [
"sg-06348763" "sg-06348763"
], ],
"subnet": "subnet-260ee27f" "subnet": "subnet-260ee27f",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "i-09241599c2590b66a", "id": "i-09241599c2590b66a",
@ -800,7 +944,9 @@
"sgs": [ "sgs": [
"sg-06348763" "sg-06348763"
], ],
"subnet": "subnet-260ee27f" "subnet": "subnet-260ee27f",
"vpc": "vpc-331ad056",
"accountId": "acct-prod"
}, },
{ {
"id": "i-0d7f643cb9d960645", "id": "i-0d7f643cb9d960645",
@ -811,7 +957,9 @@
"sgs": [ "sgs": [
"sg-0a872ac3a6d9132a6" "sg-0a872ac3a6d9132a6"
], ],
"subnet": "subnet-051f6d705e415834d" "subnet": "subnet-051f6d705e415834d",
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
}, },
{ {
"id": "i-082b27477bbe6d8b5", "id": "i-082b27477bbe6d8b5",
@ -823,7 +971,9 @@
"sg-04167d2e18ba15e45", "sg-04167d2e18ba15e45",
"sg-0a872ac3a6d9132a6" "sg-0a872ac3a6d9132a6"
], ],
"subnet": "subnet-035f81142068bf961" "subnet": "subnet-035f81142068bf961",
"vpc": "vpc-03f799f47b766798c",
"accountId": "acct-shared"
} }
], ],
"lbs": [ "lbs": [
@ -855,7 +1005,8 @@
"i-082b27477bbe6d8b5" "i-082b27477bbe6d8b5"
] ]
} }
] ],
"accountId": "acct-shared"
} }
], ],
"rds": [ "rds": [
@ -870,7 +1021,8 @@
"subnetGroup": [ "subnetGroup": [
"subnet-e98e088c", "subnet-e98e088c",
"subnet-f73afeae" "subnet-f73afeae"
] ],
"accountId": "acct-prod"
}, },
{ {
"id": "logon-db-02", "id": "logon-db-02",
@ -883,7 +1035,8 @@
"subnetGroup": [ "subnetGroup": [
"subnet-16594550", "subnet-16594550",
"subnet-ebc4188e" "subnet-ebc4188e"
] ],
"accountId": "acct-prod"
}, },
{ {
"id": "vault-db-production-v2", "id": "vault-db-production-v2",
@ -896,7 +1049,8 @@
"subnetGroup": [ "subnetGroup": [
"subnet-005bfd58e719e031f", "subnet-005bfd58e719e031f",
"subnet-05b5dcdc91a8d3da2" "subnet-05b5dcdc91a8d3da2"
] ],
"accountId": "acct-shared"
}, },
{ {
"id": "vault-db-staging-v2", "id": "vault-db-staging-v2",
@ -909,7 +1063,8 @@
"subnetGroup": [ "subnetGroup": [
"subnet-005bfd58e719e031f", "subnet-005bfd58e719e031f",
"subnet-05b5dcdc91a8d3da2" "subnet-05b5dcdc91a8d3da2"
] ],
"accountId": "acct-shared"
} }
], ],
"exposures": [ "exposures": [
@ -919,7 +1074,9 @@
"via": "APPLICATION", "via": "APPLICATION",
"to": [ "to": [
"i-082b27477bbe6d8b5:8080" "i-082b27477bbe6d8b5:8080"
] ],
"accountId": "acct-shared",
"region": "us-west-1"
}, },
{ {
"surface": "NodeAppALB:443", "surface": "NodeAppALB:443",
@ -927,7 +1084,9 @@
"via": "APPLICATION", "via": "APPLICATION",
"to": [ "to": [
"i-082b27477bbe6d8b5:8080" "i-082b27477bbe6d8b5:8080"
] ],
"accountId": "acct-shared",
"region": "us-west-1"
} }
] ]
} }

View File

@ -1,161 +1,177 @@
{ {
"nodes": { "nodes": {
"account:acct-shared": {
"position": {
"x": 2383.6718307806645,
"y": 1142.0623550975815
},
"lx": 0,
"ly": 0
},
"account:acct-prod": {
"position": {
"x": -1257.3436368163789,
"y": 1751.2282249012555
},
"lx": 0,
"ly": 0
},
"vpc-87832de2": { "vpc-87832de2": {
"position": { "position": {
"x": 756.8840099911947, "x": 2622.379364533625,
"y": 1038.4416407451458 "y": 792.8749925626238
}, },
"lx": 77.51854351992131, "lx": 77.51854351992131,
"ly": 139.5230395934425 "ly": 139.5230395934425
}, },
"vpc-c84eabad": { "vpc-c84eabad": {
"position": { "position": {
"x": -922.864188724591, "x": -1422.5355956832366,
"y": 204.9358257138463 "y": 280.0140805664038
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"vpc-331ad056": { "vpc-331ad056": {
"position": { "position": {
"x": -967.090481563079, "x": -1435.3398750652455,
"y": 1291.3678057051704 "y": 1386.2361491698744
}, },
"lx": -134.22057243218103, "lx": -134.22057243218103,
"ly": 551.6318031828098 "ly": 551.6318031828098
}, },
"vpc-afe722ca": { "vpc-afe722ca": {
"position": { "position": {
"x": 593.7116051419532, "x": -541.0511214212669,
"y": 2085.060815603146 "y": 3010.339213373424
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"vpc-03f799f47b766798c": { "vpc-03f799f47b766798c": {
"position": { "position": {
"x": -326.8836182123258, "x": 2417.1718307806645,
"y": 2966.579150844579 "y": 1690.0755318155339
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-260ee27f": { "subnet-260ee27f": {
"position": { "position": {
"x": -921.340481563079, "x": -1389.5898750652455,
"y": 1121.1067694739904 "y": 1215.9751129386946
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-f73afeae": { "subnet-f73afeae": {
"position": { "position": {
"x": -963.9358574966905, "x": -1432.185250998857,
"y": 1846.410916593678 "y": 1941.279260058381
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-ebc4188e": { "subnet-ebc4188e": {
"position": { "position": {
"x": 642.7195008673785, "x": -492.04322569584144,
"y": 1787.9702751209763 "y": 2713.2486728912545
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-005bfd58e719e031f": { "subnet-005bfd58e719e031f": {
"position": { "position": {
"x": 954.3699608755894, "x": 2795.865315418019,
"y": 767.1904595742493 "y": 521.623811391727
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-035f81142068bf961": { "subnet-035f81142068bf961": {
"position": { "position": {
"x": -727.9701282543923, "x": 2015.8700930454238,
"y": 2945.1772029052686 "y": 1673.9235838762236
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-d137fd88": { "subnet-d137fd88": {
"position": { "position": {
"x": 1035.1966337030872, "x": 2876.691988245517,
"y": 1260.4186551195244 "y": 1014.8520069370023
}, },
"lx": -95.54884268669878, "lx": -95.54884268669878,
"ly": -183.7839139599664 "ly": -183.7839139599664
}, },
"subnet-e98e088c": { "subnet-e98e088c": {
"position": { "position": {
"x": -556.0376711165936, "x": -1024.2870646187603,
"y": 1846.2285577580044 "y": 1941.0969012227074
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-08e40dcdc103438b8": { "subnet-08e40dcdc103438b8": {
"position": { "position": {
"x": 549.0070924119668, "x": 2390.5024469543973,
"y": 1405.912770994567 "y": 1160.3461228120448
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-0cb2aa7457413f5fe": { "subnet-0cb2aa7457413f5fe": {
"position": { "position": {
"x": -806.65058364074, "x": 1937.1896376590757,
"y": 3191.268048237053 "y": 1920.0144292080076
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-247a9341": { "subnet-247a9341": {
"position": { "position": {
"x": -675.3228356289688, "x": -1205.4162560440936,
"y": 122.98852970650637 "y": 178.27669594691727
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-34173f72": { "subnet-34173f72": {
"position": { "position": {
"x": -1080.405541820213, "x": -1548.6549353223795,
"y": 323.3831217211862 "y": 418.2514651858903
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-05b5dcdc91a8d3da2": { "subnet-05b5dcdc91a8d3da2": {
"position": { "position": {
"x": 550.3010345611413, "x": 2391.7963891035715,
"y": 1208.7638869426428 "y": 963.1972387601206
}, },
"lx": -56.280967047329966, "lx": -56.280967047329966,
"ly": 12.354358620145604 "ly": 12.354358620145604
}, },
"subnet-16594550": { "subnet-16594550": {
"position": { "position": {
"x": 638.2037094165278, "x": -496.5590171466923,
"y": 2341.8441019824586 "y": 3267.122499752737
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"subnet-051f6d705e415834d": { "subnet-051f6d705e415834d": {
"position": { "position": {
"x": 108.34330910685321, "x": 2852.1835304066703,
"y": 2965.3802332565056 "y": 1694.1266142274605
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-642cf701": { "sg-642cf701": {
"position": { "position": {
"x": 1368.280952213551, "x": -128.87428508740095,
"y": 1732.122796782028 "y": 3207.5801320931114
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
@ -170,8 +186,8 @@
}, },
"sg-da75adbf": { "sg-da75adbf": {
"position": { "position": {
"x": 1654.4153990786074, "x": 271.174147224467,
"y": 1502.4913097934757 "y": 2826.0633311754773
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
@ -202,224 +218,224 @@
}, },
"sg-0f2465c4421c2d5c2": { "sg-0f2465c4421c2d5c2": {
"position": { "position": {
"x": 1501.4220287756627, "x": 3737.1319279567347,
"y": 1088.2588218739315 "y": 1416.6180986742713
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-8b746aec": { "sg-8b746aec": {
"position": { "position": {
"x": 11.800399713295008, "x": -57.46491042921801,
"y": 754.3055340378875 "y": 798.8332334152173
}, },
"lx": -46.67202145388339, "lx": -46.67202145388339,
"ly": 75.49885823422312 "ly": 75.49885823422312
}, },
"sg-0a872ac3a6d9132a6": { "sg-0a872ac3a6d9132a6": {
"position": { "position": {
"x": -273.8173000765115, "x": 2449.2004574581915,
"y": 2531.372427285566 "y": 2220.1301602993412
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-076d2ea1df0054074": { "sg-076d2ea1df0054074": {
"position": { "position": {
"x": 297.4040219907455, "x": 3020.7762653398454,
"y": 2392.000439156539 "y": 2403.6912205594213
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-b45a2bd1": { "sg-b45a2bd1": {
"position": { "position": {
"x": 1496.2207074206092, "x": 3732.2813094098105,
"y": 700.5423557214449 "y": 823.5446930393971
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-4235ee27": { "sg-4235ee27": {
"position": { "position": {
"x": 1642.8599868444235, "x": 134.85579854853708,
"y": 2133.250105847895 "y": 3695.499049118454
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-04167d2e18ba15e45": { "sg-04167d2e18ba15e45": {
"position": { "position": {
"x": -660.0058544043945, "x": 2115.6486154236227,
"y": 2397.5181639300217 "y": 2405.8871924989144
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-06348763": { "sg-06348763": {
"position": { "position": {
"x": 23.18530161221225, "x": -770.8920039501696,
"y": 976.1080421357864 "y": 953.8441924471215
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-08fb4b412f90d5913": { "sg-08fb4b412f90d5913": {
"position": { "position": {
"x": 57.433554297476746, "x": -729.2224680353501,
"y": 1529.3600510391416 "y": 1385.8819086010787
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"sg-09621eafa81d9554d": { "sg-09621eafa81d9554d": {
"position": { "position": {
"x": 1492.9199816248513, "x": 3699.811020652474,
"y": 472.53158920519866 "y": 413.5355291862605
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-0dc281f8d162602c8": { "i-0dc281f8d162602c8": {
"position": { "position": {
"x": -1201.9240820025686, "x": -1670.173475504735,
"y": 1170.4054563966229 "y": 1265.2737998613272
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-0997a73b08f6e5862": { "i-0997a73b08f6e5862": {
"position": { "position": {
"x": 696.9537094165278, "x": -437.8090171466923,
"y": 2289.286847879602 "y": 3214.5652456498806
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-0c8100e3460fa8fd0": { "i-0c8100e3460fa8fd0": {
"position": { "position": {
"x": -530.5097131980381, "x": -998.7591067002047,
"y": 1487.4564852956444 "y": 1582.324828760348
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-041021cd89e15282c": { "i-041021cd89e15282c": {
"position": { "position": {
"x": 702.4695008673785, "x": -432.29322569584144,
"y": 1812.2202751209763 "y": 2737.4986728912545
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-073b97cbda2b200c3": { "i-073b97cbda2b200c3": {
"position": { "position": {
"x": 1032.1453149375961, "x": 2873.640669480026,
"y": 1118.630957918435 "y": 873.0643097359127
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-0669f35ab2d0fc444": { "i-0669f35ab2d0fc444": {
"position": { "position": {
"x": 1029.7295075033508, "x": 2871.2248620457804,
"y": 1292.025634730957 "y": 1046.458986548435
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-01b3f3cd57976bdf3": { "i-01b3f3cd57976bdf3": {
"position": { "position": {
"x": 1157.7479524685782, "x": 2999.2433070110083,
"y": 1450.706352320614 "y": 1205.139704138092
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-0272763b46610ac1b": { "i-0272763b46610ac1b": {
"position": { "position": {
"x": -1204.2644686266099, "x": -1672.5138621287765,
"y": 853.4607536515473 "y": 948.3290971162517
}, },
"lx": 238.46757499625102, "lx": 238.46757499625102,
"ly": -50.2036999992107 "ly": -50.2036999992107
}, },
"i-0c82adf476c7c5e32": { "i-0c82adf476c7c5e32": {
"position": { "position": {
"x": -1193.7978959064258, "x": -1662.0472894085924,
"y": 1010.1880963959779 "y": 1105.0564398606823
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-0636fd0b033c9b32a": { "i-0636fd0b033c9b32a": {
"position": { "position": {
"x": -515.2568811235894, "x": -983.5062746257561,
"y": 1298.8022809002794 "y": 1393.6706243649835
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-09241599c2590b66a": { "i-09241599c2590b66a": {
"position": { "position": {
"x": -555.6056787872835, "x": -1023.8550722894502,
"y": 886.9481918477713 "y": 981.8165353124757
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-0d7f643cb9d960645": { "i-0d7f643cb9d960645": {
"position": { "position": {
"x": 57.30327099761796, "x": 2801.1434922974354,
"y": 2832.8902534521053 "y": 1561.6366344230603
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"i-082b27477bbe6d8b5": { "i-082b27477bbe6d8b5": {
"position": { "position": {
"x": -666.2201282543923, "x": 2077.620093045424,
"y": 2969.4272029052686 "y": 1698.1735838762236
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"f8-db-01": { "f8-db-01": {
"position": { "position": {
"x": -518.2876711165936, "x": -986.5370646187603,
"y": 1864.4785577580044 "y": 1959.3469012227074
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"logon-db-02": { "logon-db-02": {
"position": { "position": {
"x": 694.8906262727602, "x": -439.8721002904598,
"y": 2442.9013560853155 "y": 3368.179753855594
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"vault-db-production-v2": { "vault-db-production-v2": {
"position": { "position": {
"x": 991.0522677808019, "x": 2832.5476223232313,
"y": 699.176929169678 "y": 453.61028098715565
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"vault-db-staging-v2": { "vault-db-staging-v2": {
"position": { "position": {
"x": 989.1876539703769, "x": 2830.6830085128063,
"y": 871.7039899788206 "y": 626.1373417962984
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0
}, },
"NodeAppALB": { "NodeAppALB": {
"position": { "position": {
"x": 282.38334721608845, "x": 3026.2235685159053,
"y": 3145.8702130609063 "y": 1874.6165940318608
}, },
"lx": 0, "lx": 0,
"ly": 0 "ly": 0