mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
504 lines
17 KiB
Markdown
504 lines
17 KiB
Markdown
---
|
|
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
|
|
|
|
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.
|
|
|
|
## 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
|
|
|
|
```yaml
|
|
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"
|
|
```
|
|
|
|
## AWS Resource Discovery Script
|
|
|
|
```bash
|
|
#!/usr/bin/env bash
|
|
# aws-asset-discovery.sh - Discover and inventory all AWS resources
|
|
|
|
OUTPUT_DIR="./asset-inventory/aws/$(date +%Y-%m-%d)"
|
|
mkdir -p "$OUTPUT_DIR"
|
|
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
|
|
|
|
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"
|
|
```
|
|
|
|
## 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
|
|
|
|
```yaml
|
|
# 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
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
- 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
|