From f043d20827dc0b9bf74018730f7aec5fa4c5d237 Mon Sep 17 00:00:00 2001 From: Yeming Liu Date: Tue, 19 Aug 2025 17:56:50 +1000 Subject: [PATCH 1/2] auto azp run --- .github/workflows/auto-azp-run.yml | 67 +++++ .../Check-AzureOrgMembership.ps1 | 125 +++++++++ tools/GitHubOrgMember/README.md | 262 ++++++++++++++++++ tools/GitHubOrgMember/Test-ObjectBased.ps1 | 101 +++++++ 4 files changed, 555 insertions(+) create mode 100644 .github/workflows/auto-azp-run.yml create mode 100644 tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 create mode 100644 tools/GitHubOrgMember/README.md create mode 100644 tools/GitHubOrgMember/Test-ObjectBased.ps1 diff --git a/.github/workflows/auto-azp-run.yml b/.github/workflows/auto-azp-run.yml new file mode 100644 index 000000000000..bc97d4ceac56 --- /dev/null +++ b/.github/workflows/auto-azp-run.yml @@ -0,0 +1,67 @@ +name: Auto Trigger ADO Pipeline for Azure Members +run-name: Auto trigger ADO pipeline for PR by Azure member + +on: + pull_request: + types: [opened, synchronize] + +jobs: + check-and-trigger: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + sparse-checkout: | + tools/GitHubOrgMember + + - name: Get PR author and check Azure org membership + shell: pwsh + env: + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + Write-Host "Checking if PR author '$env:PR_AUTHOR' is an Azure organization member..." + + # Use the existing script to check membership + $membershipResult = & "./tools/GitHubOrgMember/Check-AzureOrgMembership.ps1" -Username $env:PR_AUTHOR -Quiet + + Write-Host "Membership check result:" + Write-Host "Username: $($membershipResult.Username)" + Write-Host "Organization: $($membershipResult.Organization)" + Write-Host "IsMember: $($membershipResult.IsMember)" + Write-Host "Status: $($membershipResult.Status)" + + if ($membershipResult.ErrorMessage) { + Write-Host "Error: $($membershipResult.ErrorMessage)" + } + + # Set output for next step + echo "IS_AZURE_MEMBER=$($membershipResult.IsMember)" >> $env:GITHUB_ENV + echo "MEMBERSHIP_STATUS=$($membershipResult.Status)" >> $env:GITHUB_ENV + + - name: Trigger ADO pipeline for Azure member + if: env.IS_AZURE_MEMBER == 'True' + shell: pwsh + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + try { + Write-Host "PR author is an Azure member. Proceeding to trigger ADO pipeline..." + + # Comment "/azp run" on the PR to trigger ADO pipeline + Write-Host "Commenting '/azp run' on PR #$env:PR_NUMBER to trigger ADO pipeline..." + gh pr comment $env:PR_NUMBER --body "/azp run" + + Write-Host "โœ… Successfully triggered ADO pipeline for PR #$env:PR_NUMBER" + } + catch { + Write-Error "โŒ Failed to trigger ADO pipeline: $($_.Exception.Message)" + exit 1 + } diff --git a/tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 b/tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 new file mode 100644 index 000000000000..5d5ac334bf86 --- /dev/null +++ b/tools/GitHubOrgMember/Check-AzureOrgMembership.ps1 @@ -0,0 +1,125 @@ +#!/usr/bin/env pwsh +<# +.SYNOPSIS + Check if a GitHub user is a member of the Azure organization. + +.DESCRIPTION + This script uses the GitHub CLI to check if a specified user is a member of the Azure GitHub organization. + Returns a structured PowerShell object with membership details. + +.PARAMETER Username + The GitHub username to check. + +.PARAMETER Organization + The GitHub organization to check membership for. Defaults to "Azure". + +.PARAMETER Quiet + Suppress console output and only return the object. + +.OUTPUTS + PSCustomObject with the following properties: + - Username: The checked username + - Organization: The organization that was checked + - IsMember: Boolean indicating if user is a public member + - Status: Detailed status (PublicMember, NotMember, PrivateMember, UserNotFound, Error) + - ErrorMessage: Error details if Status is Error + - CheckedAt: Timestamp of when the check was performed + +.EXAMPLE + $result = .\Check-AzureOrgMembership.ps1 "octocat" + if ($result.IsMember) { Write-Host "User is a member!" } + +.EXAMPLE + .\Check-AzureOrgMembership.ps1 "octocat" -Quiet | ConvertTo-Json +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true, Position = 0)] + [ValidateNotNullOrEmpty()] + [string]$Username, + + [Parameter()] + [string]$Organization = "Azure", + + [Parameter()] + [switch]$Quiet +) + +function Write-ConditionalOutput { + param([string]$Message, [string]$ForegroundColor = "White") + if (-not $Quiet) { + Write-Host $Message -ForegroundColor $ForegroundColor + } +} + +# Initialize result object +$result = [PSCustomObject]@{ + Username = $Username + Organization = $Organization + IsMember = $false + Status = "Unknown" + ErrorMessage = $null + CheckedAt = Get-Date +} + +try { + # Check if GitHub CLI is available + if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { + $result.Status = "Error" + $result.ErrorMessage = "GitHub CLI (gh) is not installed. Install from: https://cli.github.com/" + Write-ConditionalOutput "โŒ GitHub CLI not found" "Red" + return $result + } + + # Check if authenticated + $null = gh auth status 2>&1 + if ($LASTEXITCODE -ne 0) { + $result.Status = "Error" + $result.ErrorMessage = "GitHub CLI is not authenticated. Run 'gh auth login' first." + Write-ConditionalOutput "โŒ GitHub CLI not authenticated" "Red" + return $result + } + + Write-ConditionalOutput "๐Ÿ” Checking if '$Username' is a member of '$Organization' organization..." "Yellow" + + # Check organization membership using GitHub API + gh api "orgs/$Organization/members/$Username" --silent 2>$null + $apiExitCode = $LASTEXITCODE + + if ($apiExitCode -eq 0) { + # User is a public member + $result.IsMember = $true + $result.Status = "PublicMember" + Write-ConditionalOutput "โœ… $Username is a PUBLIC member of the $Organization organization!" "Green" + } + elseif ($apiExitCode -eq 1) { + # Exit code 1 typically means 404 - could be not a member or private membership + # Check if user exists + gh api "users/$Username" --silent 2>$null + if ($LASTEXITCODE -eq 0) { + $result.IsMember = $false + $result.Status = "NotMemberOrPrivate" + Write-ConditionalOutput "โŒ $Username is either not a member of $Organization organization or has private membership." "Red" + } + else { + $result.Status = "UserNotFound" + $result.ErrorMessage = "User '$Username' was not found on GitHub." + Write-ConditionalOutput "โŒ User '$Username' was not found on GitHub." "Red" + } + } + else { + # Unexpected error + $result.Status = "Error" + $result.ErrorMessage = "Unexpected error occurred (GitHub API exit code: $apiExitCode)" + Write-ConditionalOutput "โŒ Unexpected error checking membership (exit code: $apiExitCode)" "Red" + } +} +catch { + $result.Status = "Error" + $result.ErrorMessage = "Exception occurred: $($_.Exception.Message)" + Write-ConditionalOutput "โŒ Error: $($_.Exception.Message)" "Red" +} + +# Return the result object +return $result \ No newline at end of file diff --git a/tools/GitHubOrgMember/README.md b/tools/GitHubOrgMember/README.md new file mode 100644 index 000000000000..649730050eaa --- /dev/null +++ b/tools/GitHubOrgMember/README.md @@ -0,0 +1,262 @@ +# GitHub Azure Organization Membership Checker + +PowerShell script to check if a GitHub user is a member of the Azure GitHub organization using the GitHub CLI. Re```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Organization "microsoft" -Quietrns structured PowerShell objects for easy processing and integration. + +## Prerequisites + +1. **GitHub CLI**: Install from [https://cli.github.com/](https://cli.github.com/) +2. **Authentication**: Run `gh auth login` to authenticate with GitHub + +## Script + +### Check-AzureOrgMembership.ps1 (Object-Based) + +A PowerShell script that returns structured objects with membership information. + +**Usage:** +```powershell +# Basic usage - returns object +$result = .\Check-AzureOrgMembership.ps1 "username" + +# Quiet mode - suppresses console output +$result = .\Check-AzureOrgMembership.ps1 "username" -Quiet + +# Different organization +$result = .\Check-AzureOrgMembership.ps1 "username" -Organization "microsoft" + +# Check result +if ($result.IsMember) { + Write-Host "$($result.Username) is a member!" +} +``` + +**Features:** +- Returns structured PowerShell objects +- JSON serializable +- Pipeline-friendly +- Comprehensive error handling +- Configurable organization +- Quiet mode support +- Type-safe property access + +## Object Structure + +The script returns a `PSCustomObject` with the following properties: + +```powershell +@{ + Username = "string" # The checked username + Organization = "string" # The organization that was checked + IsMember = $true/$false # Boolean indicating if user is a public member + Status = "string" # Detailed status (see below) + ErrorMessage = "string" # Error details if Status is Error + CheckedAt = [DateTime] # Timestamp of when the check was performed +} +``` + +### Status Values + +- **`PublicMember`**: User is a confirmed public member +- **`NotMemberOrPrivate`**: User is either not a member or has private membership +- **`UserNotFound`**: The specified username doesn't exist on GitHub +- **`Error`**: An error occurred (see ErrorMessage for details) + +## Important Notes + +1. **Privacy**: GitHub organization membership can be set to private. If a user is a private member, the API will return the same response as if they're not a member. + +2. **Authentication**: The GitHub CLI must be authenticated to make API calls. Some organization information may require different permission levels. + +3. **Rate Limiting**: GitHub API has rate limits. For bulk checking, consider implementing delays between requests. + +## Examples + +### Example 1: Basic Object-Based Check + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" +Write-Host "User: $($result.Username)" +Write-Host "Is Member: $($result.IsMember)" +Write-Host "Status: $($result.Status)" +``` + +**Output:** +``` +๐Ÿ” Checking if 'vidai-msft' is a member of 'Azure' organization... +โœ… vidai-msft is a PUBLIC member of the Azure organization! +User: vidai-msft +Is Member: True +Status: PublicMember +``` + +### Example 2: Quiet Mode Processing + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Quiet +if ($result.IsMember) { + Write-Host "Welcome, Azure team member!" -ForegroundColor Green +} else { + Write-Host "Access denied." -ForegroundColor Red +} +``` + +### Example 3: Batch Processing Multiple Users + +```powershell +$users = @("vidai-msft", "isra-fel", "msJinLei", "NickCandy") +$results = $users | ForEach-Object { + .\Check-AzureOrgMembership.ps1 $_ -Quiet +} + +# Display results in a table +$results | Format-Table Username, IsMember, Status -AutoSize + +# Filter to show only members +$members = $results | Where-Object { $_.IsMember } +Write-Host "Azure Members: $($members.Count)" +``` + +**Sample Output:** +``` +Username IsMember Status +-------- -------- ------ +vidai-msft True PublicMember +isra-fel True PublicMember +msJinLei False NotMemberOrPrivate +NickCandy False NotMemberOrPrivate + +Azure Members: 2 +``` + +### Example 4: JSON Export for APIs + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Quiet +$jsonOutput = $result | ConvertTo-Json +Write-Host $jsonOutput +``` + +**Sample JSON Output:** +```json +{ + "Username": "vidai-msft", + "Organization": "Azure", + "IsMember": true, + "Status": "PublicMember", + "ErrorMessage": null, + "CheckedAt": "2025-08-18T14:05:32.1234567-07:00" +} +``` + +### Example 5: Error Handling + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "non-existent-user" -Quiet +if ($result.Status -eq "Error") { + Write-Warning "Error: $($result.ErrorMessage)" +} elseif ($result.Status -eq "UserNotFound") { + Write-Host "User not found on GitHub" +} +``` + +### Example 6: Different Organization + +```powershell +$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Organization "microsoft" -Quiet +Write-Host "$($result.Username) is a member of $($result.Organization): $($result.IsMember)" +``` + +## Troubleshooting + +### GitHub CLI Not Found +``` +Install GitHub CLI from: https://cli.github.com/ +``` + +### Not Authenticated +```powershell +gh auth login +``` + +### Permission Issues +If you get permission errors, ensure your GitHub token has the necessary scopes for reading organization membership. + +## Testing + +This project includes comprehensive testing for the object-based script. + +### Running Tests + +```powershell +# Run comprehensive object-based tests +.\Test-ObjectBased.ps1 +``` + +### Quick Test + +The object-based approach makes testing much simpler: + +```powershell +# Test multiple users and validate results +$testUsers = @{ + "vidai-msft" = $true # Should be member + "msJinLei" = $false # Should not be member +} + +foreach ($user in $testUsers.Keys) { + $result = .\Check-AzureOrgMembership.ps1 $user -Quiet + $expected = $testUsers[$user] + + if ($result.IsMember -eq $expected) { + Write-Host "โœ… $user test passed" -ForegroundColor Green + } else { + Write-Host "โŒ $user test failed" -ForegroundColor Red + } +} +``` + +## Object-Based Advantages + +### โœ… **Structured Data** +- Type-safe property access +- No string parsing required +- Consistent object structure + +### โœ… **Pipeline Friendly** +```powershell +# Easy filtering and processing +$results | Where-Object { $_.IsMember } | Select-Object Username, Status +``` + +### โœ… **JSON Serializable** +```powershell +# Perfect for APIs and data storage +$results | ConvertTo-Json | Out-File "membership-results.json" +``` + +### โœ… **Error Handling** +```powershell +# Structured error information +if ($result.Status -eq "Error") { + Write-Warning $result.ErrorMessage +} +``` + +### โœ… **No Exit Codes** +- No dependency on `$LASTEXITCODE` +- Works naturally with PowerShell objects +- Easier debugging and testing + +## Important Notes + +1. **Privacy**: GitHub organization membership can be set to private. If a user is a private member, the API will return the same response as if they're not a member. + +2. **Authentication**: The GitHub CLI must be authenticated to make API calls. Some organization information may require different permission levels. + +3. **Rate Limiting**: GitHub API has rate limits. For bulk checking, consider implementing delays between requests. + +## Exit Codes + +- `0`: User is a confirmed public member +- `1`: User is not a member, has private membership, doesn't exist, or an error occurred diff --git a/tools/GitHubOrgMember/Test-ObjectBased.ps1 b/tools/GitHubOrgMember/Test-ObjectBased.ps1 new file mode 100644 index 000000000000..64e8802d172a --- /dev/null +++ b/tools/GitHubOrgMember/Test-ObjectBased.ps1 @@ -0,0 +1,101 @@ +# Object-Based Test Runner for Azure Organization Membership Checker +# This script demonstrates the object-based approach + +Write-Host "๐Ÿงช Testing Object-Based Azure Organization Membership Checker" -ForegroundColor Cyan +Write-Host "=" * 60 -ForegroundColor Cyan + +# Test data +$AzureMembers = @("vidai-msft", "isra-fel", "wyunchi-ms") +$NonMembers = @("azure-powershell-bot", "msJinLei", "NickCandy") + +# Test 1: Prerequisites +Write-Host "`n๐Ÿ“‹ Test 1: Prerequisites Check" -ForegroundColor Yellow +$prereqResult = .\Check-AzureOrgMembership.ps1 "test-user" -Quiet +if ($prereqResult.Status -eq "Error" -and $prereqResult.ErrorMessage -match "GitHub CLI") { + Write-Host " โŒ $($prereqResult.ErrorMessage)" -ForegroundColor Red + exit 1 +} else { + Write-Host " โœ… GitHub CLI is available and authenticated" -ForegroundColor Green +} + +# Test 2: Azure Members +Write-Host "`n๐Ÿ“‹ Test 2: Azure Members Testing" -ForegroundColor Yellow +$memberResults = @() +foreach ($member in $AzureMembers) { + $result = .\Check-AzureOrgMembership.ps1 $member -Quiet + $memberResults += $result + + if ($result.IsMember -and $result.Status -eq "PublicMember") { + Write-Host " โœ… $member correctly identified as Azure member" -ForegroundColor Green + } else { + Write-Host " โŒ $member test failed - IsMember: $($result.IsMember), Status: $($result.Status)" -ForegroundColor Red + } +} + +# Test 3: Non-Members +Write-Host "`n๐Ÿ“‹ Test 3: Non-Members Testing" -ForegroundColor Yellow +$nonMemberResults = @() +foreach ($nonMember in $NonMembers) { + $result = .\Check-AzureOrgMembership.ps1 $nonMember -Quiet + $nonMemberResults += $result + + if (-not $result.IsMember -and $result.Status -eq "NotMemberOrPrivate") { + Write-Host " โœ… $nonMember correctly identified as non-member" -ForegroundColor Green + } else { + Write-Host " โŒ $nonMember test failed - IsMember: $($result.IsMember), Status: $($result.Status)" -ForegroundColor Red + } +} + +# Test 4: Object Structure Validation +Write-Host "`n๐Ÿ“‹ Test 4: Object Structure Validation" -ForegroundColor Yellow +$sampleResult = $memberResults[0] +$requiredProperties = @("Username", "Organization", "IsMember", "Status", "ErrorMessage", "CheckedAt") +$allPropertiesPresent = $true + +foreach ($prop in $requiredProperties) { + if ($sampleResult.PSObject.Properties.Name -contains $prop) { + Write-Host " โœ… Property present: $prop" -ForegroundColor Green + } else { + Write-Host " โŒ Missing property: $prop" -ForegroundColor Red + $allPropertiesPresent = $false + } +} + +if ($allPropertiesPresent) { + Write-Host " โœ… All required properties present in result objects" -ForegroundColor Green +} else { + Write-Host " โŒ Result object structure validation failed" -ForegroundColor Red +} + +# Test 5: JSON Serialization +Write-Host "`n๐Ÿ“‹ Test 5: JSON Serialization Test" -ForegroundColor Yellow +try { + $jsonOutput = $sampleResult | ConvertTo-Json + $deserializedResult = $jsonOutput | ConvertFrom-Json + + if ($deserializedResult.Username -eq $sampleResult.Username) { + Write-Host " โœ… Objects properly serialize to/from JSON" -ForegroundColor Green + } else { + Write-Host " โŒ JSON serialization test failed" -ForegroundColor Red + } +} catch { + Write-Host " โŒ JSON serialization error: $($_.Exception.Message)" -ForegroundColor Red +} + +# Summary +Write-Host "`n๐ŸŽ‰ Object-Based Test Suite Complete!" -ForegroundColor Green +Write-Host "๐Ÿ“Š Summary:" -ForegroundColor Cyan +Write-Host " - Azure Members: $($AzureMembers.Count) tested" -ForegroundColor White +Write-Host " - Non-Members: $($NonMembers.Count) tested" -ForegroundColor White +Write-Host " - All results returned as structured PowerShell objects" -ForegroundColor White + +# Display sample object +Write-Host "`n๐Ÿ“„ Sample Result Object:" -ForegroundColor Cyan +$sampleResult | Format-List + +Write-Host "โœจ Benefits of Object-Based Approach:" -ForegroundColor Yellow +Write-Host " - No exit code parsing required" -ForegroundColor White +Write-Host " - Structured data easy to process" -ForegroundColor White +Write-Host " - JSON serializable for APIs" -ForegroundColor White +Write-Host " - Type-safe property access" -ForegroundColor White +Write-Host " - Pipeable and filterable" -ForegroundColor White From bec07d1ca7d8aeb76511e343738cda87ca51bb8d Mon Sep 17 00:00:00 2001 From: Yeming Liu Date: Tue, 19 Aug 2025 18:04:19 +1000 Subject: [PATCH 2/2] update markdown --- tools/GitHubOrgMember/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/GitHubOrgMember/README.md b/tools/GitHubOrgMember/README.md index 649730050eaa..209ee1deab4f 100644 --- a/tools/GitHubOrgMember/README.md +++ b/tools/GitHubOrgMember/README.md @@ -1,7 +1,6 @@ # GitHub Azure Organization Membership Checker -PowerShell script to check if a GitHub user is a member of the Azure GitHub organization using the GitHub CLI. Re```powershell -$result = .\Check-AzureOrgMembership.ps1 "vidai-msft" -Organization "microsoft" -Quietrns structured PowerShell objects for easy processing and integration. +PowerShell script to check if a GitHub user is a member of the Azure GitHub organization using the GitHub CLI. Returns structured PowerShell objects for easy processing and integration. ## Prerequisites