Wednesday, May 20, 2026

Update 365 Tenant user information using powershell and csv file

This script I use to update employee job titles and managers.  It runs monthly for us to sync employee changes in their job titles and managers.  This detects if you try and add a manger that is 'below' the user's current status.  Meaning you can add yourself as a manger to yourself.  Nor could you add an employee that was your subordinate as a manger to yourself.

Create a file called UserUpdates.csv and put in the comma delimited data.

UserPrincipalName,ManagerUPN,JobTitle,Department,OfficeLocation
user1@company.com,manager1@company.com,Systems Administrator,IT,NYC
user2@company.com,manager2@company.com,Network Supervisor,ITOP,FLA

Create a new file called UpdateUser.ps1 and paste in the code that is down below in this post.

Change the path in the script to reflect the path your csv file is located
$csvPath = "C:\updates\UserUpdates.csv"

run the script in powershell (as admin)  .\UpdateUser.ps1

the script will prompt you for a user with rights to make the changes, usually some administrator of the tenant.  Then it will run the script a line at a time.

At time of writing, the prompt that worked for me would look like this style below:  And I would select my admin user that had creds.  I found that sometimes it would give me a different looking one, and i would have to run the script again, because it would ask for creds every single time it ran a new line in the csv. 



# ###########################
# Cobbled together with CoPilot and ChatGTP
# Update the following items via a CSV
# - Manager  Job  Title  Department  Office Location
#
# Added circular hierarchy validation
# Uses Microsoft Graph PowerShell SDK
# #############################

# Install module if needed:
# Install-Module Microsoft.Graph -Scope CurrentUser

Import-Module Microsoft.Graph.Users

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "User.ReadWrite.All","Directory.ReadWrite.All"

# UserPrincipalName,ManagerUPN,JobTitle,Department,OfficeLocation
# user1@company.com,manager@company.com,Systems Administrator,IT,NYC

$csvPath = "C:\updates\UserUpdates.csv"

# Import CSV
$users = Import-Csv -Path $csvPath

# ###########################
# Function: Detect/Prevent Circular Management Structure
# ###########################

function Test-ManagerCircularReference {

    param (
        [string]$UserId,
        [string]$ManagerId
    )

    # Prevent self-manager assignment
    if ($UserId -eq $ManagerId) {
        return $true
    }

    $currentManagerId = $ManagerId

    while ($currentManagerId) {

        # Detect circular hierarchy
        if ($currentManagerId -eq $UserId) {
            return $true
        }

        try {

            $manager = Get-MgUserManager `
                -UserId $currentManagerId `
                -ErrorAction Stop

            if ($manager.Id) {
                $currentManagerId = $manager.Id
            }
            else {
                $currentManagerId = $null
            }
        }
        catch {
            $currentManagerId = $null
        }
    }

    return $false
}

# ###########################
# Process the CSV file
# ###########################

foreach ($entry in $users) {

    $userUPN        = $entry.UserPrincipalName.Trim()
    $managerUPN     = $entry.ManagerUPN.Trim()
    $jobTitle       = $entry.JobTitle.Trim()
    $department     = $entry.Department.Trim()
    $officeLocation = $entry.OfficeLocation.Trim()

    Write-Host ""
    Write-Host "Processing: $userUPN" -ForegroundColor Cyan

    try {

        # ###########################
        # Get User
        # ###########################

        $user = Get-MgUser `
            -UserId $userUPN `
            -ErrorAction Stop

        # ###########################
        # Update User Properties
        # ###########################

        Update-MgUser `
            -UserId $user.Id `
            -JobTitle $jobTitle `
            -Department $department `
            -OfficeLocation $officeLocation

        Write-Host "SUCCESS: Updated user profile properties." `
            -ForegroundColor Green

        # ###########################
        # Update Manager (if provided)
        # ###########################

        if (![string]::IsNullOrWhiteSpace($managerUPN)) {

            $manager = Get-MgUser `
                -UserId $managerUPN `
                -ErrorAction Stop

            # Validate hierarchy
            $hasCircularReference = Test-ManagerCircularReference `
                -UserId $user.Id `
                -ManagerId $manager.Id

            if ($hasCircularReference) {

                Write-Host "SKIPPED: Circular hierarchy detected." `
                    -ForegroundColor Yellow
            }
            else {

                Set-MgUserManagerByRef `
                    -UserId $user.Id `
                    -BodyParameter @{
                        "@odata.id" = "https://graph.microsoft.com/v1.0/users/$($manager.Id)"
                    }

                Write-Host "SUCCESS: Updated manager." `
                    -ForegroundColor Green
            }
        }
        else {

            Write-Host "INFO: No manager specified." `
                -ForegroundColor DarkYellow
        }
    }

    catch {

        Write-Host "FAILED: $($_.Exception.Message)" `
            -ForegroundColor Red
    }
}

# Disconnect Graph Session
Disconnect-MgGraph

Write-Host ""
Write-Host "Processing complete." -ForegroundColor Cyan


No comments:

Post a Comment

Feel free to leave a comment! If you have any information that you think should be included, please do so here and I'll get it added in.