最近公司打算统一修改AD用户的一些属性,包括SamAccountName,UPN,Office365的UPN,这样保证这些属性和邮件地址是一致的。这些修改本身不难,都可以通过PowerShell批量实现,问题在于修改之后有很多额外的问题,例如Outlook的ost文件啦,通过AD进行LDAP登录的工具了,一些软件的保存路径等需要处理。

修改了AD登录名之后的首要问题就是计算机上的用户配置文件需要进行同步修改。公司没用SCCM,因此只有自己想办法了。豆子做了些测试,基本上需要做以下操作:

  1. 以其他管理员身份登录计算机;

  2. 确认该用户abc已经退出登录状态,可以通过任务管理器或者quser来操作

  3. 修改C:\users\abc 的文件名为新的用户名C:\users\abc1

  4. 修改注册表,这个里面有一堆根据SID命名的key,需要找到对应的,然后修改对应的profileImagePath

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

  5. 创建新的symboLink连接从 c:\users\abc  <==> c:\users\abc1。windows下面有自带的mklink命令可以使用,比如 mklink /D c:\users \abc c:\users\abc1。PS5以后可以用New-item创建,但是早期的版本没有原生的PS命令,只能间接调用cmd,或者自己写一个方法

上面的操作都可以通过PS脚本来实现。

#创建SymLink的方法,这个网上发现有现成的,我就直接下载了function New-Symlink {    <#    .SYNOPSIS        Creates a symbolic link.    #>    param (        [Parameter(Position=0, Mandatory=$true)]        [string] $Link,        [Parameter(Position=1, Mandatory=$true)]        [string] $Target    )    Invoke-MKLINK -Link $Link -Target $Target -Symlink}function New-Hardlink {    <#    .SYNOPSIS        Creates a hard link.    #>    param (        [Parameter(Position=0, Mandatory=$true)]        [string] $Link,        [Parameter(Position=1, Mandatory=$true)]        [string] $Target    )    Invoke-MKLINK -Link $Link -Target $Target -HardLink}function New-Junction {    <#    .SYNOPSIS        Creates a directory junction.    #>    param (        [Parameter(Position=0, Mandatory=$true)]        [string] $Link,        [Parameter(Position=1, Mandatory=$true)]        [string] $Target    )    Invoke-MKLINK -Link $Link -Target $Target -Junction}function Invoke-MKLINK {    <#    .SYNOPSIS        Creates a symbolic link, hard link, or directory junction.    #>    [CmdletBinding(DefaultParameterSetName = "Symlink")]    param (        [Parameter(Position=0, Mandatory=$true)]        [string] $Link,        [Parameter(Position=1, Mandatory=$true)]        [string] $Target,        [Parameter(ParameterSetName = "Symlink")]        [switch] $Symlink = $true,        [Parameter(ParameterSetName = "HardLink")]        [switch] $HardLink,        [Parameter(ParameterSetName = "Junction")]        [switch] $Junction    )    # Ensure target exists.    if (-not(Test-Path $Target)) {        throw "Target does not exist.`nTarget: $Target"    }    # Ensure link does not exist.    if (Test-Path $Link) {        throw "A file or directory already exists at the link path.`nLink: $Link"    }    $isDirectory = (Get-Item $Target).PSIsContainer    $mklinkArg = ""    if ($Symlink -and $isDirectory) {        $mkLinkArg = "/D"    }    if ($Junction) {        # Ensure we are linking a directory. (Junctions don't work for files.)        if (-not($isDirectory)) {            throw "The target is a file. Junctions cannot be created for files.`nTarget: $Target"        }        $mklinkArg = "/J"    }    if ($HardLink) {        # Ensure we are linking a file. (Hard links don't work for directories.)        if ($isDirectory) {            throw "The target is a directory. Hard links cannot be created for directories.`nTarget: $Target"        }        $mkLinkArg = "/H"    }    # Capture the MKLINK output so we can return it properly.    # Includes a redirect of STDERR to STDOUT so we can capture it as well.    $output = cmd /c mklink $mkLinkArg `"$Link`" `"$Target`" 2>&1    if ($lastExitCode -ne 0) {        throw "MKLINK failed. Exit code: $lastExitCode`n$output"    }    else {        Write-Output $output    }}   #定义一个Flag跳出循环$flag=$truewhile($flag){    $oldName=read-host "Please input the old user name"    write-host 'Searching user profile..' -ForegroundColor Cyan         #测试该用户是否已经登录,这里有个小技巧把quser的字符串结果转换为对象,具体解释参考博客    http://beanxyz.blog.51cto.com/5570417/1906162    if (Test-Path "c:\users\$oldName"){        write-host "User Profile c:\users\$oldName found." -ForegroundColor Cyan        #Check if the user is currently logged In        $quser = (quser) -replace '\s{2,17}', ',' | ConvertFrom-Csv        $sessionId = $quser | Where-Object { $_.Username -eq $newName } | select -ExpandProperty id                 #如果已经登录,那么强行退出这个用户        foreach($id in $sessionId){            if($id -ne $null){                write-host "Detected User $newName still login" -ForegroundColor red                Write-Host "Force logoff the user" -ForegroundColor red                logoff $id            }                 }                $newName=read-host "Please input the new name"        $oldpath="c:\users\$oldName"        $newpath="c:\users\$newName"                 #重命名文件夹        rename-item $oldpath $newpath -Confirm -ErrorAction Stop        write-host "Searching Registry Information " -ForegroundColor Cyan                 #查询对应的注册表Key        Get-ChildItem "hklm:\software\microsoft\windows nt\currentversion\profilelist" | foreach{            #Get the username from SID            $sid=$_.Name.Split('\')[-1];                         #根据SID来匹配用户,如果用户匹配成功,那么修改对应的ProfileList            try{            $objSID = New-Object System.Security.Principal.SecurityIdentifier ($sid)            $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])             $username=$objUser.Value            }            catch{}            #change registry keys            if(($username -eq "omnicom\$oldName") -or ($username -eq "omnicom\$newName")){                write-host "Found Registry Information of user profile $newName" -ForegroundColor Cyan                $keys=Get-ItemProperty "hklm:\software\microsoft\windows nt\currentversion\profilelist\$sid"                 $keys.ProfileImagePath=$newpath                write-host "Registry key profile list is changed to $newpath" -ForegroundColor Cyan                                                  #调用上面的方法,创建Symbolink                #Create new symbolink                #New-Item -Path $oldpath -ItemType Junction -Value $newpath                New-Symlink -Link $oldpath -Target $newpath                                 break;            }            else{                write-host "$username Name not match...skip" -ForegroundColor Yellow                         }                 }        $flag=$false             }    else {        write-host "Profile is not found. Please try again" -ForegroundColor red    }}

执行效果,我直接把这个文件扔到一个远程电脑的C盘下测试,然后以本地管理员身份登录,执行这个脚本,成功!