Перейти к содержимому

Как делегировать права на редактирование gpo windows группе безопасности.

задача создать группу и дать права на редактирования gpo

<#
.SYNOPSIS
    Настраивает права группы для работы с GPO:
    - создание новых GPO;
    - редактирование существующих GPO (всех или точечно выбранных);
    - линковка GPO к OU (с наследованием на дочерние OU).

.DESCRIPTION
    ЧАСТЬ 1 (создание новых GPO):
        Добавляет группу в встроенную группу AD "Group Policy Creator Owners".
        Участники этой группы могут создавать новые GPO и автоматически
        становятся их владельцами/редакторами (но только тех, что создали
        сами - см. примечание ниже).

    ЧАСТЬ 2 (редактирование существующих GPO):
        Перебирает GPO в домене, проверяет, есть ли у группы право
        редактирования, и если нет - выдаёт его через Set-GPPermission.

        По умолчанию обрабатываются ВСЕ GPO домена. Если нужно применить
        права точечно только к конкретным политикам - заполните массив
        $SpecificGpoNames именами нужных GPO; тогда остальные GPO эта
        часть скрипта не тронет.

    ЧАСТЬ 3 (линковка GPO к OU):
        Выдаёт ОДНО наследуемое правило на каждый "корневой" OU из списка
        $LinkRootOUs. Правило распространяется на сам OU и все дочерние OU
        (сколько угодно уровней вниз), но ограничено только объектами типа
        OrganizationalUnit - т.е. НЕ затрагивает пользователей, компьютеров
        и другие объекты внутри этих OU.

        Важно: право "Link GPOs" на OU управляет только атрибутом gPLink
        (какие GPO привязаны к этому OU). Оно НЕ даёт прав на редактирование
        объектов внутри OU и НЕ даёт прав на содержимое самих GPO.

.NOTES
    ВАЖНОЕ ОГРАНИЧЕНИЕ (проверено на практике):
    Когда участник группы создаёт НОВЫЙ GPO, права редактирования на него
    получает лично этот пользователь (как Creator Owner), а НЕ вся группа
    целиком. Штатного механизма в GPMC, который бы автоматически выдавал
    права на новые GPO всей группе, не существует (права по умолчанию для
    новых GPO заданы в схеме AD на уровне всего леса и их менять не стоит
    ради одной группы). Единственный практичный вариант - периодически
    перезапускать Часть 2 этого скрипта (например, по расписанию через
    Task Scheduler), чтобы права на новые GPO подтягивались автоматически
    с небольшой задержкой.

    Требует модули GroupPolicy и ActiveDirectory.
    Запускать от имени пользователя с правами Domain Admin
    (или эквивалентной делегированной учётки для управления GPO/AD ACL).
#>

[CmdletBinding(SupportsShouldProcess = $true)]
param()

# =====================================================================
# ================  НАСТРОЙКИ — МЕНЯЙТЕ ЗНАЧЕНИЯ ЗДЕСЬ  ================
# =====================================================================

# Имя группы, которой выдаём права
$TargetGroupName = "G-GPO Editors"

# Добавлять ли группу в "Group Policy Creator Owners" (право создавать новые GPO)
$GrantCreateNewGpoRight = $true

# Уровень прав на редактирование существующих GPO:
#   "GpoEdit"                     - только редактирование настроек
#   "GpoEditDeleteModifySecurity" - редактирование + удаление + управление правами
$EditPermissionLevel = "GpoEdit"

# Точечное применение: укажите конкретные GPO по имени (DisplayName), чтобы
# права редактирования выдавались ТОЛЬКО им, а не всем GPO в домене.
# Оставьте пустым массивом @(), чтобы обработать ВСЕ GPO домена.
# Пример: $SpecificGpoNames = @("Default Domain Policy", "Firewall Rules")
$SpecificGpoNames = @()

# "Корневые" OU (DistinguishedName), с которых право линковки распространится
# вниз на ВСЕ дочерние OU автоматически (наследование).
# Пример: $LinkRootOUs = @("OU=Company,DC=contoso,DC=com")
# Если хотите выдать право на весь домен - укажите DN корня домена,
# например: @("DC=contoso,DC=com")
$LinkRootOUs = @(
    "DC=contoso,DC=com"
)

