diff --git a/scripts/graph-connect-to-graph/assets/sample.json b/scripts/graph-connect-to-graph/assets/sample.json deleted file mode 100644 index dbbf40a4e..000000000 --- a/scripts/graph-connect-to-graph/assets/sample.json +++ /dev/null @@ -1,58 +0,0 @@ -[ - { - "name": "graph-connect-to-graph", - "source": "pnp", - "title": "Connect to Graph using V1 or V2 or the SDK", - "shortDescription": "This function detects the graph version and connects accordingly", - "url": "https://pnp.github.io/script-samples/graph-connect-to-graph/README.html", - "longDescription": [ - "" - ], - "creationDateTime": "2023-07-06", - "updateDateTime": "2023-07-06", - "products": [ - "Graph" - ], - "metadata": [ - { - "key": "GRAPH-POWERSHELL", - "value": "1.0.0" - } - ], - "categories": [ - "Configure" - ], - "tags": [ - "bash", - "classic", - "Invoke-RestMethod", - "Import-Module", - "Connect-MgGraph", - "Select-MgProfile" - ], - "thumbnails": [ - { - "type": "image", - "order": 100, - "url": "https://raw.githubusercontent.com/pnp/script-samples/main/scripts/graph-connect-to-graph/assets/preview.png", - "alt": "Preview of the Graph Connection Function" - } - ], - "authors": [ - { - "gitHubAccount": "andrew-s-taylor", - "company": "", - "pictureUrl": "https://github.com/andrew-s-taylor.png", - "name": "Andrew Taylor" - } - ], - "references": [ - - { - "name": "Want to learn more about Microsoft Graph PowerShell SDK and the cmdlets", - "description": "Check out the Microsoft Graph PowerShell SDK documentation site to get started and for the reference to the cmdlets.", - "url": "https://learn.microsoft.com/graph/powershell/get-started" - } - ] - } -] diff --git a/scripts/graph-get-mfa-user-number/README.md b/scripts/graph-get-mfa-user-number/README.md new file mode 100644 index 000000000..938666acc --- /dev/null +++ b/scripts/graph-get-mfa-user-number/README.md @@ -0,0 +1,262 @@ +# Export Entra ID user MFA phone details to CSV + +Export Entra ID user MFA phone details to CSV.This PowerShell script enumerates users in Microsoft Entra ID (Azure AD) and reads their MFA/phone authentication methods from Microsoft Graph, then upserts that data into a Microsoft List named `MFAUserData` in SharePoint. + +Key behaviors: + +- Retrieves users (uses `Get-MgUser` with a filter for UPNs starting with 'T' in the provided script). +- Calls the Graph API `authentication/phoneMethods` endpoint for each user to collect MFA phone numbers. +- Cleans phone numbers (removes non-digit characters) and writes/upserts user and phone data into the `MFAUserData` list using PnP.PowerShell. + +## Important security note (READ FIRST) + +The version of the script included here contains a hard-coded client secret in `Get-UserToken`. Do NOT commit client secrets into source control. Before running in any production or shared repo, remove any hard-coded secrets and use one of the recommended approaches below: + +- Use environment variables or a secure vault (Azure Key Vault, Windows Credential Manager, SecretManager). +- Use a certificate-based credential for app-only auth instead of a client secret. +- Use Managed Identity (if running from an Azure resource that supports it). + +If this repository contains real secrets, rotate those credentials immediately. + +### Prerequisites + +- PowerShell 7+ (pwsh) is recommended. +- Install the Microsoft Graph PowerShell SDK and PnP.PowerShell modules: + +```powershell +Install-Module -Name Microsoft.Graph -Scope CurrentUser -AllowClobber +Install-Module -Name PnP.PowerShell -Scope CurrentUser +``` + +- An Azure AD application with the required application permissions (granted by an administrator). + +Suggested Graph application permissions (app-only) you'll likely need: + +- Authentication Methods / phone read permission (e.g., AuthenticationMethods.Read.All or AuthenticationMethods.ReadWrite.All) — grant admin consent. +- User read permissions (User.Read.All) — grant admin consent. +- Directory.Read.All - grant admin consent + +Note: permission names sometimes vary between SDKs and portal UI; grant the minimal required app permissions and test in a non-production tenant first. + +### Configuration + +Recommended: Export client secret and other values via environment variables and update `Get-UserToken` to read them: + +## Description + +- Prompts for Tenant Id, Client Id, and Client Secret (no secrets hard-coded) +- Uses Microsoft Graph REST with client credentials flow +- Retrieves users (script currently enumerates all users via `Get-MgUser`) and filters in code for the intended set (example: UPN starts with 'T') +- For each user, retrieves `authentication/phoneMethods` via Microsoft Graph +- Exports the following columns to CSV: + + - DisplayName + - UserPrincipalName + - LDAPUserName (onPremisesSamAccountName) + - PhoneNumber + - CleanedPhone (digits only) + +> Note: The original header included `MethodType` in comments; the current script exports the five columns above. You can append `MethodType` when iterating phone methods by adding the `@odata.type` or other method properties to the PSCustomObject. + + + +## Security notes (very important) + +- The interactive script prompts for a client secret. Avoid printing secrets to the console. The sample `Get-UserToken` implementation currently writes the secret back with `Write-Host` — remove that line immediately. +- Prefer one of these secure approaches instead of pasting secrets into the console: + + - Store secrets in Azure Key Vault and read them at runtime. + - Use environment variables (e.g., $env:MYAPP_CLIENT_SECRET) and read them in the script. + - Use certificate-based app credentials or managed identities where possible. + +- If any real client secrets existed in this repository, rotate them now. + +## How to run + +1. Open PowerShell (pwsh). +2. Install the Microsoft.Graph module (see Prerequisites). +3. Save the script file locally (e.g., `Export-UserMfaPhoneDetails.ps1`). +4. Run the script. The script prompts for Tenant Id, Client Id, Client Secret and an output CSV path. + +Example run: + +``` powershell +# Run the script; you'll be prompted for TenantId, ClientId, ClientSecret and output path +.\Export-UserMfaPhoneDetails.ps1 + +``` + +After completion you'll have a UTF-8 CSV at the path you provided. + +## Output columns + +- DisplayName +- UserPrincipalName +- LDAPUserName (onPremisesSamAccountName) +- PhoneNumber +- CleanedPhone (digits only) + +If you want `MethodType` or other phone-method metadata, add that property when building each PSCustomObject from `$m`. + +## Troubleshooting + +- No phone methods for user: the script records a row with empty phone fields for users without phone methods (adjust logic if you prefer to skip those users). +- Permission errors: ensure the app has the required application permissions and admin consent. Use the Azure portal to double-check consent. +- Graph throttling: implement retry/backoff for `Invoke-RestMethod` calls if you hit rate limits. + +## Suggested improvements / next steps + +- Do not echo secrets; use `Read-Host -AsSecureString` or environment variables. +- Replace raw REST calls with Microsoft.Graph SDK method calls where feasible for consistency. +- Add paging support or server-side filtering to only retrieve the users you need (the current script enumerates all users via `Get-MgUser -All`). +- Add logging, error handling, and a dry-run or verbose mode. + +``` powershell +<# +.SYNOPSIS + Export Entra ID user MFA phone details to CSV. + +.DESCRIPTION + - Prompts for Tenant Id, Client Id, and Client Secret (no secrets hard-coded) + - Uses Microsoft Graph REST with client credentials flow + - Retrieves users where userPrincipalName starts with 'T' + - For each user, retrieves authentication phoneMethods + - Exports: + DisplayName + UserPrincipalName + LDAPUserName (onPremisesSamAccountName) + PhoneNumber + CleanedPhone (digits only) + MethodType (@odata.type from phoneMethods) +#> + +# ================================ +# Get access token (interactive) +# ================================ +function Get-UserToken { + # Read from user + $TenantId = Read-Host "Enter Tenant Id (Directory Id / GUID)" + $ClientId = Read-Host "Enter Client Id (App Registration Id)" + $SecSecret = Read-Host "Enter Client Secret" + + # Convert secure string to plain text for token request + $Body = @{ + grant_type = "client_credentials" + scope = "https://graph.microsoft.com/.default" + client_id = $ClientId + client_secret = $SecSecret + } + + try { + $TokenResponse = Invoke-RestMethod -Method Post ` + -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" ` + -Body $Body -ErrorAction Stop + + $AccessToken = $TokenResponse.access_token + Write-Host "Access Token Obtained Successfully" -ForegroundColor Green + return $AccessToken + } + catch { + Write-Error "Failed to obtain access token: $($_.Exception.Message)" + throw + } +} + +# ================================ +# Export MFA phone details to CSV +# ================================ +function Export-UserMfaPhoneDetailsToCsv { + param ( + [Parameter(Mandatory)] + [string]$AccessToken, + + [Parameter(Mandatory)] + [string]$OutputPath + ) + + $headers = @{ + Authorization = "Bearer $AccessToken" + 'Content-Type' = 'application/json' + } + + # Helper: clean phone (digits only) + function CleanPhone { + param([string]$p) + if ([string]::IsNullOrWhiteSpace($p)) { return '' } + return ($p.ToCharArray() | Where-Object { [char]::IsDigit($_) }) -join '' + } + + # Get all users (UPN starts with 'T') + $users = Get-MgUser -All -Property "displayName,userPrincipalName,onPremisesSamAccountName,Id" + Write-Host "Retrieved $($users.Count) users from Graph" -ForegroundColor Cyan + + $results = @() + + foreach ($u in $users) { + $userId = $u.id + $displayName = $u.displayName + $upn = $u.userPrincipalName + $ldapUser = $u.onPremisesSamAccountName # This is your LDAP username + + $pmUrl = "https://graph.microsoft.com/v1.0/users/$([uri]::EscapeDataString($userId))/authentication/phoneMethods" + try { + $pm = Invoke-RestMethod -Headers $headers -Uri $pmUrl -Method Get -ErrorAction Stop + } + catch { + Write-Verbose "Failed phoneMethods for $upn : $($_.Exception.Message)" + continue + } + # Write-Host $pm.value + if (-not $pm.value) { + # No phone methods: still record the user if you want them in the CSV + $results += [PSCustomObject]@{ + DisplayName = $displayName + UserPrincipalName = $upn + LDAPUserName = $ldapUser + PhoneNumber = $null + CleanedPhone = $null + } + continue + } + + foreach ($m in $pm.value) { + $phone = $m.phoneNumber + $cleaned = CleanPhone $phone + + $results += [PSCustomObject]@{ + DisplayName = $displayName + UserPrincipalName = $upn + LDAPUserName = $ldapUser + PhoneNumber = $phone + CleanedPhone = $cleaned + } + } + } + + # Write to CSV + $results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 + Write-Host $results.Count + Write-Host "Exported $($results.Count) rows to $OutputPath" -ForegroundColor Green +} + +# ================================ +# Main +# ================================ +$token = Get-UserToken + +$outputPath = Read-Host "Enter full path for CSV output (e.g. C:\Temp\MfaUserData.csv)" + +Export-UserMfaPhoneDetailsToCsv -AccessToken $token -OutputPath $outputPath +``` + +[!INCLUDE [More about Microsoft Graph PowerShell SDK](../../docfx/includes/MORE-GRAPHSDK.md)] +*** + +## Contributors + +| Author(s) | +|-----------| +| [Divya Akula](https://github.com/divya-akula)| + +[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)] + diff --git a/scripts/graph-get-mfa-user-number/assets/example.png b/scripts/graph-get-mfa-user-number/assets/example.png new file mode 100644 index 000000000..62aee2aef Binary files /dev/null and b/scripts/graph-get-mfa-user-number/assets/example.png differ diff --git a/scripts/graph-get-mfa-user-number/assets/preview.png b/scripts/graph-get-mfa-user-number/assets/preview.png new file mode 100644 index 000000000..32a7b5d99 Binary files /dev/null and b/scripts/graph-get-mfa-user-number/assets/preview.png differ diff --git a/scripts/graph-get-mfa-user-number/assets/sample.csv b/scripts/graph-get-mfa-user-number/assets/sample.csv new file mode 100644 index 000000000..04b141016 --- /dev/null +++ b/scripts/graph-get-mfa-user-number/assets/sample.csv @@ -0,0 +1,26 @@ +DisplayName,UserPrincipalName,LDAPUserName,PhoneNumber,CleanedPhone +Adele Vance,AdeleV@5kswv1.onmicrosoft.com,,, +Divya Akula,akula.divya_gmail.com#EXT#@5kswv1.onmicrosoft.com,,, +Alex Wilber,AlexW@5kswv1.onmicrosoft.com,,, +Diego Siciliani,DiegoS@5kswv1.onmicrosoft.com,,, +Dinesh Akula,DineshAkula@5kswv1.onmicrosoft.com,,, +Divya Akula,divya.akula_tarento.com#EXT#@5kswv1.onmicrosoft.com,,, +Divya Akula,divya@5kswv1.onmicrosoft.com,,+91 99999999,919999999999 +Divya Akula,divyaakula_m365devtoy.onmicrosoft.com#EXT#@5kswv1.onmicrosoft.com,,, +Grady Archie,GradyA@5kswv1.onmicrosoft.com,,, +Hajira Begum,hajira.begum_tarento.com#EXT#@5kswv1.onmicrosoft.com,,, +Hajira,Hajira@5kswv1.onmicrosoft.com,,, +Henrietta Mueller,HenriettaM@5kswv1.onmicrosoft.com,,, +Isaiah Langer,IsaiahL@5kswv1.onmicrosoft.com,,, +Johanna Lorenz,JohannaL@5kswv1.onmicrosoft.com,,, +Joni Sherman,JoniS@5kswv1.onmicrosoft.com,,, +Lee Gu,LeeG@5kswv1.onmicrosoft.com,,, +Lidia Holloway,LidiaH@5kswv1.onmicrosoft.com,,, +Lynne Robbins,LynneR@5kswv1.onmicrosoft.com,,, +Megan Bowen,MeganB@5kswv1.onmicrosoft.com,,, +Miriam Graham,MiriamG@5kswv1.onmicrosoft.com,,, +Nestor Wilke,NestorW@5kswv1.onmicrosoft.com,,, +Patti Fernandez,PattiF@5kswv1.onmicrosoft.com,,, +User,powerplatform_devtarento.onmicrosoft.com#EXT#@5kswv1.onmicrosoft.com,,, +Pradeep Gupta,PradeepG@5kswv1.onmicrosoft.com,,, +test user,testuer@5kswv1.onmicrosoft.com,,, \ No newline at end of file