使用PowerShell脚本通过ntuser.dat更正用户配置文件路径

时间:2017-12-27 19:47:07

标签: powershell profile

我需要修改创建的现有脚本(在我之前),通过他们的ntuser.dat文件修改注册表中的用户配置文件路径。必须在本地复制文件,修改该文件,然后将其复制回服务器。这些服务器是我们的Citrix解决方案中使用的终端服务器(现在是RDS)。我需要让脚本更正注册表中的用户shell文件夹和shell文件夹(新设置在脚本中),并确保它们通过homeDirectory指向正确的主目录(当前脚本指向TS主目录)目录通过TerminalServicesHomeDirectory)。 GPO设置APPDATA和HOMESHARE。运行时,日志文件会吐出以下内容:

2017-12-22 15:23:25 --- Group name to use is test_testusers
2017-12-22 15:23:25 --- Test1script does not have a homeDirectory attribute in AD
2017-12-22 15:23:25 --- Test2script does not have a homeDirectory attribute in AD
2017-12-22 15:23:25 --- Closing log File.

从提示中收集组名称。此脚本需要能够在从输入提示收集的OU上运行。现在为剧本:

<#
.SYNOPSIS
  Correct profile path.

.DESCRIPTION
  This script is used to correct the registry entries for users 
  that were migrated to other servers, to now use relative
  path.

.OUTPUTS
  Log file stored in the current folder.

#>

Function Invoke-Logging
{
<#  
    DESCRIPTION
    -----------
    This function is used to create and update the Log File.    

DEPENDENCIES
------------               
$Script:Log - This parameter is used to declare the log file path,
              if this parameter is left blank errors will be  thrown.  

#>
[CmdletBinding(DefaultParameterSetName='Create')]
param
(
    [Parameter()]
    [string]
    $Msg,
    [Parameter(ParameterSetName='Create')]
    [Switch]
    $Create,
    [Parameter(ParameterSetName='NewLine')]
    [Switch]
    $NewLine,
    [Parameter(ParameterSetName='Append')]
    [Switch]
    $Append,
    [Parameter(ParameterSetName='Break')]
    [Switch]
    $Break,
    [Parameter(ParameterSetName='RemoveLast')]
    [Switch]
    $RemoveLast
)

Begin
{
    [String]$TimeStamp = Get-Date -Format "yyyy-MM-dd H:mm:ss"
    [String]$str = "$TimeStamp --- $Msg"
}

Process
{
    If($Create)
    {

        New-Item $Script:LogPathName -ItemType File -Force | Out-Null
    }
    ElseIf($NewLine)
    {
        $str = ""
    }
    ElseIf($Break)
    {
        $str = "----------------------------------------------------------------------------------------------------------------"
    }
    ElseIf($RemoveLast)
    {
        #Remove the last line by reading all but the last line in to an update.
        $LogContent = Get-Content $Script:LogPathName
        New-Item $Script:LogPathName -ItemType File -Force | Out-Null
        $Str = $LogContent[0..($LogContent.length-2)]
    }
    #Add Content to the log
    Add-Content -Path $Script:LogPathName -Value $Str

    #if script is in debug mode it will write to the log and the screen
    If($Script:Debug){Write-Host $str}
    } 
}

Function Get-Domains
{
<#  
    DESCRIPTION
    -----------
    This function is used to find the DN of a group in AD.  

    DEPENDENCIES
    ------------            
$Script:Domains - This variable stores the domains in the current environment.  

#>

Process
{
    $ForestDomains = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Domains

    ForEach($Domain in $ForestDomains)
    {
        $Script:Domains += "DC=$($Domain.Name.Replace(".", ",DC="))"
        }
    }
}

Function Get-ADObjectDn
{
<#  
DESCRIPTION
-----------
This function is used to find the DN of a group in AD.  

DEPENDENCIES
------------            
None  

#>
Param(
    [Parameter()]
    [string]
    $objectName
)


[System.DirectoryServices.SearchResult]$discoveredObject

Foreach ($keyDomain in $Domains)
{
    [System.DirectoryServices.DirectoryEntry]$dirEntry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$keyDomain")
    [System.DirectoryServices.DirectorySearcher]$dirSearch = New-Object System.DirectoryServices.DirectorySearcher($dirEntry, "(&(objectCategory=group)(|(sAMAccountName=$objectName)(Name=$objectName)))")
    $dirSearch.PropertiesToLoad.Add("ADsPath") | Out-Null        
    $discoveredObject = $dirSearch.FindOne()
    If ($discoveredObject -ne $null)
    {
        $adsPath = $discoveredObject.Properties["adspath"][0].ToString()
        $dirSearch.Dispose()
        $dirEntry.Dispose()
        Return $adsPath
    }
    $dirSearch.Dispose()
    $dirEntry.Dispose()
    }
    Return $null
}