# $true  - только показать, что будет сделано, БЕЗ реального изменения прав (безопасный тестовый прогон)
# $false - реально выполнить изменения
$WhatIfOnly = $true

# =====================================================================
# ============  ДАЛЬШЕ КОД, ОБЫЧНО МЕНЯТЬ НЕ НУЖНО  ============
# =====================================================================

Import-Module GroupPolicy -ErrorAction Stop
Import-Module ActiveDirectory -ErrorAction Stop

$ErrorActionPreference = "Stop"

# Надёжное сравнение IdentityReference из ACE с SID нашей группы.
# Get-Acl на пути AD:... иногда возвращает IdentityReference уже
# переведённым в формат "ДОМЕН\Имя" (NTAccount), а не как SID-строку,
# поэтому прямое сравнение строк ненадёжно - переводим в SID и сравниваем так.
function Test-IdentityMatchesGroup {
    param($IdentityReference, $GroupSid)
    try {
        $sid = $IdentityReference.Translate([System.Security.Principal.SecurityIdentifier])
        return ($sid.Value -eq $GroupSid.Value)
    }
    catch {
        return $false
    }
}

# --- Проверяем, что группа существует ---
try {
    $group = Get-ADGroup -Identity $TargetGroupName -ErrorAction Stop
}
catch {
    throw "Группа '$TargetGroupName' не найдена в AD. Проверьте имя и повторите."
}

$groupSid = $group.SID
Write-Host "Целевая группа: $($group.Name) (SID: $groupSid)" -ForegroundColor Cyan
Write-Host ""

# =====================================================================
# ЧАСТЬ 1. Право создавать новые GPO (Group Policy Creator Owners)
# =====================================================================

if ($GrantCreateNewGpoRight) {
    Write-Host "=== Право создания новых GPO ===" -ForegroundColor Yellow

    $creatorOwners = Get-ADGroup -Identity "Group Policy Creator Owners"
    $isMember = Get-ADGroupMember -Identity $creatorOwners | Where-Object { $_.SID -eq $groupSid }

    if ($isMember) {
        Write-Host "Группа уже состоит в 'Group Policy Creator Owners' - пропуск." -ForegroundColor DarkGray
    }
    elseif ($WhatIfOnly -or $PSCmdlet.ShouldProcess($TargetGroupName, "Add to Group Policy Creator Owners")) {
        if ($WhatIfOnly) {
            Write-Host "[WhatIf] Добавил бы группу '$TargetGroupName' в 'Group Policy Creator Owners'" -ForegroundColor DarkYellow
        }
        else {
            try {
                Add-ADGroupMember -Identity $creatorOwners -Members $group
                Write-Host "OK: группа добавлена в 'Group Policy Creator Owners'" -ForegroundColor Green
            }
            catch {
                Write-Warning "Ошибка при добавлении в 'Group Policy Creator Owners': $($_.Exception.Message)"
            }
        }
    }

    Write-Host ""
}

# =====================================================================
# ЧАСТЬ 2. Право редактировать существующие GPO
# =====================================================================

Write-Host "=== Проверка прав редактирования существующих GPO ===" -ForegroundColor Yellow

if ($SpecificGpoNames.Count -gt 0) {
    Write-Host "Режим точечного применения: обрабатываются только указанные GPO ($($SpecificGpoNames.Count) шт.)" -ForegroundColor Cyan

    $allGpos = @()
    foreach ($name in $SpecificGpoNames) {
        try {
            $allGpos += Get-GPO -Name $name -ErrorAction Stop
        }
        catch {
            Write-Warning "GPO с именем '$name' не найден в домене - пропуск."
        }
    }
}
else {
    $allGpos = Get-GPO -All
}

$gposWithoutEditRights = @()

foreach ($gpo in $allGpos) {
    $perms = Get-GPPermission -Guid $gpo.Id -All -ErrorAction SilentlyContinue

    $hasEditRights = $perms | Where-Object {
        $_.Trustee.Sid -eq $groupSid -and
        $_.Permission -in @("GpoEdit", "GpoEditDeleteModifySecurity", "GpoCustom")
    }

    if (-not $hasEditRights) {
        $gposWithoutEditRights += $gpo
    }
}

Write-Host "Найдено GPO без прав редактирования у группы '$TargetGroupName': $($gposWithoutEditRights.Count)" -ForegroundColor Cyan

