Param(
[string]$Region = "ap-northeast-1",
[string]$OutputDir = "./iam-full-export",
[switch]$ExportPolicyDocuments = $false
)
<#
IAM Full Inventory Export Script with CSV Output
- AWS CLI only
- Windows PowerShell 5.x compatible
#>
# ------------------------------------------------------------
# Log 出力関数
# ------------------------------------------------------------
function Write-Log {
param([string]$Message)
$timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
$line = "[$timestamp] $Message"
Write-Host $line
Add-Content -Path $LogFile -Value $line
}
# ------------------------------------------------------------
# JSON to CSV 変換関数
# ------------------------------------------------------------
function Convert-JsonToCsv {
param(
[string]$JsonPath,
[string]$CsvPath,
[string]$RootNode = ""
)
try {
if (Test-Path $JsonPath) {
$jsonData = Get-Content $JsonPath -Raw | ConvertFrom-Json
if ($RootNode -ne "") {
$data = $jsonData.$RootNode
} else {
$data = $jsonData
}
if ($data -and $data.Count -gt 0) {
$data | Export-Csv -Path $CsvPath -NoTypeInformation -Encoding UTF8
Write-Log "CSV出力: $CsvPath"
}
}
}
catch {
Write-Log "ERROR: CSV変換失敗 - $JsonPath - $($_.Exception.Message)"
}
}
function Convert-UserDetailsToCsv {
param([string]$UserDir, [string]$UserName)
$csvData = @()
# MFA デバイス
$mfaPath = "$UserDir\mfa.json"
if (Test-Path $mfaPath) {
$mfaData = Get-Content $mfaPath -Raw | ConvertFrom-Json
foreach ($device in $mfaData.MFADevices) {
$csvData += [PSCustomObject]@{
UserName = $UserName
DataType = "MFA"
DeviceName = $device.SerialNumber
EnableDate = $device.EnableDate
Status = "Active"
}
}
}
# アクセスキー
$keysPath = "$UserDir\access-keys.json"
if (Test-Path $keysPath) {
$keysData = Get-Content $keysPath -Raw | ConvertFrom-Json
foreach ($key in $keysData.AccessKeyMetadata) {
$csvData += [PSCustomObject]@{
UserName = $UserName
DataType = "AccessKey"
AccessKeyId = $key.AccessKeyId
Status = $key.Status
CreateDate = $key.CreateDate
}
}
}
# グループ
$groupsPath = "$UserDir\groups.json"
if (Test-Path $groupsPath) {
$groupsData = Get-Content $groupsPath -Raw | ConvertFrom-Json
foreach ($group in $groupsData.Groups) {
$csvData += [PSCustomObject]@{
UserName = $UserName
DataType = "Group"
GroupName = $group.GroupName
GroupId = $group.GroupId
}
}
}
# インラインポリシー
$inlinePath = "$UserDir\inline-policies.json"
if (Test-Path $inlinePath) {
$inlineData = Get-Content $inlinePath -Raw | ConvertFrom-Json
foreach ($policy in $inlineData.PolicyNames) {
$csvData += [PSCustomObject]@{
UserName = $UserName
DataType = "InlinePolicy"
PolicyName = $policy
}
}
}
# マネージドポリシー
$managedPath = "$UserDir\managed-policies.json"
if (Test-Path $managedPath) {
$managedData = Get-Content $managedPath -Raw | ConvertFrom-Json
foreach ($policy in $managedData.AttachedPolicies) {
$csvData += [PSCustomObject]@{
UserName = $UserName
DataType = "ManagedPolicy"
PolicyName = $policy.PolicyName
PolicyArn = $policy.PolicyArn
}
}
}
if ($csvData.Count -gt 0) {
$csvPath = "$UserDir\user-details.csv"
$csvData | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
}
}
# ------------------------------------------------------------
# 初期設定
# ------------------------------------------------------------
$Date = Get-Date -Format "yyyyMMdd_HHmmss"
$Output = Join-Path $OutputDir "export_$Date"
# 出力フォルダ作成
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
New-Item -ItemType Directory -Path $Output | Out-Null
# ログ開始
$LogFile = "$Output\export.log"
Write-Log "=== IAM Full Inventory Export Start ==="
Write-Log "Region : $Region"
Write-Log "Output Directory : $Output"
# AWS CLI 対応リージョンを設定
$env:AWS_DEFAULT_REGION = $Region
# AWS CLI 動作チェック
try {
aws sts get-caller-identity | Out-Null
Write-Log "AWS CLI OK: Caller identity confirmed"
} catch {
Write-Log "ERROR: AWS CLI 実行不可"
exit 1
}
# ------------------------------------------------------------
# Part 2: IAM 基本情報 (CSV出力)
# ------------------------------------------------------------
Write-Log "Part2: IAM 基本情報取得開始..."
# JSON 取得
aws iam list-users > "$Output\list-users.json"
aws iam list-roles > "$Output\list-roles.json"
aws iam list-groups > "$Output\list-groups.json"
aws iam list-policies --scope Local > "$Output\list-policies.json"
# CSV 変換
Convert-JsonToCsv -JsonPath "$Output\list-users.json" -CsvPath "$Output\users.csv" -RootNode "Users"
Convert-JsonToCsv -JsonPath "$Output\list-roles.json" -CsvPath "$Output\roles.csv" -RootNode "Roles"
Convert-JsonToCsv -JsonPath "$Output\list-groups.json" -CsvPath "$Output\groups.csv" -RootNode "Groups"
Convert-JsonToCsv -JsonPath "$Output\list-policies.json" -CsvPath "$Output\policies.csv" -RootNode "Policies"
Write-Log "Part2 完了"
# ------------------------------------------------------------
# Part 3: Users の詳細 (CSV出力)
# ------------------------------------------------------------
Write-Log "Part3: IAM Users 詳細取得..."
$Users = (Get-Content "$Output\list-users.json" | ConvertFrom-Json).Users
$UserDetailDir = "$Output\users"
New-Item -ItemType Directory -Path $UserDetailDir | Out-Null
# ユーザー詳細CSV用の配列
$allUserDetails = @()
foreach ($u in $Users) {
$name = $u.UserName
$Dir = "$UserDetailDir\$name"
New-Item -ItemType Directory -Path $Dir | Out-Null
Write-Log "User: $name"
# JSON 取得
aws iam list-mfa-devices --user-name $name > "$Dir\mfa.json"
aws iam list-access-keys --user-name $name > "$Dir\access-keys.json"
aws iam list-groups-for-user --user-name $name > "$Dir\groups.json"
aws iam list-user-policies --user-name $name > "$Dir\inline-policies.json"
aws iam list-attached-user-policies --user-name $name > "$Dir\managed-policies.json"
# 詳細データをCSVに変換
Convert-UserDetailsToCsv -UserDir $Dir -UserName $name
# 統合CSV用データ収集
$userDetail = [PSCustomObject]@{
UserName = $name
UserId = $u.UserId
Arn = $u.Arn
CreateDate = $u.CreateDate
MFA_Devices = (Get-Content "$Dir\mfa.json" | ConvertFrom-Json).MFADevices.Count
AccessKeys = (Get-Content "$Dir\access-keys.json" | ConvertFrom-Json).AccessKeyMetadata.Count
Groups = (Get-Content "$Dir\groups.json" | ConvertFrom-Json).Groups.Count
InlinePolicies = (Get-Content "$Dir\inline-policies.json" | ConvertFrom-Json).PolicyNames.Count
ManagedPolicies = (Get-Content "$Dir\managed-policies.json" | ConvertFrom-Json).AttachedPolicies.Count
}
$allUserDetails += $userDetail
}
# ユーザーサマリーCSV
if ($allUserDetails.Count -gt 0) {
$allUserDetails | Export-Csv -Path "$Output\users-summary.csv" -NoTypeInformation -Encoding UTF8
}
Write-Log "Part3 完了"
# ------------------------------------------------------------
# Part 4: Roles の詳細 (CSV出力)
# ------------------------------------------------------------
Write-Log "Part4: IAM Roles 詳細取得..."
$Roles = (Get-Content "$Output\list-roles.json" | ConvertFrom-Json).Roles
$RoleDetailDir = "$Output\roles"
New-Item -ItemType Directory -Path $RoleDetailDir | Out-Null
$allRoleDetails = @()
foreach ($r in $Roles) {
$name = $r.RoleName
$Dir = "$RoleDetailDir\$name"
New-Item -ItemType Directory -Path $Dir | Out-Null
Write-Log "Role: $name"
# JSON 取得
aws iam list-role-policies --role-name $name > "$Dir\inline-policies.json"
aws iam list-attached-role-policies --role-name $name > "$Dir\managed-policies.json"
# ロール詳細データ
$roleDetail = [PSCustomObject]@{
RoleName = $name
RoleId = $r.RoleId
Arn = $r.Arn
CreateDate = $r.CreateDate
Description = $r.Description
InlinePolicies = (Get-Content "$Dir\inline-policies.json" | ConvertFrom-Json).PolicyNames.Count
ManagedPolicies = (Get-Content "$Dir\managed-policies.json" | ConvertFrom-Json).AttachedPolicies.Count
AssumeRolePolicy = if ($r.AssumeRolePolicyDocument) { "Yes" } else { "No" }
}
$allRoleDetails += $roleDetail
}
# ロールサマリーCSV
if ($allRoleDetails.Count -gt 0) {
$allRoleDetails | Export-Csv -Path "$Output\roles-summary.csv" -NoTypeInformation -Encoding UTF8
}
Write-Log "Part4 完了"
# ------------------------------------------------------------
# Part 5: Credential Report (CSV)
# ------------------------------------------------------------
Write-Log "Part5: Credential Report生成..."
aws iam generate-credential-report | Out-Null
Start-Sleep 2
aws iam get-credential-report --query Content --output text |
Out-File -Encoding ascii "$Output\credential-report.csv"
Write-Log "Part5 完了"
# ------------------------------------------------------------
# Part 6: ポリシー文書 (CSV出力)
# ------------------------------------------------------------
Write-Log "Part6: ポリシー文書処理..."
$Policies = (Get-Content "$Output\list-policies.json" | ConvertFrom-Json).Policies
$PolicyDir = "$Output\policies"
New-Item -ItemType Directory -Path $PolicyDir | Out-Null
$allPolicyDetails = @()
foreach ($p in $Policies) {
$name = $p.PolicyName
$arn = $p.Arn
Write-Log "Policy: $name"
$policyDetail = [PSCustomObject]@{
PolicyName = $name
PolicyId = $p.PolicyId
Arn = $arn
CreateDate = $p.CreateDate
UpdateDate = $p.UpdateDate
Description = if ($p.Description) { $p.Description } else { "" }
IsAttachable = $p.IsAttachable
Path = $p.Path
DefaultVersionId = $p.DefaultVersionId
Scope = if ($arn -like "arn:aws:iam::aws:policy/*") { "AWS Managed" } else { "Customer Managed" }
}
$allPolicyDetails += $policyDetail
if ($ExportPolicyDocuments) {
$safeName = $name.Replace(":", "_").Replace("/", "_")
$DocDir = "$PolicyDir\$safeName"
New-Item -ItemType Directory -Path $DocDir | Out-Null
try {
$meta = aws iam get-policy --policy-arn $arn | ConvertFrom-Json
$version = $meta.Policy.DefaultVersionId
aws iam get-policy-version --policy-arn $arn --version-id $version > "$DocDir\policy-$version.json"
# ポリシードキュメントの情報をCSVに追加
$policyDoc = Get-Content "$DocDir\policy-$version.json" -Raw | ConvertFrom-Json
$policyDetail | Add-Member -NotePropertyName "DocumentVersion" -NotePropertyValue $version -Force
$policyDetail | Add-Member -NotePropertyName "DocumentStatementCount" -NotePropertyValue $policyDoc.PolicyVersion.Document.Statement.Count -Force
} catch {
Write-Log "WARNING: ポリシードキュメント取得失敗 - $name"
}
}
}
# ポリシーサマリーCSV
if ($allPolicyDetails.Count -gt 0) {
$allPolicyDetails | Export-Csv -Path "$Output\policies-summary.csv" -NoTypeInformation -Encoding UTF8
}
Write-Log "Part6 完了"
# ------------------------------------------------------------
# Part 7: Access Advisor (CSV出力)
# ------------------------------------------------------------
Write-Log "Part7: Access Advisor 処理..."
$AccessDir = "$Output\access-advisor"
New-Item -ItemType Directory -Path $AccessDir | Out-Null
$allAccessData = @()
foreach ($u in $Users) {
Write-Log "AccessAdvisor User: $($u.UserName)"
try {
$jobResult = aws iam generate-service-last-accessed-details --arn $u.Arn | ConvertFrom-Json
$jobId = $jobResult.JobId
# ジョブ完了待機(簡易版)
Start-Sleep -Seconds 3
$accessDetails = aws iam get-service-last-accessed-details --job-id $jobId | ConvertFrom-Json
foreach ($service in $accessDetails.ServicesLastAccessed) {
$accessData = [PSCustomObject]@{
EntityType = "User"
EntityName = $u.UserName
ServiceName = $service.ServiceName
LastAccessed = if ($service.LastAuthenticated) { $service.LastAuthenticated } else { "Never" }
ServiceNamespace = $service.ServiceNamespace
TotalAuthenticatedEntities = $service.TotalAuthenticatedEntities
JobId = $jobId
JobCompletionDate = $accessDetails.JobCompletionDate
}
$allAccessData += $accessData
}
} catch {
Write-Log "WARNING: Access Advisor 取得失敗 - User: $($u.UserName)"
}
}
foreach ($r in $Roles) {
Write-Log "AccessAdvisor Role: $($r.RoleName)"
try {
$jobResult = aws iam generate-service-last-accessed-details --arn $r.Arn | ConvertFrom-Json
$jobId = $jobResult.JobId
Start-Sleep -Seconds 3
$accessDetails = aws iam get-service-last-accessed-details --job-id $jobId | ConvertFrom-Json
foreach ($service in $accessDetails.ServicesLastAccessed) {
$accessData = [PSCustomObject]@{
EntityType = "Role"
EntityName = $r.RoleName
ServiceName = $service.ServiceName
LastAccessed = if ($service.LastAuthenticated) { $service.LastAuthenticated } else { "Never" }
ServiceNamespace = $service.ServiceNamespace
TotalAuthenticatedEntities = $service.TotalAuthenticatedEntities
JobId = $jobId
JobCompletionDate = $accessDetails.JobCompletionDate
}
$allAccessData += $accessData
}
} catch {
Write-Log "WARNING: Access Advisor 取得失敗 - Role: $($r.RoleName)"
}
}
# Access Advisor CSV
if ($allAccessData.Count -gt 0) {
$allAccessData | Export-Csv -Path "$Output\access-advisor.csv" -NoTypeInformation -Encoding UTF8
}
Write-Log "Part7 完了"
# ------------------------------------------------------------
# Part 8: Groups の詳細 (CSV出力)
# ------------------------------------------------------------
Write-Log "Part8: IAM Groups 詳細取得..."
$Groups = (Get-Content "$Output\list-groups.json" | ConvertFrom-Json).Groups
$GroupDetailDir = "$Output\groups"
New-Item -ItemType Directory -Path $GroupDetailDir | Out-Null
$allGroupDetails = @()
$allGroupMemberships = @()
foreach ($g in $Groups) {
$name = $g.GroupName
$Dir = "$GroupDetailDir\$name"
New-Item -ItemType Directory -Path $Dir | Out-Null
Write-Log "Group: $name"
# JSON 取得
aws iam get-group --group-name $name > "$Dir\users.json"
aws iam list-group-policies --group-name $name > "$Dir\inline.json"
aws iam list-attached-group-policies --group-name $name > "$Dir\managed.json"
# グループ詳細データ
$usersData = Get-Content "$Dir\users.json" -Raw | ConvertFrom-Json
$inlineData = Get-Content "$Dir\inline.json" -Raw | ConvertFrom-Json
$managedData = Get-Content "$Dir\managed.json" -Raw | ConvertFrom-Json
$groupDetail = [PSCustomObject]@{
GroupName = $name
GroupId = $g.GroupId
Arn = $g.Arn
CreateDate = $g.CreateDate
Path = $g.Path
MemberCount = $usersData.Users.Count
InlinePolicyCount = $inlineData.PolicyNames.Count
ManagedPolicyCount = $managedData.AttachedPolicies.Count
}
$allGroupDetails += $groupDetail
# グループメンバーシップデータ
foreach ($user in $usersData.Users) {
$membershipData = [PSCustomObject]@{
GroupName = $name
UserName = $user.UserName
UserId = $user.UserId
UserArn = $user.Arn
MembershipType = "Direct"
}
$allGroupMemberships += $membershipData
}
}
# グループサマリーCSV
if ($allGroupDetails.Count -gt 0) {
$allGroupDetails | Export-Csv -Path "$Output\groups-summary.csv" -NoTypeInformation -Encoding UTF8
}
# グループメンバーシップCSV
if ($allGroupMemberships.Count -gt 0) {
$allGroupMemberships | Export-Csv -Path "$Output\group-memberships.csv" -NoTypeInformation -Encoding UTF8
}
Write-Log "Part8 完了"
# ------------------------------------------------------------
# Part 9: ポリシーアタッチメント (CSV出力)
# ------------------------------------------------------------
Write-Log "Part9: ポリシーアタッチメント処理..."
$allPolicyAttachments = @()
foreach ($policy in $Policies) {
try {
$entities = aws iam list-entities-for-policy --policy-arn $policy.Arn | ConvertFrom-Json
# ユーザーアタッチメント
foreach ($user in $entities.PolicyUsers) {
$attachmentData = [PSCustomObject]@{
PolicyName = $policy.PolicyName
PolicyArn = $policy.Arn
EntityType = "User"
EntityName = $user.UserName
EntityId = $user.UserId
}
$allPolicyAttachments += $attachmentData
}
# グループアタッチメント
foreach ($group in $entities.PolicyGroups) {
$attachmentData = [PSCustomObject]@{
PolicyName = $policy.PolicyName
PolicyArn = $policy.Arn
EntityType = "Group"
EntityName = $group.GroupName
EntityId = $group.GroupId
}
$allPolicyAttachments += $attachmentData
}
# ロールアタッチメント
foreach ($role in $entities.PolicyRoles) {
$attachmentData = [PSCustomObject]@{
PolicyName = $policy.PolicyName
PolicyArn = $policy.Arn
EntityType = "Role"
EntityName = $role.RoleName
EntityId = $role.RoleId
}
$allPolicyAttachments += $attachmentData
}
} catch {
Write-Log "WARNING: ポリシーアタッチメント取得失敗 - $($policy.PolicyName)"
}
}
# ポリシーアタッチメントCSV
if ($allPolicyAttachments.Count -gt 0) {
$allPolicyAttachments | Export-Csv -Path "$Output\policy-attachments.csv" -NoTypeInformation -Encoding UTF8
}
Write-Log "Part9 完了"
# ------------------------------------------------------------
# Part 10: 最終サマリーと統計 (CSV出力) - 続き
# ------------------------------------------------------------
# 統計データの収集
$usersWithMFA = 0
$usersWithAccessKeys = 0
$usersWithConsoleAccess = 0
# $inactiveUsers = 0
foreach ($userDir in (Get-ChildItem "$UserDetailDir\*" -Directory)) {
$mfaData = Get-Content "$userDir\mfa.json" -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
$accessKeysData = Get-Content "$userDir\access-keys.json" -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
$loginProfile = $null
try {
$loginProfile = aws iam get-login-profile --user-name (Split-Path $userDir -Leaf) 2>$null | ConvertFrom-Json
} catch {}
if ($mfaData -and $mfaData.MFADevices.Count -gt 0) { $usersWithMFA++ }
if ($accessKeysData -and $accessKeysData.AccessKeyMetadata.Count -gt 0) { $usersWithAccessKeys++ }
if ($loginProfile) { $usersWithConsoleAccess++ }
}
# 最終サマリーデータ
$summaryStats = [PSCustomObject]@{
ExportDate = $Date
Region = $Region
TotalExecutionMinutes = [math]::Round($executionTime.TotalMinutes, 2)
TotalUsers = $Users.Count
TotalRoles = $Roles.Count
TotalGroups = $Groups.Count
TotalPolicies = $Policies.Count
UsersWithMFA = $usersWithMFA
UsersWithAccessKeys = $usersWithAccessKeys
UsersWithConsoleAccess = $usersWithConsoleAccess
MFAEnforcementRate = if ($Users.Count -gt 0) { [math]::Round(($usersWithMFA / $Users.Count) * 100, 2) } else { 0 }
AccessKeyUsageRate = if ($Users.Count -gt 0) { [math]::Round(($usersWithAccessKeys / $Users.Count) * 100, 2) } else { 0 }
TotalPolicyAttachments = $allPolicyAttachments.Count
TotalAccessAdvisorRecords = $allAccessData.Count
TotalGroupMemberships = $allGroupMemberships.Count
ExportPolicyDocuments = $ExportPolicyDocuments
OutputDirectory = $Output
}
# セキュリティ状態の分析
$securityAnalysis = @()
# MFA状態
$securityAnalysis += [PSCustomObject]@{
Category = "MFA"
Metric = "MFA有効ユーザー数"
Value = $usersWithMFA
Total = $Users.Count
Percentage = if ($Users.Count -gt 0) { [math]::Round(($usersWithMFA / $Users.Count) * 100, 2) } else { 0 }
Status = if (($usersWithMFA / $Users.Count) -ge 0.9) { "Good" } elseif (($usersWithMFA / $Users.Count) -ge 0.7) { "Fair" } else { "Poor" }
}
# アクセスキー状態
$securityAnalysis += [PSCustomObject]@{
Category = "AccessKeys"
Metric = "アクセスキー保有ユーザー数"
Value = $usersWithAccessKeys
Total = $Users.Count
Percentage = if ($Users.Count -gt 0) { [math]::Round(($usersWithAccessKeys / $Users.Count) * 100, 2) } else { 0 }
Status = if (($usersWithAccessKeys / $Users.Count) -le 0.5) { "Good" } elseif (($usersWithAccessKeys / $Users.Count) -le 0.7) { "Fair" } else { "Poor" }
}
# コンソールアクセス状態
$securityAnalysis += [PSCustomObject]@{
Category = "ConsoleAccess"
Metric = "コンソールアクセス可能ユーザー数"
Value = $usersWithConsoleAccess
Total = $Users.Count
Percentage = if ($Users.Count -gt 0) { [math]::Round(($usersWithConsoleAccess / $Users.Count) * 100, 2) } else { 0 }
Status = if (($usersWithConsoleAccess / $Users.Count) -le 0.3) { "Good" } elseif (($usersWithConsoleAccess / $Users.Count) -le 0.5) { "Fair" } else { "Poor" }
}
# ポリシー状態
$securityAnalysis += [PSCustomObject]@{
Category = "Policies"
Metric = "カスタマーポリシー数"
Value = ($Policies | Where-Object { $_.Arn -notlike "arn:aws:iam::aws:policy/*" }).Count
Total = $Policies.Count
Percentage = if ($Policies.Count -gt 0) { [math]::Round((($Policies | Where-Object { $_.Arn -notlike "arn:aws:iam::aws:policy/*" }).Count / $Policies.Count) * 100, 2) } else { 0 }
Status = if (($Policies | Where-Object { $_.Arn -notlike "arn:aws:iam::aws:policy/*" }).Count -le 50) { "Good" } elseif (($Policies | Where-Object { $_.Arn -notlike "arn:aws:iam::aws:policy/*" }).Count -le 100) { "Fair" } else { "Poor" }
}
# CSVファイル出力
$summaryStats | Export-Csv -Path "$Output\execution-summary.csv" -NoTypeInformation -Encoding UTF8
$securityAnalysis | Export-Csv -Path "$Output\security-analysis.csv" -NoTypeInformation -Encoding UTF8
# JSON形式でも出力(分析用)
$summaryStats | ConvertTo-Json -Depth 3 | Out-File -Path "$Output\execution-summary.json" -Encoding UTF8
$securityAnalysis | ConvertTo-Json -Depth 3 | Out-File -Path "$Output\security-analysis.json" -Encoding UTF8
# 出力ファイル一覧の作成
$outputFiles = Get-ChildItem -Path $Output -Recurse -File |
Where-Object { $_.Name -notlike "*.log" } |
ForEach-Object {
[PSCustomObject]@{
FileName = $_.Name
Directory = $_.Directory.Name
FileSizeKB = [math]::Round($_.Length / 1KB, 2)
FilePath = $_.FullName.Replace($Output, "").Trim('\')
}
}
$outputFiles | Export-Csv -Path "$Output\file-inventory.csv" -NoTypeInformation -Encoding UTF8
Write-Log "Part10 完了"
# ------------------------------------------------------------
# 完了処理とレポート生成
# ------------------------------------------------------------
Write-Log "=== 最終処理 ==="
# 実行時間計算
$totalTime = (Get-Date) - $startTime
# 最終レポート
$finalReport = [PSCustomObject]@{
ReportType = "IAM Full Inventory Export"
ExecutionDate = $Date
Region = $Region
StartTime = $startTime.ToString("yyyy-MM-dd HH:mm:ss")
EndTime = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
TotalDuration = "$([math]::Round($totalTime.TotalMinutes, 2)) minutes"
InventoryCounts = @{
Users = $Users.Count
Roles = $Roles.Count
Groups = $Groups.Count
Policies = $Policies.Count
}
SecurityPosture = @{
MFAEnabledUsers = $usersWithMFA
MFAEnforcementRate = if ($Users.Count -gt 0) { [math]::Round(($usersWithMFA / $Users.Count) * 100, 2) } else { 0 }
UsersWithAccessKeys = $usersWithAccessKeys
UsersWithConsoleAccess = $usersWithConsoleAccess
}
DataCollection = @{
TotalCSVFiles = ($outputFiles | Where-Object { $_.FileName -like "*.csv" }).Count
TotalJSONFiles = ($outputFiles | Where-Object { $_.FileName -like "*.json" }).Count
TotalFiles = $outputFiles.Count
TotalDataSizeMB = [math]::Round(($outputFiles | Measure-Object -Property FileSizeKB -Sum).Sum / 1024, 2)
}
OutputLocation = $Output
ExportPolicyDocuments = $ExportPolicyDocuments
}
$finalReport | ConvertTo-Json -Depth 4 | Out-File -Path "$Output\final-report.json" -Encoding UTF8
# 簡易サマリー表示用CSV
$simpleSummary = [PSCustomObject]@{
Category = "Users"
Count = $Users.Count
Details = "$usersWithMFA with MFA, $usersWithAccessKeys with Access Keys"
}
$simpleSummary += [PSCustomObject]@{
Category = "Roles"
Count = $Roles.Count
Details = "$(($allRoleDetails | Measure-Object -Property InlinePolicies -Sum).Sum) inline policies"
}
$simpleSummary += [PSCustomObject]@{
Category = "Groups"
Count = $Groups.Count
Details = "$($allGroupMemberships.Count) total memberships"
}
$simpleSummary += [PSCustomObject]@{
Category = "Policies"
Count = $Policies.Count
Details = "$(($Policies | Where-Object { $_.Arn -notlike 'arn:aws:iam::aws:policy/*' }).Count) customer managed"
}
$simpleSummary | Export-Csv -Path "$Output\simple-summary.csv" -NoTypeInformation -Encoding UTF8
# ------------------------------------------------------------
# 完了メッセージ
# ------------------------------------------------------------
Write-Log "=== IAM Full Inventory Export Completed Successfully ==="
Write-Log "総実行時間: $([math]::Round($totalTime.TotalMinutes, 2)) 分"
Write-Log "出力ファイル数: $($outputFiles.Count) files"
Write-Log "総データ容量: $([math]::Round(($outputFiles | Measure-Object -Property FileSizeKB -Sum).Sum / 1024, 2)) MB"
Write-Host "`n" + "="*60
Write-Host "IAM フルインベントリ出力完了" -ForegroundColor Green
Write-Host "="*60
Write-Host "出力先: $Output" -ForegroundColor Yellow
Write-Host "実行時間: $([math]::Round($totalTime.TotalMinutes, 2)) 分" -ForegroundColor White
Write-Host "`n主要統計:" -ForegroundColor Cyan
Write-Host " - ユーザー: $($Users.Count) (MFA有効: $usersWithMFA)" -ForegroundColor White
Write-Host " - ロール: $($Roles.Count)" -ForegroundColor White
Write-Host " - グループ: $($Groups.Count)" -ForegroundColor White
Write-Host " - ポリシー: $($Policies.Count)" -ForegroundColor White
Write-Host "`n出力ファイル:" -ForegroundColor Cyan
Write-Host " - 実行サマリー: execution-summary.csv" -ForegroundColor White
Write-Host " - セキュリティ分析: security-analysis.csv" -ForegroundColor White
Write-Host " - ユーザー詳細: users-summary.csv" -ForegroundColor White
Write-Host " - ポリシーアタッチメント: policy-attachments.csv" -ForegroundColor White
Write-Host " - アクセスアドバイザー: access-advisor.csv" -ForegroundColor White
Write-Host " - クレデンシャルレポート: credential-report.csv" -ForegroundColor White
Write-Host " - ファイル一覧: file-inventory.csv" -ForegroundColor White
Write-Host "`nセキュリティ状態:" -ForegroundColor Cyan
# セキュリティ状態の表示
$mfaRate = if ($Users.Count -gt 0) { [math]::Round(($usersWithMFA / $Users.Count) * 100, 2) } else { 0 }
$mfaColor = if ($mfaRate -ge 90) { "Green" } elseif ($mfaRate -ge 70) { "Yellow" } else { "Red" }
Write-Host " - MFA有効率: $mfaRate% " -ForegroundColor $mfaColor -NoNewline
Write-Host "($usersWithMFA/$($Users.Count) users)" -ForegroundColor White
$consoleRate = if ($Users.Count -gt 0) { [math]::Round(($usersWithConsoleAccess / $Users.Count) * 100, 2) } else { 0 }
$consoleColor = if ($consoleRate -le 30) { "Green" } elseif ($consoleRate -le 50) { "Yellow" } else { "Red" }
Write-Host " - コンソールアクセス: $consoleRate% " -ForegroundColor $consoleColor -NoNewline
Write-Host "($usersWithConsoleAccess/$($Users.Count) users)" -ForegroundColor White
# 推奨アクションの表示
Write-Host "`n推奨アクション:" -ForegroundColor Cyan
if ($mfaRate -lt 90) {
Write-Host " - MFAを有効化していないユーザーを確認してください" -ForegroundColor Yellow
}
if ($consoleRate -gt 50) {
Write-Host " - コンソールアクセスが不必要に多い可能性があります" -ForegroundColor Yellow
}
# 古いアクセスキーのチェック(簡易版)
$oldAccessKeys = @()
foreach ($userDir in (Get-ChildItem "$UserDetailDir\*" -Directory)) {
$accessKeysData = Get-Content "$userDir\access-keys.json" -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
if ($accessKeysData -and $accessKeysData.AccessKeyMetadata) {
foreach ($key in $accessKeysData.AccessKeyMetadata) {
$createDate = [DateTime]$key.CreateDate
$ageDays = ((Get-Date) - $createDate).Days
if ($ageDays -gt 90) {
$oldAccessKeys += [PSCustomObject]@{
UserName = (Split-Path $userDir -Leaf)
AccessKeyId = $key.AccessKeyId
CreateDate = $key.CreateDate
AgeDays = $ageDays
Status = $key.Status
}
}
}
}
}
if ($oldAccessKeys.Count -gt 0) {
Write-Host " - 90日以上経過したアクセスキーが $($oldAccessKeys.Count) 個見つかりました" -ForegroundColor Red
$oldAccessKeys | Export-Csv -Path "$Output\old-access-keys.csv" -NoTypeInformation -Encoding UTF8
}
# 空のグループのチェック
$emptyGroups = $allGroupDetails | Where-Object { $_.MemberCount -eq 0 }
if ($emptyGroups.Count -gt 0) {
Write-Host " - メンバーがいないグループが $($emptyGroups.Count) 個見つかりました" -ForegroundColor Yellow
$emptyGroups | Export-Csv -Path "$Output\empty-groups.csv" -NoTypeInformation -Encoding UTF8
}
# ポリシーがアタッチされていないロールのチェック
$rolesWithoutPolicies = $allRoleDetails | Where-Object { $_.InlinePolicies -eq 0 -and $_.ManagedPolicies -eq 0 }
if ($rolesWithoutPolicies.Count -gt 0) {
Write-Host " - ポリシーがアタッチされていないロールが $($rolesWithoutPolicies.Count) 個見つかりました" -ForegroundColor Yellow
$rolesWithoutPolicies | Export-Csv -Path "$Output\roles-without-policies.csv" -NoTypeInformation -Encoding UTF8
}
Write-Host "`n" + "="*60
Write-Host "詳細な分析結果は各CSVファイルをご確認ください" -ForegroundColor Green
Write-Host "="*60
# 最終ログ
Write-Log "出力完了: 総ファイル数 $($outputFiles.Count)"
Write-Log "セキュリティ状態: MFA $mfaRate%, コンソールアクセス $consoleRate%"
Write-Log "推奨アクション: 古いアクセスキー $($oldAccessKeys.Count)件, 空のグループ $($emptyGroups.Count)件"
# メール送信機能(オプション)
$emailReport = @"
IAM Full Inventory Report
=========================
実行日時: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
リージョン: $Region
実行時間: $([math]::Round($totalTime.TotalMinutes, 2)) 分
インベントリ統計:
- ユーザー: $($Users.Count) (MFA有効: $usersWithMFA)
- ロール: $($Roles.Count)
- グループ: $($Groups.Count)
- ポリシー: $($Policies.Count)
セキュリティ状態:
- MFA有効率: $mfaRate%
- コンソールアクセス: $consoleRate%
検出された問題:
- 古いアクセスキー: $($oldAccessKeys.Count) 件
- 空のグループ: $($emptyGroups.Count) 件
- ポリシーなしロール: $($rolesWithoutPolicies.Count) 件
出力先: $Output
"@
$emailReport | Out-File -Path "$Output\email-report.txt" -Encoding UTF8
# クリーンアップ(オプション:必要に応じてコメントアウト)
# 大きなJSONファイルを削除(CSVがあれば十分な場合)
Get-ChildItem -Path $Output -Recurse -File |
Where-Object { $_.Extension -eq ".json" -and $_.Name -notlike "*summary.json" } |
ForEach-Object {
Write-Log "削除: $($_.Name)"
Remove-Item $_.FullName -Force
}
Stop-Transcript
# 終了コード
exit 0
📊 完成したスクリプトの主な出力CSVファイル
主要なCSV出力ファイル:
users-summary.csv– 全ユーザーの基本情報と統計roles-summary.csv– 全ロールの基本情報groups-summary.csv– 全グループの基本情報policies-summary.csv– 全ポリシーの基本情報access-advisor.csv– サービス最終アクセス情報credential-report.csv– 公式クレデンシャルレポートpolicy-attachments.csv– ポリシーアタッチメント情報group-memberships.csv– グループメンバーシップexecution-summary.csv– 実行サマリーsecurity-analysis.csv– セキュリティ分析結果
セキュリティチェックCSV:
old-access-keys.csv– 古いアクセスキーempty-groups.csv– 空のグループroles-without-policies.csv– ポリシーなしロール
補助ファイル:
file-inventory.csv– 出力ファイル一覧email-report.txt– メール用サマリー
🎯 スクリプトの特徴
- 完全なCSV出力: すべてのデータがCSV形式で出力
- セキュリティ分析: 自動的なセキュリティ状態チェック
- 推奨アクション: 問題点の自動検出と推奨対応
- 進捗表示: 実行状況がわかりやすい
- ログ機能: 詳細な実行ログを保持
- 柔軟な出力: 必要な情報だけを選択可能
このスクリプトは、IAM環境の完全な可視化を提供し、セキュリティ監査、コンプライアンス対応、権限見直しに必要なすべての情報をCSV形式で出力します。
コメント