Загрузка данных


function Get-DFSServers {
    <#
    .SYNOPSIS
        Перечисляет все серверы, используемые в качестве целей в DFS-пространствах домена.
    .DESCRIPTION
        Функция подключается к Active Directory, находит все объекты msDFS-Namespacev2 и msDFS-Linkv2,
        извлекает атрибут msDFS-TargetListv2 (XML с целевыми UNC-путями) и возвращает уникальные имена серверов.
    .PARAMETER DFSRootPath
        Корневой UNC-путь DFS, например \\domain.corporate.ru\ или \\domain.corporate.ru\Namespace.
    .EXAMPLE
        Get-DFSServers -DFSRootPath "\\mydep.corporate.ru\"
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$DFSRootPath
    )

    # Нормализуем путь: убираем завершающие слеши
    $DFSRootPath = $DFSRootPath.TrimEnd('\')
    if ($DFSRootPath -match '^\\\\') {
        $DFSRootPath = $DFSRootPath.Substring(2)
    }

    # Извлекаем домен (первый компонент пути)
    $parts = $DFSRootPath -split '\\'
    $domain = $parts[0]

    # Строим DN для контейнера Dfs-Configuration
    $domainParts = $domain -split '\.'
    $dn = ($domainParts | ForEach-Object { "DC=$_" }) -join ','
    $dfsConfigDN = "CN=Dfs-Configuration,CN=System,$dn"

    Write-Verbose "Подключение к AD: LDAP://$dfsConfigDN"

    try {
        $root = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$dfsConfigDN")
        $searcher = New-Object System.DirectoryServices.DirectorySearcher($root)
        # Ищем и пространства имён, и папки (ссылки)
        $searcher.Filter = "(|(objectClass=msDFS-Namespacev2)(objectClass=msDFS-Linkv2))"
        $searcher.PageSize = 1000
        $searcher.PropertiesToLoad.Add("msDFS-TargetListv2") | Out-Null
        $searcher.PropertiesToLoad.Add("name") | Out-Null
    }
    catch {
        Write-Error "Не удалось подключиться к Active Directory. Проверьте путь и права доступа."
        return
    }

    $servers = @{}   # Хэш-таблица для уникальных имён

    foreach ($result in $searcher.FindAll()) {
        $targetList = $result.Properties["msdfs-targetlistv2"]
        if (-not $targetList -or $targetList.Count -eq 0) { continue }

        $bytes = $targetList[0]
        if (-not $bytes) { continue }

        # Атрибут хранится в кодировке UTF-16 LE (Unicode)
        $xmlString = [System.Text.Encoding]::Unicode.GetString($bytes)
        # Удаляем нулевые символы, которые могут появиться при декодировании
        $xmlString = $xmlString -replace "`0", ""

        try {
            [xml]$xml = $xmlString
            # Ищем все элементы <target> в любом пространстве имён XML
            $targetNodes = $xml.SelectNodes("//target")
            foreach ($node in $targetNodes) {
                $unc = $node.InnerText
                if ($unc -match '^\\\\([^\\]+)') {
                    $serverName = $matches[1]
                    $servers[$serverName] = $true
                }
            }
        }
        catch {
            Write-Warning "Не удалось разобрать XML для объекта '$($result.Properties['name'])'"
        }
    }

    # Возвращаем отсортированный список уникальных серверов
    return $servers.Keys | Sort-Object
}