2026-01-27 17:35:45 -05:00
---
name : asset-inventory
description : Maintain IT asset inventory and configuration management database. Track hardware, software, and cloud resources. Use when managing IT assets.
license : MIT
metadata :
author : devops-skills
version : "1.0"
---
# Asset Inventory
2026-03-24 18:02:50 -04:00
Maintain comprehensive IT asset inventory using automated discovery, AWS Config rules, cloud asset discovery scripts, CMDB integration, and tagging enforcement for compliance and operational visibility.
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
## When to Use
- Building or maintaining an IT asset inventory for compliance frameworks (ISO 27001, SOC 2, FedRAMP)
- Implementing automated cloud resource discovery across accounts and regions
- Enforcing tagging standards for cost allocation, ownership, and data classification
- Integrating asset data with a CMDB for operational workflows
- Preparing for audits that require a complete system component inventory
## Asset Categories and Schema
2026-01-27 17:35:45 -05:00
```yaml
2026-03-24 18:02:50 -04:00
asset_categories :
compute :
cloud :
- EC2 instances / Azure VMs / GCE instances
- Lambda functions / Azure Functions / Cloud Functions
- ECS/EKS clusters and tasks
- Container images in registries
on_premise :
- Physical servers
- Virtual machines (VMware, Hyper-V)
storage :
- S3 buckets / Azure Storage / GCS buckets
- EBS volumes / Managed Disks / Persistent Disks
- RDS instances / Azure SQL / Cloud SQL
- DynamoDB tables / Cosmos DB / Firestore
- EFS / Azure Files / Filestore
network :
- VPCs / VNets / VPC Networks
- Load balancers (ALB, NLB, Azure LB, GCP LB)
- DNS zones and records
- VPN gateways and connections
- CDN distributions
security :
- IAM users, roles, and policies
- KMS keys / Key Vault keys
- Certificates (ACM, Key Vault, Certificate Manager)
- Security groups / NSGs / Firewall rules
- WAF configurations
applications :
- SaaS subscriptions
- Licensed software
- Custom applications
- APIs and integrations
endpoints :
- Laptops and desktops
- Mobile devices
- Printers and peripherals
asset_record_schema :
required_fields :
asset_id : "Unique identifier (auto-generated)"
name : "Human-readable name"
type : "Category from above taxonomy"
provider : "AWS / Azure / GCP / On-Premise / SaaS"
account_or_subscription : "Cloud account ID"
region : "Deployment region/location"
owner : "Team or individual responsible"
data_classification : "Public / Internal / Confidential / Restricted"
environment : "Production / Staging / Development / Sandbox"
status : "Active / Decommissioning / Retired"
created_date : "When the asset was provisioned"
last_seen : "Last automated discovery timestamp"
optional_fields :
cost_center : "For cost allocation"
compliance_scope : "SOC2 / HIPAA / PCI / None"
backup_policy : "Backup schedule reference"
dr_tier : "Critical / Essential / Standard / Non-essential"
expiration_date : "For time-limited resources"
tags : "Key-value pairs from cloud provider"
dependencies : "Upstream and downstream services"
2026-01-27 17:35:45 -05:00
```
2026-03-24 18:02:50 -04:00
## AWS Resource Discovery Script
2026-01-27 17:35:45 -05:00
```bash
2026-03-24 18:02:50 -04:00
#!/usr/bin/env bash
# aws-asset-discovery.sh - Discover and inventory all AWS resources
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
OUTPUT_DIR = "./asset-inventory/aws/ $( date +%Y-%m-%d) "
mkdir -p " $OUTPUT_DIR "
ACCOUNT_ID = $( aws sts get-caller-identity --query Account --output text)
2026-01-27 17:35:45 -05:00
2026-03-24 18:02:50 -04:00
echo "=== AWS Asset Discovery for Account $ACCOUNT_ID ==="
# EC2 Instances
echo "--- EC2 Instances ---"
aws ec2 describe-instances \
--query 'Reservations[*].Instances[*].{
InstanceId:InstanceId,
Type:InstanceType,
State:State.Name,
AZ:Placement.AvailabilityZone,
VpcId:VpcId,
PrivateIP:PrivateIpAddress,
PublicIP:PublicIpAddress,
LaunchTime:LaunchTime,
Name:Tags[?Key==`Name`].Value|[0],
Owner:Tags[?Key==`Owner`].Value|[0],
Environment:Tags[?Key==`Environment`].Value|[0]
}' --output json | jq 'flatten' > " $OUTPUT_DIR /ec2-instances.json"
# RDS Databases
echo "--- RDS Instances ---"
aws rds describe-db-instances \
--query 'DBInstances[*].{
DBInstanceId:DBInstanceIdentifier,
Engine:Engine,
EngineVersion:EngineVersion,
Class:DBInstanceClass,
Status:DBInstanceStatus,
MultiAZ:MultiAZ,
Encrypted:StorageEncrypted,
Endpoint:Endpoint.Address,
BackupRetention:BackupRetentionPeriod
}' --output json > " $OUTPUT_DIR /rds-instances.json"
# S3 Buckets
echo "--- S3 Buckets ---"
aws s3api list-buckets --query 'Buckets[*].{Name:Name,Created:CreationDate}' --output json | \
jq -c '.[]' | while read -r bucket; do
name = $( echo " $bucket " | jq -r '.Name' )
region = $( aws s3api get-bucket-location --bucket " $name " --query 'LocationConstraint' --output text 2>/dev/null)
encryption = $( aws s3api get-bucket-encryption --bucket " $name " 2>/dev/null | jq -r '.ServerSideEncryptionConfiguration.Rules[0].ApplyServerSideEncryptionByDefault.SSEAlgorithm' 2>/dev/null)
versioning = $( aws s3api get-bucket-versioning --bucket " $name " --query 'Status' --output text 2>/dev/null)
echo "{\"Name\":\" $name \",\"Region\":\" ${ region :- us -east-1 } \",\"Encryption\":\" ${ encryption :- none } \",\"Versioning\":\" ${ versioning :- Disabled } \"}"
done | jq -s '.' > " $OUTPUT_DIR /s3-buckets.json"
# Lambda Functions
echo "--- Lambda Functions ---"
aws lambda list-functions \
--query 'Functions[*].{
Name:FunctionName,
Runtime:Runtime,
MemorySize:MemorySize,
Timeout:Timeout,
LastModified:LastModified,
CodeSize:CodeSize
}' --output json > " $OUTPUT_DIR /lambda-functions.json"
# VPCs and Security Groups
echo "--- VPCs ---"
aws ec2 describe-vpcs \
--query 'Vpcs[*].{
VpcId:VpcId,
CidrBlock:CidrBlock,
State:State,
IsDefault:IsDefault,
Name:Tags[?Key==`Name`].Value|[0]
}' --output json > " $OUTPUT_DIR /vpcs.json"
echo "--- Security Groups ---"
aws ec2 describe-security-groups \
--query 'SecurityGroups[*].{
GroupId:GroupId,
GroupName:GroupName,
VpcId:VpcId,
Description:Description,
IngressRuleCount:length(IpPermissions),
EgressRuleCount:length(IpPermissionsEgress)
}' --output json > " $OUTPUT_DIR /security-groups.json"
# IAM Users and Roles
echo "--- IAM Users ---"
aws iam list-users \
--query 'Users[*].{UserName:UserName,Created:CreateDate,PasswordLastUsed:PasswordLastUsed}' \
--output json > " $OUTPUT_DIR /iam-users.json"
echo "--- IAM Roles ---"
aws iam list-roles \
--query 'Roles[*].{RoleName:RoleName,Created:CreateDate,LastUsed:RoleLastUsed.LastUsedDate}' \
--output json > " $OUTPUT_DIR /iam-roles.json"
# EKS Clusters
echo "--- EKS Clusters ---"
aws eks list-clusters --query 'clusters' --output json | jq -r '.[]' | while read -r cluster; do
aws eks describe-cluster --name " $cluster " \
--query 'cluster.{Name:name,Version:version,Status:status,Endpoint:endpoint,Created:createdAt}'
done | jq -s '.' > " $OUTPUT_DIR /eks-clusters.json" 2>/dev/null
# KMS Keys
echo "--- KMS Keys ---"
aws kms list-keys --query 'Keys[*].KeyId' --output text | tr '\t' '\n' | while read -r key_id; do
aws kms describe-key --key-id " $key_id " \
--query 'KeyMetadata.{KeyId:KeyId,Description:Description,State:KeyState,Created:CreationDate,Manager:KeyManager}' 2>/dev/null
done | jq -s '.' > " $OUTPUT_DIR /kms-keys.json"
# Generate summary
echo "=== Inventory Summary ==="
echo "EC2 Instances: $( jq 'length' " $OUTPUT_DIR /ec2-instances.json" ) "
echo "RDS Instances: $( jq 'length' " $OUTPUT_DIR /rds-instances.json" ) "
echo "S3 Buckets: $( jq 'length' " $OUTPUT_DIR /s3-buckets.json" ) "
echo "Lambda Functions: $( jq 'length' " $OUTPUT_DIR /lambda-functions.json" ) "
echo "VPCs: $( jq 'length' " $OUTPUT_DIR /vpcs.json" ) "
echo "Security Groups: $( jq 'length' " $OUTPUT_DIR /security-groups.json" ) "
echo "IAM Users: $( jq 'length' " $OUTPUT_DIR /iam-users.json" ) "
echo "IAM Roles: $( jq 'length' " $OUTPUT_DIR /iam-roles.json" ) "
echo "Inventory saved to $OUTPUT_DIR "
2026-01-27 17:35:45 -05:00
```
2026-03-24 18:02:50 -04:00
## AWS Config Rules for Inventory Compliance
```bash
# Enable AWS Config recorder
aws configservice put-configuration-recorder \
--configuration-recorder name = default,roleARN= arn:aws:iam::123456789012:role/aws-config-role \
--recording-group allSupported = true,includeGlobalResourceTypes= true
# Start recording
aws configservice start-configuration-recorder --configuration-recorder-name default
# Enable required-tags Config rule
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "required-tags",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "REQUIRED_TAGS"
},
"InputParameters": "{\"tag1Key\":\"Owner\",\"tag2Key\":\"Environment\",\"tag3Key\":\"CostCenter\",\"tag4Key\":\"DataClassification\"}",
"Scope": {
"ComplianceResourceTypes": [
"AWS::EC2::Instance",
"AWS::RDS::DBInstance",
"AWS::S3::Bucket",
"AWS::Lambda::Function"
]
}
}'
# Config rule for encryption compliance
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "encrypted-volumes",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "ENCRYPTED_VOLUMES"
}
}'
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "rds-storage-encrypted",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "RDS_STORAGE_ENCRYPTED"
}
}'
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "s3-bucket-server-side-encryption-enabled",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
}
}'
# Query AWS Config for all resources of a type
aws configservice list-discovered-resources --resource-type AWS::EC2::Instance
aws configservice list-discovered-resources --resource-type AWS::RDS::DBInstance
# Advanced query with AWS Config SQL
aws configservice select -resource-config \
--expression "SELECT resourceId, resourceType, tags, configuration.instanceType
WHERE resourceType = 'AWS::EC2::Instance'
AND tags.tag('Environment') = 'production'"
# Get compliance summary
aws configservice get-compliance-summary-by-config-rule
aws configservice get-compliance-summary-by-resource-type
```
## Tagging Enforcement
2026-01-27 17:35:45 -05:00
```yaml
2026-03-24 18:02:50 -04:00
# AWS Tag Policy (applied via AWS Organizations)
tag_policy :
tags :
Owner :
tag_key :
"@@assign": "Owner"
enforced_for :
"@@assign" :
- "ec2:instance"
- "rds:db"
- "s3:bucket"
- "lambda:function"
Environment :
tag_key :
"@@assign": "Environment"
tag_value :
"@@assign" :
- "production"
- "staging"
- "development"
- "sandbox"
DataClassification :
tag_key :
"@@assign": "DataClassification"
tag_value :
"@@assign" :
- "public"
- "internal"
- "confidential"
- "restricted"
CostCenter :
tag_key :
"@@assign": "CostCenter"
```
```hcl
# Terraform - Enforce tags on all resources with default_tags
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
ManagedBy = "terraform"
Environment = var . environment
Owner = var . team_name
CostCenter = var . cost_center
DataClassification = var . data_classification
}
}
}
```
## Multi-Cloud Discovery
```bash
#!/usr/bin/env bash
# multi-cloud-discovery.sh - Discover assets across AWS, Azure, and GCP
OUTPUT_DIR = "./asset-inventory/multi-cloud/ $( date +%Y-%m-%d) "
mkdir -p " $OUTPUT_DIR "
echo "=== Multi-Cloud Asset Discovery ==="
# AWS - using Resource Groups Tagging API
echo "--- AWS Resources ---"
aws resourcegroupstaggingapi get-resources \
--query 'ResourceTagMappingList[*].{ARN:ResourceARN,Tags:Tags}' \
--output json > " $OUTPUT_DIR /aws-all-resources.json"
echo "AWS resources: $( jq 'length' " $OUTPUT_DIR /aws-all-resources.json" ) "
# Azure - using Resource Graph
echo "--- Azure Resources ---"
az graph query -q "Resources | project name, type, location, resourceGroup, subscriptionId, tags" \
--output json > " $OUTPUT_DIR /azure-all-resources.json" 2>/dev/null
# GCP - using Cloud Asset Inventory
echo "--- GCP Resources ---"
gcloud asset search-all-resources \
--scope= "organizations/ORG_ID" \
--format= json > " $OUTPUT_DIR /gcp-all-resources.json" 2>/dev/null
# Find untagged resources
echo "=== Untagged Resources ==="
jq '[.[] | select(.Tags == null or .Tags == [])] | length' " $OUTPUT_DIR /aws-all-resources.json"
echo "Discovery complete. Results in $OUTPUT_DIR "
```
## CMDB Integration
```python
"""
CMDB sync script - Normalize cloud assets and push to CMDB API.
"""
import json
import requests
from datetime import datetime , timezone
class CMDBSync :
def __init__ ( self , cmdb_url , api_token ):
self . cmdb_url = cmdb_url
self . headers = {
"Authorization" : f "Bearer { api_token } " ,
"Content-Type" : "application/json" ,
}
def normalize_aws_instance ( self , instance ):
"""Convert AWS EC2 instance to common asset schema."""
tags = { t [ "Key" ]: t [ "Value" ] for t in ( instance . get ( "Tags" ) or [])}
return {
"asset_id" : f "aws: { instance [ 'InstanceId' ] } " ,
"name" : tags . get ( "Name" , instance [ "InstanceId" ]),
"type" : "compute" ,
"provider" : "aws" ,
"region" : instance . get ( "AZ" , "unknown" )[: - 1 ],
"configuration" : {
"instance_type" : instance . get ( "Type" ),
"state" : instance . get ( "State" ),
"vpc_id" : instance . get ( "VpcId" ),
},
"owner" : tags . get ( "Owner" , "unassigned" ),
"environment" : tags . get ( "Environment" , "unknown" ),
"data_classification" : tags . get ( "DataClassification" , "unknown" ),
"status" : "active" if instance . get ( "State" ) == "running" else "stopped" ,
"last_seen" : datetime . now ( timezone . utc ) . isoformat (),
}
def sync_assets ( self , assets ):
"""Push normalized assets to CMDB."""
results = { "created" : 0 , "updated" : 0 , "errors" : 0 }
for asset in assets :
try :
resp = requests . get (
f " { self . cmdb_url } /assets/ { asset [ 'asset_id' ] } " ,
headers = self . headers ,
)
if resp . status_code == 200 :
requests . put (
f " { self . cmdb_url } /assets/ { asset [ 'asset_id' ] } " ,
headers = self . headers ,
json = asset ,
)
results [ "updated" ] += 1
else :
requests . post (
f " { self . cmdb_url } /assets" ,
headers = self . headers ,
json = asset ,
)
results [ "created" ] += 1
except Exception :
results [ "errors" ] += 1
return results
```
## Asset Inventory Checklist
```yaml
asset_inventory_checklist :
discovery :
- [ ] Automated discovery scripts running for all cloud accounts
- [ ] Discovery covers all resource types (compute, storage, network, IAM)
- [ ] Multi-region discovery enabled
- [ ] On -premise assets cataloged
- [ ] SaaS subscriptions inventoried
- [ ] Discovery runs daily (minimum weekly)
classification :
- [ ] Required tags defined (Owner, Environment, DataClassification, CostCenter)
- [ ] Tag enforcement via AWS Organizations tag policies
- [ ] Tag enforcement via Terraform default_tags
- [ ] Tag enforcement via CI/CD policy checks (Checkov, OPA)
- [ ] Untagged resource reports generated and tracked
configuration_management :
- [ ] AWS Config enabled in all regions
- [ ] Config rules enforce encryption, tagging, and security baselines
- [ ] Configuration compliance summary reviewed weekly
- [ ] Drift detection enabled for IaC-managed resources
cmdb :
- [ ] CMDB sync automated from cloud discovery
- [ ] Common schema defined across all providers
- [ ] Reconciliation process identifies orphaned records
- [ ] New resources auto-assigned default owner
- [ ] Asset lifecycle tracked (created, active, decommissioning, retired)
governance :
- [ ] Asset owners assigned and current
- [ ] Quarterly inventory reconciliation conducted
- [ ] Compliance scope tagging accurate (SOC2, HIPAA, PCI)
- [ ] Asset inventory available for auditor review
- [ ] Decommissioned assets tracked for data retention compliance
2026-01-27 17:35:45 -05:00
```
## Best Practices
2026-03-24 18:02:50 -04:00
- Automate discovery rather than relying on manual inventory: cloud environments change too fast for spreadsheets
- Use AWS Config, Azure Resource Graph, and GCP Cloud Asset Inventory as authoritative data sources
- Enforce tagging at provisioning time through IaC defaults and policy-as-code guardrails
- Assign every asset an owner: unowned resources become security and cost liabilities
- Reconcile inventory regularly and investigate orphaned assets (CMDB record with no real resource and vice versa)
- Track data classification as a mandatory tag to support compliance scoping decisions
- Maintain asset lifecycle states to distinguish active resources from those being decommissioned
- Integrate asset inventory with incident response to quickly identify affected systems during investigations
- Export inventory data for compliance audits in accessible formats (CSV, JSON)
- Review untagged and unclassified resource reports weekly to maintain inventory quality