AWS IAM ユーザー情報取得 PS1

AWS
Param(
    [string]$Region = "ap-northeast-1",
    [string]$OutputDir = "./iam-full-export",
    [switch]$ExportPolicyDocuments = $false
)

# ---- 初期設定 ---------------------------------------------------
$policyDocsDir = Join-Path $OutputDir "policy-documents"

try {
    New-Item -ItemType Directory -Path $OutputDir -ErrorAction SilentlyContinue | Out-Null
    if ($ExportPolicyDocuments) {
        New-Item -ItemType Directory -Path $policyDocsDir -ErrorAction SilentlyContinue | Out-Null
    }
} catch {
    Write-Error "出力ディレクトリ作成失敗: $($_.Exception.Message)"
    exit 1
}

$logFile = Join-Path $OutputDir "iam-export-log-$(Get-Date -Format 'yyyyMMdd-HHmmss').txt"
Start-Transcript -Path $logFile -Append

Write-Host "=== IAM ユーザー情報収集開始 ===" -ForegroundColor Green
Write-Host "リージョン: $Region" -ForegroundColor Yellow
Write-Host "出力先: $OutputDir" -ForegroundColor Yellow

try {
    # ---- IAM ユーザー一覧取得 ---------------------------------------------------
    Write-Host "IAM ユーザー一覧取得中..." -ForegroundColor Cyan
    $users = (aws iam list-users --region $Region --output json | ConvertFrom-Json).Users
    if (!$users) { throw "IAM ユーザー一覧取得失敗" }

    $totalUsers = $users.Count
    Write-Host "対象ユーザー数: $totalUsers" -ForegroundColor Green

    $results = @()
    $count = 0

    # ---- 各ユーザー処理 ---------------------------------------------------
    foreach ($u in $users) {
        $count++
        $userName = $u.UserName
        Write-Progress -Activity "IAM ユーザー処理中" -Status "$userName ($count/$totalUsers)" -PercentComplete (($count/$totalUsers)*100)

        # 初期レコード生成
        $info = [ordered]@{
            UserName=""
            UserId=""
            Arn=""
            CreateDate=""
            ConsoleLoginEnabled="No"
            ConsoleLoginStatus="無効"
            PasswordLastUsed="Never"
            MFAEnabled="No"
            MFADeviceSerial=""
            AccessKey1Id="" ; AccessKey1Status="" ; AccessKey1CreateDate="" ; AccessKey1LastUsed=""
            AccessKey2Id="" ; AccessKey2Status="" ; AccessKey2CreateDate="" ; AccessKey2LastUsed=""
            AccessKeysStatus="無効"
            Groups=""
            AttachedPolicies=""
            InlinePolicies=""
            LastActivityDate=""
            AccountStatus="無効"
        }

        # 基本情報
        $info.UserName = $userName
        $info.UserId   = $u.UserId
        $info.Arn      = $u.Arn
        $info.CreateDate = $u.CreateDate.ToString("yyyy-MM-dd HH:mm:ss")

        $consoleEnabled = $false
        $activeKeys = 0

        # ---- get-user(PasswordLastUsed) ----
        try {
            $g = aws iam get-user --user-name $userName --region $Region --output json | ConvertFrom-Json
            if ($g.User.PasswordLastUsed) {
                $info.PasswordLastUsed = $g.User.PasswordLastUsed.ToString("yyyy-MM-dd HH:mm:ss")
            }
        } catch {
            Write-Warning "ユーザー $userName の詳細情報取得失敗: $($_.Exception.Message)"
        }

        # ---- Console Login ----
        try { 
            $loginProfile = aws iam get-login-profile --user-name $userName --region $Region --output json | ConvertFrom-Json
            if($loginProfile){
                $consoleEnabled = $true
                $info.ConsoleLoginEnabled = "Yes"
                $info.ConsoleLoginStatus = "有効"
            }

        } catch {
            Write-Warning "ユーザー $userName のログインプロファイル取得失敗: $($_.Exception.Message)"
        }

        # ---- MFA ----
        try {
            $mfa = aws iam list-mfa-devices --user-name $userName --region $Region --output json | ConvertFrom-Json
            if ($mfa.MFADevices.Count) {
                $info.MFAEnabled = "Yes"
                $info.MFADeviceSerial = $mfa.MFADevices[0].SerialNumber
            }
        } catch {
            Write-Warning "ユーザー $userName のMFAデバイス情報取得失敗: $($_.Exception.Message)"
        }

        # ---- Access Keys ----
        try {
            $keys = (aws iam list-access-keys --user-name $userName --region $Region --output json | ConvertFrom-Json).AccessKeyMetadata

            for ($i=0; $i -lt $keys.Count; $i++) {
                $kid = "AccessKey$($i+1)"

                $info["${kid}Id"] = $keys[$i].AccessKeyId
                $info["${kid}Status"] = $keys[$i].Status
                $info["${kid}CreateDate"] = $keys[$i].CreateDate.ToString("yyyy-MM-dd HH:mm:ss")

                if ($keys[$i].Status -eq "Active") {
                    $activeKeys++
                }

                try {
                    $lu = aws iam get-access-key-last-used --access-key-id $keys[$i].AccessKeyId --region $Region --output json | ConvertFrom-Json
                    if ($lu.AccessKeyLastUsed.LastUsedDate) {
                        $info["${kid}LastUsed"] = $lu.AccessKeyLastUsed.LastUsedDate.ToString("yyyy-MM-dd HH:mm:ss")
                    } else {
                        $info["${kid}LastUsed"] = "Never"
                    }
                } catch {
                    $info["${kid}LastUsed"] = "Error"
                    Write-Warning "アクセスキー $($keys[$i].AccessKeyId) の使用状況取得失敗: $($_.Exception.Message)"
                }
            }

            # アクセスキー全体の状態
            $info.AccessKeysStatus = if ($activeKeys -gt 0) { "有効 ($activeKeys個)" } else { "無効" }

        } catch {
            Write-Warning "ユーザー $userName のアクセスキー情報取得失敗: $($_.Exception.Message)"
        }

        # ---- Groups ----
        try {
            $info.Groups = (
                aws iam list-groups-for-user --user-name $userName --region $Region --output json |
                ConvertFrom-Json | Select-Object -Expand Groups |
                ForEach-Object { $_.GroupName }
            ) -join "|"
        } catch {
            Write-Warning "ユーザー $userName のグループ情報取得失敗: $($_.Exception.Message)"
        }

        # ---- Attached Policies ----
        try {
            $attach = (aws iam list-attached-user-policies --user-name $userName --region $Region --output json | ConvertFrom-Json).AttachedPolicies
            $info.AttachedPolicies = ($attach | ForEach-Object { $_.PolicyName }) -join "|"

            if ($ExportPolicyDocuments) {
                foreach ($p in $attach) {
                    try {
                        $policy = aws iam get-policy --policy-arn $p.PolicyArn --region $Region --output json | ConvertFrom-Json
                        $ver = aws iam get-policy-version --policy-arn $p.PolicyArn --version-id $policy.Policy.DefaultVersionId --region $Region --output json | ConvertFrom-Json
                        ($ver.PolicyVersion.Document | ConvertTo-Json -Depth 10) |
                            Out-File (Join-Path $policyDocsDir "$($userName)_$($p.PolicyName)_attached.json")
                    } catch {
                        Write-Warning "ポリシー $($p.PolicyName) のドキュメント取得失敗: $($_.Exception.Message)"
                    }
                }
            }
        } catch {
            Write-Warning "ユーザー $userName のアタッチ済みポリシー情報取得失敗: $($_.Exception.Message)"
        }

        # ---- Inline Policies ----
        try {
            $inline = (aws iam list-user-policies --user-name $userName --region $Region --output json | ConvertFrom-Json).PolicyNames
            $info.InlinePolicies = $inline -join "|"

            if ($ExportPolicyDocuments) {
                foreach ($name in $inline) {
                    try {
                        $doc = aws iam get-user-policy --user-name $userName --policy-name $name --region $Region --output json | ConvertFrom-Json
                        ($doc.PolicyDocument | ConvertTo-Json -Depth 10) |
                            Out-File (Join-Path $policyDocsDir "$($userName)_${name}_inline.json")
                    } catch {
                        Write-Warning "インラインポリシー $name のドキュメント取得失敗: $($_.Exception.Message)"
                    }
                }
            }
        } catch {
            Write-Warning "ユーザー $userName のインラインポリシー情報取得失敗: $($_.Exception.Message)"
        }

        # ---- LastActivityDate ----
        $dates = @()
        foreach ($k in @("PasswordLastUsed","AccessKey1LastUsed","AccessKey2LastUsed")) {
            if ($info[$k] -and $info[$k] -notmatch "Never|Error") {
                try {
                    $dates += [datetime]$info[$k]
                } catch {
                    Write-Warning "日付変換エラー ($userName - $k): $($info[$k])"
                }
            }
        }

        $info.LastActivityDate = if ($dates.Count) {
            ($dates | Measure-Object -Maximum).Maximum.ToString("yyyy-MM-dd HH:mm:ss")
        } else { "Never" }

        # ---- アカウント状態判定 ----
        if ($consoleEnabled -or $activeKeys -gt 0) {
            if ($consoleEnabled -and $activeKeys -gt 0) {
                $info.AccountStatus = "有効 (コンソール+API)"
            } elseif ($consoleEnabled) {
                $info.AccountStatus = "有効 (コンソール)"
            } else {
                $info.AccountStatus = "有効 (API)"
            }
        }

        $results += [pscustomobject]$info
    }

    Write-Progress -Activity "完了" -Completed

    # ---- CSV 出力 ---------------------------------------------------
    $csv = Join-Path $OutputDir "iam-users-full-$(Get-Date -Format 'yyyyMMdd-HHmmss').csv"
    $results | Export-Csv -Path $csv -NoTypeInformation -Encoding UTF8

    # 統計情報
    $activeAccounts = ($results | Where-Object { $_.AccountStatus -match "有効" }).Count
    $inactiveAccounts = ($results | Where-Object { $_.AccountStatus -eq "無効" }).Count
    $consoleAccounts = ($results | Where-Object { $_.ConsoleLoginStatus -eq "有効" }).Count
    $apiAccounts = ($results | Where-Object { $_.AccessKeysStatus -match "有効" }).Count

    Write-Host "`n=== 完了:IAM ユーザー情報出力 ===" -ForegroundColor Green
    Write-Host "CSV: $csv" -ForegroundColor Yellow
    Write-Host "ログ: $logFile" -ForegroundColor Yellow
    if ($ExportPolicyDocuments) { Write-Host "ポリシードキュメント: $policyDocsDir" -ForegroundColor Yellow }

    Write-Host "`n=== アカウント状態統計 ===" -ForegroundColor Cyan
    Write-Host "総ユーザー数: $totalUsers" -ForegroundColor White
    Write-Host "有効アカウント: $activeAccounts" -ForegroundColor White
    Write-Host "無効アカウント: $inactiveAccounts" -ForegroundColor White
    Write-Host "コンソールログイン有効: $consoleAccounts" -ForegroundColor White
    Write-Host "APIアクセス有効: $apiAccounts" -ForegroundColor White

} catch {
    Write-Error "スクリプト実行エラー: $($_.Exception.Message)"
    throw
} finally {
    Stop-Transcript
}
# 基本実行
.\iam-export.ps1

# リージョン指定
.\iam-export.ps1 -Region us-east-1

# ポリシードキュメントも出力
.\iam-export.ps1 -ExportPolicyDocuments

# 出力ディレクトリ指定
.\iam-export.ps1 -OutputDir "C:\iam-backup"

コメント