Function Get-ADGroupMembers
{
<#  
DESCRIPTION
-----------
This function is used to return all users and their TerminalServicesHomeDirectory
of a specified group.   

DEPENDENCIES
------------              
None  

#>

Param(
    [Parameter()]
    [string]
    $objectDN
)

[String]$TSHome
$UserData = @()
[String]$dsFilter = "(&(objectClass=user)(memberof:=$($objectDN.Replace(' LDAP://',''))))"
[String]$domainBase = $objectDN.Substring($objectDN.IndexOf("DC="))
[System.DirectoryServices.DirectoryEntry]$deBase = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$domainBase")
[System.DirectoryServices.DirectorySearcher]$dirSearch = New-Object System.DirectoryServices.DirectorySearcher($deBase)
$dirSearch.Filter = $dsFilter
$dirSearch.SearchScope = "Subtree"
$dirSearch.PageSize = 1000
$dirSearch.CacheResults = $false
$dirSearch.PropertiesToLoad.Add("sAMAccountName") | Out-Null
[System.DirectoryServices.SearchResultCollection]$results = $dirSearch.FindAll()

Foreach ($Member in $results)
{
    [System.DirectoryServices.DirectoryEntry]$deUser = New-Object System.DirectoryServices.DirectoryEntry($Member.Properties["adspath"][0].ToString())
    $TSHome = $deUser.TerminalServicesHomeDirectory

    If($TSHome.GetType() -eq [String])
    {
        $UserData += @{Name = $deUser.sAMAccountName.Value;Path = $TSHome}
    }Else
    {
        Invoke-Logging -Append -Msg "$($deUser.sAMAccountName.Value) does not have a TerminalServicesHomeDirectory attribute in AD"
    }

}

$results.Dispose()
$dirSearch.Dispose()
$deBase.Dispose()
return $UserData
}

Function Fix-NTUSER
{
<#  
DESCRIPTION
-----------
This function will copy the ntuser.dat file locally, load it to the registry,
make the updates and then copy it back to the server after renaming the exiting
file on the server. 

DEPENDENCIES
------------

None  

#>
param
(
    [Parameter()]
    [string]
    $Path,
    [Parameter()]
    [string]
    $UserName
)

Begin
{
    $ProfileReg = New-Object System.Text.RegularExpressions.Regex('(\\\\([\w]+\.?)+\\[\w\d\.]+\$?)', 'ignorecase')
    $Path = "$Path\UPM\RDS\UPM_Profile\ntuser.dat"
    $LocalPath = "$Script:CurrentDirectory\$UserName\$UserName.dat"
}

Process
{

    If(Test-Path $Path)
    {
        Try
        {
        New-Item "$Script:CurrentDirectory\$UserName" -Type Directory -ErrorAction SilentlyContinue | Out-Null
        Invoke-Logging -Append -Msg "Copying $Path"
        Copy-Item -Path $Path -Destination $LocalPath -Force
        C:\Windows\system32\reg.exe load "hku\$UserName" $LocalPath | Out-Null

            ForEach($KeyPath in $RegKeys.Keys)
            {
                If (Test-Path HKU:\$UserName\$KeyPath)
                {
                    ForEach($Key in $RegKeys.Item($KeyPath))
                    {
                        $CurrentValue = (Get-ItemProperty -Path HKU:\$UserName\$KeyPath).$Key
                        If($ProfileReg.IsMatch($CurrentValue))
                        {
                            Set-ItemProperty -Path HKU:\$UserName\$KeyPath -Name $Key -Value $ProfileReg.Replace($CurrentValue, '%HOMESHARE%')
                            Invoke-Logging -Append -Msg "Updated HKEY_USERS\$UserName\$KeyPath\$Key From $CurrentValue To $($ProfileReg.Replace($CurrentValue, '%HOMESHARE%'))"
                        }Else
                        {
                            Invoke-Logging -Append -Msg "Nothing to update for HKEY_USERS\$UserName\$KeyPath\$Key $CurrentValue"
                        }
                    }
                }
            }

        C:\Windows\system32\reg.exe unload "hku\$UserName" | Out-Null

        Invoke-Logging -Append -Msg "Renaming current ntuser.dat"
        Move-Item -Path $Path -Destination "$Path.old_$(Get-Date -Format "yyyyMMMdd_HHmm")"

        Invoke-Logging -Append -Msg "Copying good ntuser.dat to original location"
        Copy-Item -Path $LocalPath -Destination $Path

        Invoke-Logging -Append -Msg "Cleaning up local files"
        Remove-Item "$Script:CurrentDirectory\$UserName" -Force -Recurse

        Invoke-Logging -Append -Msg "Completed processing registry updates for $UserName"
        }Catch
        {
            If(Test-Path HKU:\$UserName){C:\Windows\system32\reg.exe unload "hku\$UserName" | Out-Null}
            Invoke-Logging -Append -Msg "ERROR: $($_.Exception.Message)" 
        }
    }Else
    {
        Invoke-Logging -Append -Msg "$Path Not found"
    }
} 
}


