<# .SYNOPSIS Updates Entra ID users based on all available columns in a CSV file. .DESCRIPTION Dynamically maps CSV headers to Microsoft Graph User properties. Compares CSV values to live data and updates only when a difference is found. .PARAMETER CsvPath The full path to the source CSV file. .EXAMPLE Update-EntraUsersDynamic -CsvPath ".\Exports\UserUpdate_20260226.csv" #> function Update-EntraUsersDynamic { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path $_ })] [string]$CsvPath ) process { Set-StrictMode -Version Latest # 1. Setup Logging $LogPath = Join-Path -Path $PSScriptRoot -ChildPath "Logs" if (-not (Test-Path $LogPath)) { New-Item -ItemType Directory -Force | Out-Null } $Timestamp = Get-Date -Format "yyyyMMdd-HHmm" $LogFile = Join-Path -Path $LogPath -ChildPath "DynamicUpdate_$Timestamp.log" try { # 2. Load Data and Headers $UserData = Import-Csv -Path $CsvPath $Columns = $UserData[0].PSObject.Properties.Name # Attributes we should NOT try to update $ExcludedAttributes = @('UserPrincipalName', 'Id', 'DeletedDateTime', 'CreatedDateTime') if (-not (Get-MgContext)) { throw "Not connected to Microsoft Graph. Run Connect-MgGraph." } foreach ($Row in $UserData) { $UPN = $Row.UserPrincipalName if ([string]::IsNullOrWhiteSpace($UPN)) { continue } try { # 3. Fetch current user with all properties defined in CSV headers Write-Verbose "Processing $UPN..." $CurrentTarget = Get-MgUser -UserId $UPN -Property $Columns -ErrorAction Stop $UpdateHash = @{} foreach ($Col in $Columns) { # Skip excluded columns and empty CSV cells if ($Col -in $ExcludedAttributes -or [string]::IsNullOrWhiteSpace($Row.$Col)) { continue } # 4. Compare CSV value to Current Entra ID value $CsvValue = $Row.$Col $CurrentValue = $CurrentTarget.$Col if ($CsvValue -ne $CurrentValue) { $UpdateHash[$Col] = $CsvValue } } # 5. Execute Update if changes exist if ($UpdateHash.Count -gt 0) { Update-MgUser -UserId $UPN @UpdateHash -ErrorAction Stop Write-Output "SUCCESS: Updated $UPN ($($UpdateHash.Keys -join ', '))" "$Timestamp,$UPN,Updated,$($UpdateHash.Keys -join ';')" | Out-File $LogFile -Append } else { Write-Verbose "SKIP: No changes for $UPN" "$Timestamp,$UPN,NoChange,Matched" | Out-File $LogFile -Append } } catch { Write-Warning "ERROR: Could not update $UPN - $($_.Exception.Message)" "$Timestamp,$UPN,Error,$($_.Exception.Message)" | Out-File $LogFile -Append } } } catch { Write-Error "Fatal Script Error: $($_.Exception.Message)" } } }