Многие системные администраторы в своей практике сталкиваются с файлопомойками, на которых их коллеги нерадивые назначали права к файлам и папкам напрямую пользователю, а не группе доступа. А пользователей со временем удаляли из домена. Как итог имеем гору файлов и папок, у которых в разделе безопасность мы видим гору sid ов. Чтобы побороть данную проблему предлагаю небольшой powershell скрипт

# Каталог в котором выполнять проверку
$folder="D:\DT$\data"
# Получаем список всех файлов, папок и подпапок
$resc = gci -recurse $folder
# Построчно обрабатываем полученный массив
foreach($r in $resc){
    # Получаем acl лист объекта, в случае ошибки выводим путь до  проблемного файла/папки
    try {$acl = (Get-Item $r.fullname ).GetAccessControl('Access') }  catch { "error path" + $r.fullname  }
    # Отсортируем явно назначенные и содержащие в названии кусок SID
    $acesToRemoves = $acl.Access | ?{ $_.IsInherited -eq $false -and $_.IdentityReference -like "S-1-*"}
    # На случай если  несколько таких потеряшек у объекта
    foreach ( $acesToRemove in $acesToRemoves){
        # Отфильтровываем пустые переменные дабы избежать ошибок
        if ($acesToRemove){
            $r.fullname # Выводим с чем работаем
            # Удаляем sid из acl листа и применяем новый acl на папку/файл
            $acl.RemoveAccessRuleAll($acesToRemove)
            Set-Acl -AclObject $acl $r.fullname
        }
    }
}

Ну, к сожалению ничего не идеально в этом мире, да и пользователи любят поэмы сочинять в названиях файлов. Остается только безотказный способ

ищем проблемные объекты при помощи утилиты AccessEnum https://learn.microsoft.com/ru-ru/sysinternals/downloads/accessenum и чистим ручками

Комментарии (5)


  1. gotch
    22.07.2024 13:37
    +3

    Нет у PowerShell линтера, но это не повод так код оформлять

    # Каталог в котором выполнять проверку
    $folder = "D:\DT$\data"
    # Получаем список всех файлов,папок и подпапок
    $resc = Get-ChildItem -recurse $folder
    # Построчно обрабатываем полученный масив
    foreach ($r in $resc) {
        # Получаем acl лист обьета, в случае ошибки выводим путь до проблемного файла/папки
        try {$acl = (Get-Item -Path $r.fullname).GetAccessControl('Access')} catch {"error path" $r.fullname}
        # Отсортируем явно назначенные и содержащие в названии кусок SID
        $acesToRemoves = $acl.Access|?{$_.IsInherited -eq $false -and $_.IdentityReference -like "S-1-*"}
        # На случай если несколько таких потеряшек у обьекта
        foreach ($acesToRemove in $acesToRemoves) {
            # Отфильтровываем пустые переменые дабы избежать ошибок
            if ($acesToRemove) {
                $r.fullname #выводим с чем работаем
                # Удаляем sid из acl листа и применяем новый acl на папку/файл
                $acl.RemoveAccessRuleAll($acesToRemove)
                Set-Acl -AclObject $acl $r.fullname
            }
        }
    }


    1. fiverok Автор
      22.07.2024 13:37

      я не великий кодер, оформляю код чтобы можно было легко его читать, если критикуешь предлагай как лучше сделать, я всегда рад советам экспертов


      1. gotch
        22.07.2024 13:37
        +1

        Так вроде и предложил?

        • висящие пробелы (в конце строки)

        • неправильное оформление пробелами конструкций foreach

        • неправильные отступы в блоках

        • использование алиасов командлетов

        • использование параметров по умолчанию при вызове командлетов

        • разный регистр одной переменной

        • оформление комментариев

        https://poshcode.gitbook.io/powershell-practice-and-style

        Плюс справка, например foreach - в ней будут примеры правильного оформления.


        1. fiverok Автор
          22.07.2024 13:37

          о спасибо, обязательно изучу


  1. ildarz
    22.07.2024 13:37

    try .... catch

    Не сработает в случае non-terminating errors (хотя бы в случае недоступности файла по пути). Имеет смысл ставить -ErrorAction Stop в команду, чьи ошибки обрабатываете.

    $acl = (Get-Item $r.fullname ).GetAccessControl('Access')

    Но зачем?! У вас уже получен объект, зачем повторный Get-Item? И почему не Get-Acl?

    catch {"error path" $r.fullname}

    ... и выполнение пошло дальше, хотя выполнять уже нечего на этой итерации. В данном случае некритично, ибо переменная обнулится, но иногда может быть крайне чревато, и надо ставить continue или break.

    if ($acesToRemove)

    Не уверен в осмысленности этой проверки. Если у вас будет пустой массив acl, весь foreach просто не сработает, пустому элементу тут тоже взяться неоткуда по логике скрипта.

    Set-Acl -AclObject $acl $r.fullname

    Это надо выносить на уровень выше (вызывать один раз после того, как удалением лишних правил окончательно сформировали нужный acl). Иначе у вас на каждое удаленное правило будет применение на все нижележащее дерево, что замедлит исполнение в разы без какой бы то ни было необходимости.