[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic')

[String]$Script:LogName = "Fix-SBCPath_$(Get-Date -Format "yyyyMMMdd_HHmm").log"
[String]$Script:CurrentDirectory = Split-Path $($MyInvocation.MyCommand.Path)
[String]$Script:LogFolder = $CurrentDirectory
[String]$Script:LogPathName = "$LogFolder\$LogName"
[String]$Script:ScriptName = "Fix-SBCPath"
[String]$Script:Version = "1.0"
[Boolean]$Script:Debug = $false
[String[]]$Script:Domains = @()
[Int]$CurrentUser = 0
[HashTable]$RegKeys = @{}
$RegKeys.Add('Volatile Environment', @('APPDATA', 'HOMESHARE'))
$RegKeys.Add('Software\Microsoft\Windows\CurrentVersion\Explorer\Shell 
Folders', @('{374DE290-123F-4565-9164-39C4925E467B}', '{56784854-C6CB-462B-
8169-88E350ACB882}', 
'{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}', '{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}', 'Administrative Tools',
'AppData', 'Desktop', 'Favorites', 'My Music', 'My Pictures', 'My Video', 'Personal', 'Programs', 'Start Menu', 'Startup'))
$RegKeys.Add('Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders', @('{374DE290-123F-4565-9164-39C4925E467B}', '{56784854-C6CB-462B-8169-88E350ACB882}', 
'{7D1D3A04-DEBB-4115-95CF-2F29DA2920DA}', '{BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968}', 'Administrative Tools',
'AppData', 'Desktop', 'Favorites', 'My Music', 'My Pictures', 'My Video', 'Personal', 'Programs', 'Start Menu', 'Startup'))


Invoke-Logging -Create -Msg "$Script:ScriptName Version $Script:Version Log Started."
Invoke-Logging -NewLine

Get-Domains

#Get the group name from the user
$GroupName = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the Terminal Services User group to query", "Group Name")

If([String]::IsNullorEmpty($GroupName))
{
Invoke-Logging -Append -Msg "User Cancelled input."
Invoke-Logging -Append -Msg "Closing log File."
Invoke-Logging -NewLine
break
}

Invoke-Logging -Append -Msg "Group name to use is $GroupName"

[String]$GroupDN = Get-ADObjectDn -objectName $GroupName

If($GroupDN -eq $null)
{
    Invoke-Logging -Append -Msg "$GroupName was not found in AD"
}Else
{
#Create a drive for HKEY_USERS to make registry edits as hives are loaded.
New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS -ErrorAction SilentlyContinue | Out-Null

#Get a collection of users and the paths 
$Users = Get-ADGroupMembers -objectDN $GroupDN

ForEach($User in $Users)
{
    $CurrentUser += 1
    If($user.Path -ne $null)
    {
        Write-Progress -Activity "Correcting Users NTUSER.DAT Files" -status "Correcting $($User.Name)" -percentComplete (($CurrentUser / $Users.count)*100)
        Fix-NTUSER -UserName $User.Name -Path $User.Path
    }
}

Remove-PSDrive -Name HKU
}

Invoke-Logging -Append -Msg "Closing log File."
Invoke-Logging -NewLine

更新:尝试获取ADspath时脚本失败。在Get-ADObjectDn函数中,ADspath设置为加载其属性。这将返回null,这是导致脚本失败的原因。有没有更好的方法来加载ADspath?

1 个答案:

答案 0 :(得分:0)

好的,想出了一些帮助。发布的谎言在这里:

Foreach ($Member in $results)
{
    [System.DirectoryServices.DirectoryEntry]$deUser = New-Object System.DirectoryServices.DirectoryEntry($Member.Properties["adspath"][0].ToString())
    $TSHome = $deUser.homeDirectory

    If($TSHome.GetType() -eq [String])
    {
        $UserData += @{Name = $deUser.sAMAccountName.Value;Path = $TSHome}
    }Else
    {
        Invoke-Logging -Append -Msg "$($deUser.sAMAccountName.Value) does not have a homeDirectory attribute in AD"
    }

}

具体在这里:if($ TSHome.GetType() - eq [String])

我正在寻找一个字符串,当我在这里明确地将其更改为集合时:[System.DirectoryServices.DirectoryEntry] $ deUser = New-Object System.DirectoryServices.DirectoryEntry($ Member.Properties [“adspath”] [0]的ToString())

我只需将String更改为System.DirectoryServices.PropertyValueCollection

最终结果如下:

Foreach ($Member in $results)
{
    [System.DirectoryServices.DirectoryEntry]$deUser = New-Object System.DirectoryServices.DirectoryEntry($Member.Properties["ADspath"][0].ToString())
    $TSHome = $deUser.homeDirectory

    If($TSHome.GetType() -eq [System.DirectoryServices.PropertyValueCollection])
    {
        $UserData += @{Name = $deUser.sAMAccountName.Value;Path = $TSHome}
    }Else
    {
        Invoke-Logging -Append -Msg "$($deUser.sAMAccountName.Value) does not have a homeDirectory attribute in AD"
    }

}

该更改允许我设置$ TSHome变量,现在脚本正常运行!

感谢您的帮助,并对令人费解的剧本和解释表示抱歉。