if ($gposWithoutEditRights.Count -gt 0) {
    $gposWithoutEditRights | Select-Object DisplayName, Id | Format-Table -AutoSize
}

foreach ($gpo in $gposWithoutEditRights) {
    if ($WhatIfOnly -or $PSCmdlet.ShouldProcess($gpo.DisplayName, "Grant $EditPermissionLevel to $TargetGroupName")) {
        if ($WhatIfOnly) {
            Write-Host "[WhatIf] Выдал бы '$EditPermissionLevel' на GPO: $($gpo.DisplayName)" -ForegroundColor DarkYellow
        }
        else {
            try {
                Set-GPPermission -Guid $gpo.Id `
                    -TargetName $TargetGroupName `
                    -TargetType Group `
                    -PermissionLevel $EditPermissionLevel `
                    -Replace:$false | Out-Null

                Write-Host "OK: выдано '$EditPermissionLevel' на GPO: $($gpo.DisplayName)" -ForegroundColor Green
            }
            catch {
                Write-Warning "Ошибка при выдаче прав на GPO '$($gpo.DisplayName)': $($_.Exception.Message)"
            }
        }
    }
}

Write-Host ""

# =====================================================================
# ЧАСТЬ 3. Право линковки GPO к OU (с наследованием на дочерние OU)
# =====================================================================

Write-Host "=== Право линковки GPO (Link GPOs), с наследованием ===" -ForegroundColor Yellow

# GUID свойства gPLink (сама привязка GPO к контейнеру - запись в это свойство)
$gpLinkPropertyGuid = [Guid]"f30e3bbe-9ff0-11d1-b603-0000f80367c1"

# GUID класса объекта organizationalUnit - ограничиваем наследование только OU,
# чтобы правило не "утекало" на пользователей/компьютеров внутри OU
$organizationalUnitClassGuid = [Guid]"bf967aa5-0de6-11d0-a285-00aa003049e2"

foreach ($rootDn in $LinkRootOUs) {
    try {
        $rootObject = Get-ADObject -Identity $rootDn -ErrorAction Stop
    }
    catch {
        Write-Warning "Не удалось найти объект '$rootDn': $($_.Exception.Message)"
        continue
    }

    $adPath = "AD:$rootDn"
    $acl = Get-Acl -Path $adPath

    $existingRule = $acl.Access | Where-Object {
        (Test-IdentityMatchesGroup -IdentityReference $_.IdentityReference -GroupSid $groupSid) -and
        $_.ObjectType -eq $gpLinkPropertyGuid -and
        $_.ActiveDirectoryRights -match "WriteProperty"
    }

    if ($existingRule) {
        Write-Host "Пропуск (правило уже есть): $rootDn" -ForegroundColor DarkGray
        continue
    }

    if ($WhatIfOnly -or $PSCmdlet.ShouldProcess($rootDn, "Grant inheritable Link GPOs to $TargetGroupName")) {
        if ($WhatIfOnly) {
            Write-Host "[WhatIf] Выдал бы наследуемое право Link GPOs на: $rootDn (и все дочерние OU)" -ForegroundColor DarkYellow
        }
        else {
            try {
                # 6-параметрический конструктор: включает objectType (какое право)
                # и inheritedObjectType (к объектам какого класса это наследование применяется)
                $rule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
                    $groupSid,
                    [System.DirectoryServices.ActiveDirectoryRights]"WriteProperty",
                    [System.Security.AccessControl.AccessControlType]::Allow,
                    $gpLinkPropertyGuid,
                    [System.DirectoryServices.ActiveDirectorySecurityInheritance]::All,
                    $organizationalUnitClassGuid
                )

                $acl.AddAccessRule($rule)
                Set-Acl -Path $adPath -AclObject $acl

                Write-Host "OK: выдано наследуемое право Link GPOs на: $rootDn (и все дочерние OU)" -ForegroundColor Green
            }
            catch {
                Write-Warning "Ошибка при выдаче прав линковки на '$rootDn': $($_.Exception.Message)"
            }
        }
    }
}

Write-Host ""
Write-Host "Готово." -ForegroundColor Cyan

Similar Posts:

Метки:

Добавить комментарий

Яндекс.Метрика