Помимо основной задачи предотвращения утечек конфиденциальной информации у DLP-системы могут быть и второстепенные (дополнительные) задачи. К ним относятся:
Наша ручная DLP система не будет решать все поставленные задачи, а сосредоточиться только на:
Поиск файлов на ОС Windows, простая и тривиальная задача. Даже поиск на удаленной машине не намного сложнее. А вот если нужно, что то найти на сотни машинах, тут уж возникает вопрос как? Не руками же проходиться по всем ПК. Данная задача довольно часто встречается в работе Windows админов, когда например, нужно провести аудит по хранению информации. Вы скажете, что есть примеры реализации, да, но они больше нацелены на поиск запрещенной для хранения данных (фильмов, игр и т.п), предложенный мной вариант реализует одну из задач DLP системы.
И так что умеет скрипт (точнее набор скриптов)? Что бы не утруждать администратора и не выгружать список ПК для проверки, скрипт интегрируется с AD и получает нужную ему информацию, имеется возможность фильтра по расширению и имени, добавления исключений, формирования отчета в папке и с возможностью отослать сообщение администратору и пользователю (Оповещение об обнаруженных файлах и рекомендациям, которое необходимо предпринять). Параметры можно комбинировать и менять, получая нужный функционал. После поиска формируется список файлов с именами ПК в которых хранится информация по поиску. Вторая часть скрипта это удаление найденных файлов, всех или по определенным критериям.
Мы используем этот скрипт с целью слежения за пользователями и сообщения им о необходимости хранить рабочие документы в определенном месте (личном сетевом диске), скрипт определяет активного пользователя на ПК, берет данные о почтовом ящике из АД и отсылает уведомления о том какой файл и где обнаружен на локальном диске, который пользователь должен переместить на сетевое хранилище либо он будет удален. В конечном итоге удаляем, то, что не по регламенту. Таким образом, реализуем одну из функций DLP-систем по контролю хранения конфиденциальной информации.
![](https://habrastorage.org/r/w780q1/files/ba9/525/5bf/ba95255bf6da4fcf900ecf9155d64a71.jpg)
![](https://habrastorage.org/r/w780q1/files/ef8/fd5/44e/ef8fd544e584406aab5a44da143e4784.jpg)
Таким образом все машины, попадают в поле обработки скрипта, в том числе которые уже были отсканированы.
![](https://habrastorage.org/r/w780q1/files/cfc/25e/4df/cfc25e4df82e4b809d83a3f46d3feeca.jpg)
Скрипт выполняется как команда с заданными параметрами. Ниже приведены примеры запуска скрипта и его параметры. Start-AuditFiles – команда выполняющая скрипт. Параметры можно комбинировать, так как того требует поставленная задача.
В этом примере осуществляется поиск на компьютерах из OU, файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), кроме каталогов (*Folder1*,*Folder2*), отчет дублируется в каталог (\\server\reports\). Отчет отсылается пользователю и администратору. Количество потоков равно 5-ти.
В этом примере осуществляется поиск на компьютерах (ws-pc-4902,ws-pc-098), файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*). Отчет отсылается только администратору. Количество потоков равно 10-ти.
OU (обязательный или необходимо задать RemoteComputer) – путь к организационной единице с целевыми компьютерами, если этот параметр не указан, следует указать параметр RemoteComputer. Один из двух этих параметров должен быть использован в скрипте.
Пример: -OU «OU=Test,DC=root,DC=local» или -OU $Computerlist (задается переменная в комбинации с другими скриптами).
RemoteComputer (обязательный или необходимо задать OU) – задается если необходимо выполнить скрипт только для определенных компьютеров из списка, либо один определенный, либо несколько указав из через запятую.
Пример: -RemoteComputer ws-pc-4902,ws-pc-0982
IncludeFile (обязательный, возможно использование маски *) – перечень файлов или их расширения, поиск которых необходимо выполнить (может быть списком).
ExclusionFile (опциональный) – перечень файлов, которые необходимо исключить из поиска (может быть списком).
ExclusionFolder (опциональный) – перечень исключенных из поиска каталогов.
ReportPath (опциональный) – путь к сетевому ресурсу или локальному каталогу, в который будет выполняться копирование результатов сканирования.
AdminMail (опциональный) – адрес от имени которого выполняется отправка отчета, на этот же адрес будут доставляться отчеты предназначенные администратору.
SMTP (опциональный) – имя SMTP сервере, используемый в качестве шлюза отправки сообщений.
AdminOnly (опциональный) – включает режим отправки отчетов только администратору.
Throttle (обязательный, числовое значение от 1 – до 99) – устанавливает количество потоков сканирования.
Необходимо в каталог «C:\Windows\system32\WindowsPowerShell\v1.0\Modules», скопировать файлы:
Invoke-Parallel.psm1
Start-AuditFiles.psm1
Перед выполнением скрипта модули необходимо импортировать:
Данный скрипт необходимо сохранить как файл Invoke-Parallel.psm1.
Следующий скрипт необходимо сохранить как файл Start-AuditFiles.psm1.
Создайте файл, в примере каталог c:\scripts.
RunScript.ps1 — файл
Скопируйте текст:
Скрипт хранит результаты сканирования в каталоге %TEMP%. В примере этот каталог: C:\Users\Администратор\AppData\Local\Temp. Если компьютер доступен и файл найден – создается файл с результатом. Если компьютер не доступен – информация об это добавляется в файл offline.txt.
![](https://habrastorage.org/r/w780q1/files/2da/d9c/ca6/2dad9cca651e4a8b8c35e760ea1a1992.jpg)
Запустите PowerShell выполните команду:
Remove-AuditFiles — выполнит поиск всех результатов поиска, обработав каждый результат удалит файлы.
Вы можете указать конкретный файл (конкретный компьютер) Например:
Remove-AuditFiles — TargetFile ws-9281.txt,ws-8721.txt
Запустите PowerShell выполните команду:
Reset-AuditComputers
После выполнения, компьютеры в указанной OU будут отмечены как не отсканированы:
- копирование передаваемых сообщений для расследования инцидентов безопасности, в будущем;
- устранение возможности оправки не только конфиденциальной информации, но и различной нежелательной (спама, оскорбительных выражений, информации эротического содержания, огромных объёмов данных и т.п.);
- фильтрация нежелательной информации при получении, а не только при отправке;
- устранения способов использования информационных ресурсов, сотрудниками, в личных целях;
- уменьшение трафика, оптимизация нагрузки каналов;
- контроль рабочего времени сотрудников.
Наша ручная DLP система не будет решать все поставленные задачи, а сосредоточиться только на:
- поиске определенных файлов в сети;
- составление отчетов и доставка отчетов системному администратору или офицеру безопасности;
- выполнение удаления, по определенным критериям.
Поиск файлов на ОС Windows, простая и тривиальная задача. Даже поиск на удаленной машине не намного сложнее. А вот если нужно, что то найти на сотни машинах, тут уж возникает вопрос как? Не руками же проходиться по всем ПК. Данная задача довольно часто встречается в работе Windows админов, когда например, нужно провести аудит по хранению информации. Вы скажете, что есть примеры реализации, да, но они больше нацелены на поиск запрещенной для хранения данных (фильмов, игр и т.п), предложенный мной вариант реализует одну из задач DLP системы.
И так что умеет скрипт (точнее набор скриптов)? Что бы не утруждать администратора и не выгружать список ПК для проверки, скрипт интегрируется с AD и получает нужную ему информацию, имеется возможность фильтра по расширению и имени, добавления исключений, формирования отчета в папке и с возможностью отослать сообщение администратору и пользователю (Оповещение об обнаруженных файлах и рекомендациям, которое необходимо предпринять). Параметры можно комбинировать и менять, получая нужный функционал. После поиска формируется список файлов с именами ПК в которых хранится информация по поиску. Вторая часть скрипта это удаление найденных файлов, всех или по определенным критериям.
Мы используем этот скрипт с целью слежения за пользователями и сообщения им о необходимости хранить рабочие документы в определенном месте (личном сетевом диске), скрипт определяет активного пользователя на ПК, берет данные о почтовом ящике из АД и отсылает уведомления о том какой файл и где обнаружен на локальном диске, который пользователь должен переместить на сетевое хранилище либо он будет удален. В конечном итоге удаляем, то, что не по регламенту. Таким образом, реализуем одну из функций DLP-систем по контролю хранения конфиденциальной информации.
Алгоритм скрипта по поиску файлов
![](https://habrastorage.org/files/ba9/525/5bf/ba95255bf6da4fcf900ecf9155d64a71.jpg)
- Получает перечень рабочих станций из определенной OU
- Проверяет данные в атрибуте HomePage, если он имеет значение «Pass», пропускает поиск файлов, так как на этом компьютере уже осуществлялся поиск
- Проверяет доступность
- Если не доступен, записывает об этом в файл
- Если доступен, выполняет поиск файлов
- По окончанию поиска, записывает в атрибут HomePage – значение «Pass»
- Формируется файл и именем машины и перечнем найденных файлов
- Оправляется сообщение администратору с вложением
- Определяет имя локального пользователя
- Узнает в AD почтовый адрес пользователя
- Отправляет копию отчета
Алгоритм скрипта сброса обхода
![](https://habrastorage.org/files/ef8/fd5/44e/ef8fd544e584406aab5a44da143e4784.jpg)
- Получает перечень машин из AD
- Устанавливает значение атрибута notpass
Таким образом все машины, попадают в поле обработки скрипта, в том числе которые уже были отсканированы.
Алгоритм скрипта удаления файлов
![](https://habrastorage.org/files/cfc/25e/4df/cfc25e4df82e4b809d83a3f46d3feeca.jpg)
- Загружает содержимое скрипта
- По каждой строке из списка (результата), удаляет объект на удаленном компьютере
Настройка скрипта и запуск (аудит файлов)
Скрипт выполняется как команда с заданными параметрами. Ниже приведены примеры запуска скрипта и его параметры. Start-AuditFiles – команда выполняющая скрипт. Параметры можно комбинировать, так как того требует поставленная задача.
Пример 1
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder “*Folder1*,*Folder2*” -ReportPath \\server\reports\ - Throttle 5
В этом примере осуществляется поиск на компьютерах из OU, файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), кроме каталогов (*Folder1*,*Folder2*), отчет дублируется в каталог (\\server\reports\). Отчет отсылается пользователю и администратору. Количество потоков равно 5-ти.
Пример 2
Start-AuditFiles -RemoteComputer ws-pc-4902,ws-pc-0982 -SMTP smtp.server.com -AdminMail administrator@server.com -Include *.doc,*.docx,*.sys -ExclusionFile *New*,*au* -AdminOnly - Throttle 10
В этом примере осуществляется поиск на компьютерах (ws-pc-4902,ws-pc-098), файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*). Отчет отсылается только администратору. Количество потоков равно 10-ти.
Параметры целевых компьютеров
OU (обязательный или необходимо задать RemoteComputer) – путь к организационной единице с целевыми компьютерами, если этот параметр не указан, следует указать параметр RemoteComputer. Один из двух этих параметров должен быть использован в скрипте.
Пример: -OU «OU=Test,DC=root,DC=local» или -OU $Computerlist (задается переменная в комбинации с другими скриптами).
RemoteComputer (обязательный или необходимо задать OU) – задается если необходимо выполнить скрипт только для определенных компьютеров из списка, либо один определенный, либо несколько указав из через запятую.
Пример: -RemoteComputer ws-pc-4902,ws-pc-0982
Параметры поиска
IncludeFile (обязательный, возможно использование маски *) – перечень файлов или их расширения, поиск которых необходимо выполнить (может быть списком).
ExclusionFile (опциональный) – перечень файлов, которые необходимо исключить из поиска (может быть списком).
ExclusionFolder (опциональный) – перечень исключенных из поиска каталогов.
Параметры отчетов
ReportPath (опциональный) – путь к сетевому ресурсу или локальному каталогу, в который будет выполняться копирование результатов сканирования.
AdminMail (опциональный) – адрес от имени которого выполняется отправка отчета, на этот же адрес будут доставляться отчеты предназначенные администратору.
SMTP (опциональный) – имя SMTP сервере, используемый в качестве шлюза отправки сообщений.
AdminOnly (опциональный) – включает режим отправки отчетов только администратору.
Throttle (обязательный, числовое значение от 1 – до 99) – устанавливает количество потоков сканирования.
Установка модулей
Необходимо в каталог «C:\Windows\system32\WindowsPowerShell\v1.0\Modules», скопировать файлы:
Invoke-Parallel.psm1
Start-AuditFiles.psm1
Перед выполнением скрипта модули необходимо импортировать:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Invoke-Parallel.psm1
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
Скрипт (модуль)
Данный скрипт необходимо сохранить как файл Invoke-Parallel.psm1.
Скрипт Invoke-Parallel.psm1
function Invoke-Parallel {
[cmdletbinding(DefaultParameterSetName='ScriptBlock')]
Param (
[Parameter(Mandatory=$false,position=0,ParameterSetName='ScriptBlock')]
[System.Management.Automation.ScriptBlock]$ScriptBlock,
[Parameter(Mandatory=$false,ParameterSetName='ScriptFile')]
[ValidateScript({test-path $_ -pathtype leaf})]
$ScriptFile,
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[Alias('CN','__Server','IPAddress','Server','ComputerName')]
[PSObject]$InputObject,
[PSObject]$Parameter,
[switch]$ImportVariables,
[switch]$ImportModules,
[int]$Throttle = 20,
[int]$SleepTimer = 200,
[int]$RunspaceTimeout = 0,
[switch]$NoCloseOnTimeout = $false,
[int]$MaxQueue,
[validatescript({Test-Path (Split-Path $_ -parent)})]
[string]$LogFile = "C:\temp\log.log",
[switch] $Quiet = $false
)
Begin {
if( -not $PSBoundParameters.ContainsKey('MaxQueue') )
{
if($RunspaceTimeout -ne 0){ $script:MaxQueue = $Throttle }
else{ $script:MaxQueue = $Throttle * 3 }
}
else
{
$script:MaxQueue = $MaxQueue
}
Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'"
if ($ImportVariables -or $ImportModules)
{
$StandardUserEnv = [powershell]::Create().addscript({
$Modules = Get-Module | Select -ExpandProperty Name
$Snapins = Get-PSSnapin | Select -ExpandProperty Name
$Variables = Get-Variable | Select -ExpandProperty Name
@{
Variables = $Variables
Modules = $Modules
Snapins = $Snapins
}
}).invoke()[0]
if ($ImportVariables) {
Function _temp {[cmdletbinding()] param() }
$VariablesToExclude = @( (Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables )
Write-Verbose "Excluding variables $( ($VariablesToExclude | sort ) -join ", ")"
$UserVariables = @( Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) } )
Write-Verbose "Found variables to import: $( ($UserVariables | Select -expandproperty Name | Sort ) -join ", " | Out-String).`n"
}
if ($ImportModules)
{
$UserModules = @( Get-Module | Where {$StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue)} | Select -ExpandProperty Path )
$UserSnapins = @( Get-PSSnapin | Select -ExpandProperty Name | Where {$StandardUserEnv.Snapins -notcontains $_ } )
}
}
Function Get-RunspaceData {
[cmdletbinding()]
param( [switch]$Wait )
Do {
$more = $false
if (-not $Quiet) {
Write-Progress -Activity "Running Query" -Status "Starting threads"`
-CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"`
-PercentComplete $( Try { $script:completedCount / $totalCount * 100 } Catch {0} )
}
Foreach($runspace in $runspaces) {
$currentdate = Get-Date
$runtime = $currentdate - $runspace.startTime
$runMin = [math]::Round( $runtime.totalminutes ,2 )
$log = "" | select Date, Action, Runtime, Status, Details
$log.Action = "Removing:'$($runspace.object)'"
$log.Date = $currentdate
$log.Runtime = "$runMin minutes"
If ($runspace.Runspace.isCompleted) {
$script:completedCount++
if($runspace.powershell.Streams.Error.Count -gt 0) {
$log.status = "CompletedWithErrors"
Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
foreach($ErrorRecord in $runspace.powershell.Streams.Error) {
Write-Error -ErrorRecord $ErrorRecord
}
}
else {
$log.status = "Completed"
Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
}
$runspace.powershell.EndInvoke($runspace.Runspace)
$runspace.powershell.dispose()
$runspace.Runspace = $null
$runspace.powershell = $null
}
ElseIf ( $runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) {
$script:completedCount++
$timedOutTasks = $true
$log.status = "TimedOut"
Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1]
Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)"
if (!$noCloseOnTimeout) { $runspace.powershell.dispose() }
$runspace.Runspace = $null
$runspace.powershell = $null
$completedCount++
}
ElseIf ($runspace.Runspace -ne $null ) {
$log = $null
$more = $true
}
if($logFile -and $log){
($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append
}
}
$temphash = $runspaces.clone()
$temphash | Where { $_.runspace -eq $Null } | ForEach {
$Runspaces.remove($_)
}
if($PSBoundParameters['Wait']){ Start-Sleep -milliseconds $SleepTimer }
} while ($more -and $PSBoundParameters['Wait'])
}
if($PSCmdlet.ParameterSetName -eq 'ScriptFile')
{
$ScriptBlock = [scriptblock]::Create( $(Get-Content $ScriptFile | out-string) )
}
elseif($PSCmdlet.ParameterSetName -eq 'ScriptBlock')
{
[string[]]$ParamsToAdd = '$_'
if( $PSBoundParameters.ContainsKey('Parameter') )
{
$ParamsToAdd += '$Parameter'
}
$UsingVariableData = $Null
if($PSVersionTable.PSVersion.Major -gt 2)
{
$UsingVariables = $ScriptBlock.ast.FindAll({$args[0] -is [System.Management.Automation.Language.UsingExpressionAst]},$True)
If ($UsingVariables)
{
$List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]'
ForEach ($Ast in $UsingVariables)
{
[void]$list.Add($Ast.SubExpression)
}
$UsingVar = $UsingVariables | Group SubExpression | ForEach {$_.Group | Select -First 1}
$UsingVariableData = ForEach ($Var in $UsingVar) {
Try
{
$Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop
[pscustomobject]@{
Name = $Var.SubExpression.Extent.Text
Value = $Value.Value
NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath)
}
}
Catch
{
Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!"
}
}
$ParamsToAdd += $UsingVariableData | Select -ExpandProperty NewName -Unique
$NewParams = $UsingVariableData.NewName -join ', '
$Tuple = [Tuple]::Create($list, $NewParams)
$bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance"
$GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl',$bindingFlags))
$StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast,@($Tuple))
$ScriptBlock = [scriptblock]::Create($StringScriptBlock)
Write-Verbose $StringScriptBlock
}
}
$ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString())
}
else
{
Throw "Must provide ScriptBlock or ScriptFile"; Break
}
Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)"
Write-Verbose "Creating runspace pool and session states"
$sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
if ($ImportVariables)
{
if($UserVariables.count -gt 0)
{
foreach($Variable in $UserVariables)
{
$sessionstate.Variables.Add( (New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null) )
}
}
}
if ($ImportModules)
{
if($UserModules.count -gt 0)
{
foreach($ModulePath in $UserModules)
{
$sessionstate.ImportPSModule($ModulePath)
}
}
if($UserSnapins.count -gt 0)
{
foreach($PSSnapin in $UserSnapins)
{
[void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null)
}
}
}
$runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host)
$runspacepool.Open()
Write-Verbose "Creating empty collection to hold runspace jobs"
$Script:runspaces = New-Object System.Collections.ArrayList
$bound = $PSBoundParameters.keys -contains "InputObject"
if(-not $bound)
{
[System.Collections.ArrayList]$allObjects = @()
}
if( $LogFile ){
New-Item -ItemType file -path $logFile -force | Out-Null
("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile
}
$log = "" | Select Date, Action, Runtime, Status, Details
$log.Date = Get-Date
$log.Action = "Batch processing started"
$log.Runtime = $null
$log.Status = "Started"
$log.Details = $null
if($logFile) {
($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append
}
$timedOutTasks = $false
}
Process {
if($bound)
{
$allObjects = $InputObject
}
Else
{
[void]$allObjects.add( $InputObject )
}
}
End {
Try
{
$totalCount = $allObjects.count
$script:completedCount = 0
$startedCount = 0
foreach($object in $allObjects){
$powershell = [powershell]::Create()
if ($VerbosePreference -eq 'Continue')
{
[void]$PowerShell.AddScript({$VerbosePreference = 'Continue'})
}
[void]$PowerShell.AddScript($ScriptBlock).AddArgument($object)
if ($parameter)
{
[void]$PowerShell.AddArgument($parameter)
}
if ($UsingVariableData)
{
Foreach($UsingVariable in $UsingVariableData) {
Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)"
[void]$PowerShell.AddArgument($UsingVariable.Value)
}
}
$powershell.RunspacePool = $runspacepool
$temp = "" | Select-Object PowerShell, StartTime, object, Runspace
$temp.PowerShell = $powershell
$temp.StartTime = Get-Date
$temp.object = $object
$temp.Runspace = $powershell.BeginInvoke()
$startedCount++
Write-Verbose ( "Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring() )
$runspaces.Add($temp) | Out-Null
Get-RunspaceData
$firstRun = $true
while ($runspaces.count -ge $Script:MaxQueue) {
if($firstRun){
Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit."
}
$firstRun = $false
Get-RunspaceData
Start-Sleep -Milliseconds $sleepTimer
}
}
Write-Verbose ( "Finish processing the remaining runspace jobs: {0}" -f ( @($runspaces | Where {$_.Runspace -ne $Null}).Count) )
Get-RunspaceData -wait
if (-not $quiet) {
Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed
}
}
Finally
{
if ( ($timedOutTasks -eq $false) -or ( ($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false) ) ) {
Write-Verbose "Closing the runspace pool"
$runspacepool.close()
}
[gc]::Collect()
}
}
}
Следующий скрипт необходимо сохранить как файл Start-AuditFiles.psm1.
Скрипт Start-AuditFiles.psm1
$Body =
", напоминаем Вам о действии приказа о запрете хранения служебной информации на локальных дисках ПК.
В прикрепленном к письму файле, Вы найдете список документов обнаруженных на Вашем ПК потенциально содержащих служебную информацию.
Вам необходимо в кратчайшие сроки:
1) проверить указанные документы на предмет наличия служебной информации
2) переместить файлы содержащие служебную информацию на личный сетевой диск.
Function Start-AuditFiles {
<#
.Synopsis
Сканирует файлы на удаленной машине, в случае успеха отправляет отчет почтовым сообщением
.Description
Сканер позволяет обнаружить искомые файлы на удаленной машине через административный ресурс (C$ D$ .. и т.д).
После выполнения скрипт:
1. Находит объекты комрьютер в опеределенной OU или заданый компьютер через параметр
2. Проверяет доступность машины
3. Выполняет поиск всех дисков
4. Выполняет поиск искомых файлов с фильтрацией, формирует отчет
5. Отправляет отчет администратору
6. Получает активного пользователя, определяет его почтовый адрес, отправляет копию отчета
7. Формирует списки результатов сканирования, опционально копирует результаты на удаленный ресурс
.Examples
Пример 1
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder *Folder1*,*Folder2* -ReportPath \\server\reports\
В этом примере осуществляется поиск на компьютерах из OU, файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), кроме каталогов (*Folder1*,*Folder2*), отчет дублируется в каталог (\\server\reports\)
Пример 2
Start-AuditFiles -RemoteComputer ws-pc-4902,ws-pc-0982 -SMTP smtp.server.com -AdminMail administrator@server.com -Include *.doc,*.docx,*.sys -ExclusionFile *New*,*au* -AdminOnly
В этом примере осуществляется поиск на компьютерах (ws-pc-4902,ws-pc-098), файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), отчет отсылается только администратору
.Notes
Следует использовать только один из ключей OU или RemoteComputer, OU указывает организационную единицу, RemoteComputer указывает один или несколько компьютер в качестве объектов сканирования
.Link
...
#>
[CmdletBinding()]
Param (
[String]$OU,
[String[]]$RemoteComputer,
[String[]]$ExclusionFile,
[String]$ReportPath,
[String]$AdminMail,
[String[]]$IncludeFile,
[String[]]$ExclusionFolder,
[Switch]$AdminOnly = $false,
[String]$SMTP,
[String]$Throttle = 5
)
If (!$RemoteComputer) {$Hosts = (Get-ADComputer -Filter * -SearchBase $OU -Properties * | where { ( $PSItem.HomePage -notlike 'pass' )} ).name} else { $Hosts = $RemoteComputer }
invoke-parallel -InputObject $Hosts -throttle $Throttle -ImportVariables -ScriptBlock {
if(Test-Connection -ComputerName $_ -BufferSize 16 -quiet -count 2) {
$Object = $_
$ErrorActionPreference = 'SilentlyContinue'
$ExclusionFolder2 = $ExclusionFolder -replace ",","|"
$StartTime = (Get-Date).ToString()
$Hosts
(Get-WMIObject Win32_LogicalDisk -filter "DriveType = 3" -ComputerName $Object | %{Get-ChildItem ('\\' + $Object + '\' + ($_.DeviceID).remove(1) + '$\*') -Include $IncludeFile -Exclude $ExclusionFile -Recurse -Force | ?{$PSItem.FullName -notmatch $ExclusionFolder2}}).FullName | Out-File -FilePath $env:TEMP\$Object.txt -Encoding unicode
If (!$ReportPath) {} else {Copy-Item -Path $env:TEMP\$Object.txt -Destination $ReportPath -Force}
$EndTime = (Get-Date).ToString()
Write-Output ($Object) | Add-Content $env:TEMP\Online.txt
Invoke-Item $env:TEMP\$Object.txt
$Results = "" | Select ComputerName, "StartTime", "EndTime"
$Results.ComputerName = $Object
$Results.StartTime = $StartTime
$Results.EndTime =$EndTime
$Results
If ((Get-Content $env:TEMP\$Object.txt) -eq $Null) {}
else {
Try {
Send-MailMessage -SmtpServer $SMTP -to $AdminMail -Body $Object -From denis.pasternak@hotmail.com -Subject $Object -Attachments $env:TEMP\$Object.txt
} Catch {''}
If ($AdminOnly -eq $True) { Write-Host "Включен параметр AdminOnly - отчет отправлен только аминистратору" -ForegroundColor Yellow} else
{
$Username=((gwmi win32_computersystem -computer $Object -ErrorAction SilentlyContinue).UserName -split '\\')[1]
if($username -ne $null)
{
$Body = $Body
$dispalyname = (Get-AdUser $username -properties DisplayName).DisplayName
$email = (Get-AdUser $username -properties mail).mail
sleep -Seconds 3
Send-MailMessage -SmtpServer $SMTP -Body ( 'Уважаемый ' + $Dispalyname + ' ' + $Body | out-string ) -To $email -From $AdminMail -Subject $Object -Attachments $env:TEMP\$Object.txt -Encoding Unicode
}
}
}
else{ }
}
else {
(Write-Output ($Object + ' ' + (Get-Date).ToString()) | Add-Content $env:TEMP\Offline.txt)}
}
$OU= $null
$RemoteComputer = $null
$Hosts = $nul
Get-Content $env:TEMP\Online.txt | Set-ADComputer -HomePage 'pass'
}
Function Remove-AuditFiles {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetFile
)
Get-Content -Path "$env:TEMP\$Path" | %{Remove-Item $PSItem}
}
Function Reset-AuditComputers {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetOU
)
Get-ADComputer -Filter * -SearchBase $TargetOU -Properties * | Set-ADComputer -HomePage 'notpass'
'' | Set-Content -Path $env:TEMP\Online.txt
}
Выполнение по расписанию
Создайте файл, в примере каталог c:\scripts.
RunScript.ps1 — файл
Скопируйте текст:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Invoke-Parallel.psm1
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
# Ниже приведен пример, укажите предпочитаемые параметры и их значения
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder *Folder1*,*Folder2* -ReportPath \\server\reports\ - Throttle 5
Создание расписания поиска файлов
- в управлении компьютером, перейдите в раздел «Планировщик заданий». Создайте задание, введите желаемое имя задания
- укажите периодичность «Ежедневное»
- установите период «каждый день»
- оставьте опцию «Запустить программу»
- путь к программе — C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe Аргумент запуска — C:\Scripts\RunScript.ps1
- после создания, откройте свойство задания, перейдите на вкладку «Триггеры»
- установите опцию «Повторить задачу каждые», указав нужную периодичность повторения
- установите опции «Выполнять для всех пользователей» и «Выполнять с наивысшим приоритетом». Укажите пользователя, имеющего право на чтение всех файлов на удаленных компьютерах. Как правило это пользователи, входящие в группу администраторов на удаленных компьютерах.
Информация о результатах и рабочих файлах
Скрипт хранит результаты сканирования в каталоге %TEMP%. В примере этот каталог: C:\Users\Администратор\AppData\Local\Temp. Если компьютер доступен и файл найден – создается файл с результатом. Если компьютер не доступен – информация об это добавляется в файл offline.txt.
![](https://habrastorage.org/files/2da/d9c/ca6/2dad9cca651e4a8b8c35e760ea1a1992.jpg)
Удаление найденных файлов
Запустите PowerShell выполните команду:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
Remove-AuditFiles
Или
Remove-AuditFiles - TargetFile ws-9281.txt,ws-8721.txt
Remove-AuditFiles — выполнит поиск всех результатов поиска, обработав каждый результат удалит файлы.
Вы можете указать конкретный файл (конкретный компьютер) Например:
Remove-AuditFiles — TargetFile ws-9281.txt,ws-8721.txt
Сброс перечня отсканированных компьютеров
Запустите PowerShell выполните команду:
Reset-AuditComputers
После выполнения, компьютеры в указанной OU будут отмечены как не отсканированы:
Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1
Reset-AuditComputers - TargetOU OU "OU=Test,DC=root,DC=local"
Описание скрипта
Переменная $Body – содержит текст, который в дальнейшем будет вставлен в тело письма. В дальнейшем этот текст будет отправлен в письме конечному пользователю.
Начало функции, которой дано производное название Start-AuditFiles:
Описание скрипта, сопроводительный текст справки. Позволяет использовать помощь, получить информацию о примерах и синтаксисе используя «Get-Help Start-AuditFiles»:
Описываем переменные, которые в дальнейшем будем использовать в функции:
$OU — путь к Organization Unit в Active Directory
$ExclusionFile –перечень файлов, исключённых из поиска
$ReportPath – путь к каталогу, куда будут дублироваться отчеты
$AdminMail – адрес электронной администратора, от которого отправляются отчеты и куда будет приходить копия, предназначенная администратору
$IncludeFile – расширения файлов или имена файлов, поиск которых осуществляется
$ExclusionFolder – перечень папок, исключенных из поиска
$AdminOnly – параметр отвечающий, будут ли отчеты отправляться только администратору
$SMTP – адрес или имя SMTP сервера
$Throttle – количество параллельных потоков.
Выполняется проверка, если использовался ключ «RemoteComputer», в таком случае сканирование пройдет только на определенном (заданном) компьютере, если указан ключ «OU» — в этом случае список будет получен из определённой OU AD, будут выбраны компьютеры с атрибутом HomePage – не равным «pass» (это выполняется, дабы исключить повторный поиск на машинах, на которых уже был проведен поиск).
Начало выполнения скрипта, подставляется значение Host и число потоков:
Проверка доступности компьютера в сети:
Присваиваются значения переменных, необходимых для выполнения поиска взятые из переменных описанных выше. Запоминается начало выполнения поиска.
Выполнение поиска:
Если в момент запуска скрипта, был объявлен параметр «ReportPath», отчет копируется в заданный каталог.
Запоминаем время завершения поиска.
Добавляется запись в лог файл, с успешно завершенными операциями.
Вывод на экран таблицы с результатом поиска, начало поиска и его время его окончание, объект на котором производился поиск.
Открывает содержимое отчета, если он не пустой, выполняет действие (отправка отчета), если пустой ничего не выполняет. Предотвращает отправку пустых отчетов.
Если файл отчета не оказался пустой, формируется сообщение с заданными параметрами SMTP сервера, адресом администратора, тестом сообщения, вложением отчета.
Сообщает на экране о том, что включён решим отправки только администратору, если был указан соответствующий ключ.
Получаем имя пользователя активного на удаленном компьютере.
Если значение пользователя не пустое, ищем пользователя в Active Directory, определяем его значение почтовый адрес из моля Email. Получаем так же ФИО из Active Directory.
Отправляем сообщение пользователю, с телом письма указанным в переменной $Body с подстановкой ФИО взятого из Active Directory.
Если компьютер не был доступен, записываем результат в лог файл, о том что он был не доступен.
Сбрасываем переменные.
Устанавливаем атрибут «HomePage» для объектов компьютеры в Active Directory. Что поможет нам не проводить повторный поиск на этих компьютерах, пока эти значения не будут сброшены.
Функция позволяет, открыть файл, получить содержимое (путь к файлу на удаленном компьютере), по каждой строке удалить файл на удаленном компьютере. $TargetFile – задает путь к файлу в котором хранится список файлов на удаление.
Функция позволяет сбросить атрибут HomePage объектов в Active Directory. Берет список компьютеров из указанной OU. Удаляет список в лог-файле.
$Body =
", напоминаем Вам о действии приказа о запрете хранения служебной информации на локальных дисках ПК.
В прикрепленном к письму файле, Вы найдете список документов обнаруженных на Вашем ПК потенциально содержащих служебную информацию.
Вам необходимо в кратчайшие сроки:
1) проверить указанные документы на предмет наличия служебной информации
2) переместить файлы содержащие служебную информацию на личный сетевой диск.
Начало функции, которой дано производное название Start-AuditFiles:
Function Start-AuditFiles {
Описание скрипта, сопроводительный текст справки. Позволяет использовать помощь, получить информацию о примерах и синтаксисе используя «Get-Help Start-AuditFiles»:
<#
.Synopsis
Сканирует файлы на удаленной машине, в случае успеха отправляет отчет почтовым сообщением
.Description
Сканер позволяет обнаружить искомые файлы на удаленной машине через административный ресурс (C$ D$ .. и т.д).
После выполнения скрипт:
1. Находит объекты комрьютер в опеределенной OU или заданый компьютер через параметр
2. Проверяет доступность машины
3. Выполняет поиск всех дисков
4. Выполняет поиск искомых файлов с фильтрацией, формирует отчет
5. Отправляет отчет администратору
6. Получает активного пользователя, определяет его почтовый адрес, отправляет копию отчета
7. Формирует списки результатов сканирования, опционально копирует результаты на удаленный ресурс
.Examples
Пример 1
Start-AuditFiles -OU "OU=Test,DC=root,DC=local" -SMTP smtp.server.com -AdminMail administrator@server.com -IncludeFile *.doc,*.docx,*.sys -ExclusionFile *File1*,*File2* -ExclusionFolder *Folder1*,*Folder2* -ReportPath \\server\reports\
В этом примере осуществляется поиск на компьютерах из OU, файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), кроме каталогов (*Folder1*,*Folder2*), отчет дублируется в каталог (\\server\reports\)
Пример 2
Start-AuditFiles -RemoteComputer ws-pc-4902,ws-pc-0982 -SMTP smtp.server.com -AdminMail administrator@server.com -Include *.doc,*.docx,*.sys -ExclusionFile *New*,*au* -AdminOnly
В этом примере осуществляется поиск на компьютерах (ws-pc-4902,ws-pc-098), файлы все с расширением (*.doc,*.docx,*.sys) кроме файлов (*File1*,*File2*), отчет отсылается только администратору
.Notes
Следует использовать только один из ключей OU или RemoteComputer, OU указывает организационную единицу, RemoteComputer указывает один или несколько компьютер в качестве объектов сканирования
.Link
...
#>
Описываем переменные, которые в дальнейшем будем использовать в функции:
$OU — путь к Organization Unit в Active Directory
$ExclusionFile –перечень файлов, исключённых из поиска
$ReportPath – путь к каталогу, куда будут дублироваться отчеты
$AdminMail – адрес электронной администратора, от которого отправляются отчеты и куда будет приходить копия, предназначенная администратору
$IncludeFile – расширения файлов или имена файлов, поиск которых осуществляется
$ExclusionFolder – перечень папок, исключенных из поиска
$AdminOnly – параметр отвечающий, будут ли отчеты отправляться только администратору
$SMTP – адрес или имя SMTP сервера
$Throttle – количество параллельных потоков.
[CmdletBinding()]
Param (
[String]$OU,
[String[]]$RemoteComputer,
[String[]]$ExclusionFile,
[String]$ReportPath,
[String]$AdminMail,
[String[]]$IncludeFile,
[String[]]$ExclusionFolder,
[Switch]$AdminOnly = $false,
[String]$SMTP,
[String]$Throttle = 5
)
Выполняется проверка, если использовался ключ «RemoteComputer», в таком случае сканирование пройдет только на определенном (заданном) компьютере, если указан ключ «OU» — в этом случае список будет получен из определённой OU AD, будут выбраны компьютеры с атрибутом HomePage – не равным «pass» (это выполняется, дабы исключить повторный поиск на машинах, на которых уже был проведен поиск).
If (!$RemoteComputer) {$Hosts = (Get-ADComputer -Filter * -SearchBase $OU -Properties * | where { ( $PSItem.HomePage -notlike 'pass' )} ).name} else { $Hosts = $RemoteComputer }
Начало выполнения скрипта, подставляется значение Host и число потоков:
invoke-parallel -InputObject $Hosts -throttle $Throttle -ImportVariables -ScriptBlock {
Проверка доступности компьютера в сети:
if(Test-Connection -ComputerName $_ -BufferSize 16 -quiet -count 2) {
Присваиваются значения переменных, необходимых для выполнения поиска взятые из переменных описанных выше. Запоминается начало выполнения поиска.
$Object = $_
$ErrorActionPreference = 'SilentlyContinue'
$ExclusionFolder2 = $ExclusionFolder -replace ",","|"
$StartTime = (Get-Date).ToString()
$Hosts
Выполнение поиска:
- получение списка физических дисков (Get-WMIObject Win32_LogicalDisk -filter «DriveType = 3» -ComputerName $Object)
- формируется UNC путь с проставлением буквы диска (Get-ChildItem ('\\' + $Object + '\' + ($_.DeviceID).remove(1) + '$\*')
- указывается ключ поиска включенных объектов и исключения (-Include $IncludeFile -Exclude $ExclusionFile -Recurse -Force)
- исключение для каталогов, убираем из результатов поиска не нужные каталоги (?{$PSItem.FullName -notmatch $ExclusionFolder2}}).FullName)
- формируется отчет во временном каталоге (Out-File -FilePath $env:TEMP\$Object.txt -Encoding unicode)
(Get-WMIObject Win32_LogicalDisk -filter "DriveType = 3" -ComputerName $Object | %{Get-ChildItem ('\\' + $Object + '\' + ($_.DeviceID).remove(1) + '$\*') -Include $IncludeFile -Exclude $ExclusionFile -Recurse -Force | ?{$PSItem.FullName -notmatch $ExclusionFolder2}}).FullName | Out-File -FilePath $env:TEMP\$Object.txt -Encoding unicode
Если в момент запуска скрипта, был объявлен параметр «ReportPath», отчет копируется в заданный каталог.
If (!$ReportPath) {} else {Copy-Item -Path $env:TEMP\$Object.txt -Destination $ReportPath -Force}
Запоминаем время завершения поиска.
$EndTime = (Get-Date).ToString()
Добавляется запись в лог файл, с успешно завершенными операциями.
Write-Output ($Object) | Add-Content $env:TEMP\Online.txt
Вывод на экран таблицы с результатом поиска, начало поиска и его время его окончание, объект на котором производился поиск.
Invoke-Item $env:TEMP\$Object.txt
$Results = "" | Select ComputerName, "StartTime", "EndTime"
$Results.ComputerName = $Object
$Results.StartTime = $StartTime
$Results.EndTime =$EndTime
$Results
Открывает содержимое отчета, если он не пустой, выполняет действие (отправка отчета), если пустой ничего не выполняет. Предотвращает отправку пустых отчетов.
If ((Get-Content $env:TEMP\$Object.txt) -eq $Null) {}
Если файл отчета не оказался пустой, формируется сообщение с заданными параметрами SMTP сервера, адресом администратора, тестом сообщения, вложением отчета.
else {
Try {
Send-MailMessage -SmtpServer $SMTP -to $AdminMail -Body $Object -From denis.pasternak@hotmail.com -Subject $Object -Attachments $env:TEMP\$Object.txt
} Catch {''}
Сообщает на экране о том, что включён решим отправки только администратору, если был указан соответствующий ключ.
If ($AdminOnly -eq $True) { Write-Host "Включен параметр AdminOnly - отчет отправлен только аминистратору" -ForegroundColor Yellow} else
Получаем имя пользователя активного на удаленном компьютере.
{
$Username=((gwmi win32_computersystem -computer $Object -ErrorAction SilentlyContinue).UserName -split '\\')[1]
if($username -ne $null)
Если значение пользователя не пустое, ищем пользователя в Active Directory, определяем его значение почтовый адрес из моля Email. Получаем так же ФИО из Active Directory.
{
$Body = $Body
$dispalyname = (Get-AdUser $username -properties DisplayName).DisplayName
$email = (Get-AdUser $username -properties mail).mail
sleep -Seconds 3
Отправляем сообщение пользователю, с телом письма указанным в переменной $Body с подстановкой ФИО взятого из Active Directory.
Send-MailMessage -SmtpServer $SMTP -Body ( 'Уважаемый ' + $Dispalyname + ' ' + $Body | out-string ) -To $email -From $AdminMail -Subject $Object -Attachments $env:TEMP\$Object.txt -Encoding Unicode
Если компьютер не был доступен, записываем результат в лог файл, о том что он был не доступен.
}
}
}
else{ }
}
else {
(Write-Output ($Object + ' ' + (Get-Date).ToString()) | Add-Content $env:TEMP\Offline.txt)}
}
Сбрасываем переменные.
$OU= $null
$RemoteComputer = $null
$Hosts = $nul
Устанавливаем атрибут «HomePage» для объектов компьютеры в Active Directory. Что поможет нам не проводить повторный поиск на этих компьютерах, пока эти значения не будут сброшены.
Get-Content $env:TEMP\Online.txt | Set-ADComputer -HomePage 'pass'
}
Функция позволяет, открыть файл, получить содержимое (путь к файлу на удаленном компьютере), по каждой строке удалить файл на удаленном компьютере. $TargetFile – задает путь к файлу в котором хранится список файлов на удаление.
Function Remove-AuditFiles {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetFile
)
Get-Content -Path "$env:TEMP\$Path" | %{Remove-Item $PSItem}
}
Функция позволяет сбросить атрибут HomePage объектов в Active Directory. Берет список компьютеров из указанной OU. Удаляет список в лог-файле.
Function Reset-AuditComputers {
[CmdletBinding(SupportsShouldProcess=$True)]
Param (
[String]$TargetOU
)
Get-ADComputer -Filter * -SearchBase $TargetOU -Properties * | Set-ADComputer -HomePage 'notpass'
'' | Set-Content -Path $env:TEMP\Online.txt
}