aws iam 棚卸

AWS
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出力ファイル:

  1. users-summary.csv – 全ユーザーの基本情報と統計
  2. roles-summary.csv – 全ロールの基本情報
  3. groups-summary.csv – 全グループの基本情報
  4. policies-summary.csv – 全ポリシーの基本情報
  5. access-advisor.csv – サービス最終アクセス情報
  6. credential-report.csv – 公式クレデンシャルレポート
  7. policy-attachments.csv – ポリシーアタッチメント情報
  8. group-memberships.csv – グループメンバーシップ
  9. execution-summary.csv – 実行サマリー
  10. security-analysis.csv – セキュリティ分析結果

セキュリティチェックCSV:

  1. old-access-keys.csv – 古いアクセスキー
  2. empty-groups.csv – 空のグループ
  3. roles-without-policies.csv – ポリシーなしロール

補助ファイル:

  1. file-inventory.csv – 出力ファイル一覧
  2. email-report.txt – メール用サマリー

🎯 スクリプトの特徴

  1. 完全なCSV出力: すべてのデータがCSV形式で出力
  2. セキュリティ分析: 自動的なセキュリティ状態チェック
  3. 推奨アクション: 問題点の自動検出と推奨対応
  4. 進捗表示: 実行状況がわかりやすい
  5. ログ機能: 詳細な実行ログを保持
  6. 柔軟な出力: 必要な情報だけを選択可能

このスクリプトは、IAM環境の完全な可視化を提供し、セキュリティ監査、コンプライアンス対応、権限見直しに必要なすべての情報をCSV形式で出力します。

コメント