📜 ⬆️ ⬇️

DLP do-it-yourself system

In addition to the main task of preventing the leakage of confidential information, DLP systems may also have secondary (additional) tasks. These include:


Our manual DLP system will not solve all the tasks, and focus only on:


Finding files on Windows is a simple and trivial task. Even searching on a remote machine is not much more difficult. But if you need something to find on hundreds of machines, then the question arises how? Do not use your hands to go through all the PCs. This task is quite common in the work of Windows admins, when, for example, an audit of information storage is needed. You will say that there are examples of implementation, yes, but they are more aimed at searching for data that is prohibited for storage (movies, games, etc.), the variant I proposed implements one of the tasks of the DLP system.
')
And so what can a script (or rather a set of scripts)? In order not to bother the administrator and unload the list of PCs for checking, the script integrates with AD and receives the information it needs, it is possible to filter by extension and name, add exceptions, generate a report in a folder and send the message to the administrator and user (Notification of detected files and recommendations to be taken). Parameters can be combined and changed, getting the necessary functionality. After the search, a list of files is created with the names of the PCs in which information on the search is stored. The second part of the script is to delete the found files, all or according to certain criteria.

We use this script to track users and notify them about the need to store working documents in a specific place (personal network drive), the script identifies the active user on the PC, takes the mailbox data from the AD and sends notifications about which file is found and where on a local disk that the user must move to the network storage or it will be deleted. In the end, we delete, what is not according to the regulations. Thus, we realize one of the functions of DLP-systems for controlling the storage of confidential information.

File Script Algorithm




  1. Gets a list of workstations from a specific OU
  2. Checks the data in the HomePage attribute, if it is “Pass”, skips searching for files, as this computer has already searched
  3. Checks availability
  4. If not available, writes this to a file.
  5. If available, searches for files.
  6. At the end of the search, writes to the attribute HomePage - the value "Pass"
  7. The file is formed by the name of the machine and the list of found files.
  8. Message is sent to the administrator with an attachment
  9. Specifies the name of the local user.
  10. Find out in AD mailing address of the user
  11. Sends a copy of the report

Algorithm bypass reset script




  1. Get list of cars from AD
  2. Sets the value of the attribute notpass

Thus, all the machines fall into the script processing field, including those that have already been scanned.

File Delete Script Algorithm




  1. Load script contents
  2. For each line in the list (result), deletes the object on the remote computer

Setting up the script and running (file auditing)


The script is executed as a command with the specified parameters. Below are examples of running the script and its parameters. Start-AuditFiles - the command that executes the script. Parameters can be combined, as required by the task.

Example 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 

In this example, the search is performed on computers from the OU, all files with the extension (* .doc, *. Docx, *. Sys) except files (* File1 *, * File2 *), except directories (* Folder1 *, * Folder2 *) , the report is duplicated in the directory (\\ server \ reports \). The report is sent to the user and administrator. The number of threads is 5.

Example 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 

In this example, the search is performed on computers (ws-pc-4902, ws-pc-098), all files with the extension (* .doc, *. Docx, *. Sys) except files (* File1 *, * File2 *). The report is sent only to the administrator. The number of threads is 10.

Target computer settings


OU ( required or RemoteComputer must be specified ) - path to the organizational unit with target computers, if this parameter is not specified, the RemoteComputer parameter should be specified. One of these two parameters should be used in the script.

Example: -OU "OU = Test, DC = root, DC = local" or -OU $ Computerlist (the variable is set in combination with other scripts).

RemoteComputer ( mandatory or OU must be specified ) - is set if it is necessary to execute the script only for certain computers from the list, either one specific or several, specifying them with a comma.

Example: -RemoteComputer ws-pc-4902, ws-pc-0982

Search options


IncludeFile ( required, it is possible to use a mask *) - a list of files or their extensions to be searched (may be a list).

ExclusionFile ( optional ) - a list of files that should be excluded from the search (can be a list).

ExclusionFolder ( optional ) - the list of excluded directories.

Report Options


ReportPath ( optional ) - the path to a network resource or local directory to which the scan results will be copied.

AdminMail ( optional ) - the address on whose behalf the report is sent, reports destined for the administrator will be delivered to the same address.

SMTP ( optional ) - The name of the SMTP server used as the sending gateway.

AdminOnly ( optional ) - enables the report sending mode only to the administrator.

Throttle ( mandatory, numeric value from 1 - to 99 ) - sets the number of scan threads.

Module installation


It is necessary in the directory "C: \ Windows \ system32 \ WindowsPowerShell \ v1.0 \ Modules", copy the files:
Invoke-Parallel.psm1
Start-AuditFiles.psm1

Before running the script, modules must be imported:

 Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Invoke-Parallel.psm1 Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1 

Script (module)


This script must be saved as an Invoke-Parallel.psm1 file.

Script 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() } } } 


The following script must be saved as a Start-AuditFiles.psm1 file .

Script 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 } 


Scheduled Run


Create a file in the example directory c: \ scripts.

RunScript.ps1 file

Copy the text:

 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 

Creating a file search schedule



Information on results and work files


The script stores the scan results in the% TEMP% directory. In the example, this directory is: C: \ Users \ Administrator \ AppData \ Local \ Temp. If the computer is available and the file is found, a file is created with the result. If the computer is not available - information about this is added to the offline.txt file.



Delete found files


Run PowerShell run the command:

 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 - performs a search of all search results, processing each result will remove files.

You can specify a specific file (specific computer). For example:

Remove-AuditFiles - TargetFile ws-9281.txt, ws-8721.txt

Reset scanned computers list


Run PowerShell run the command:

Reset-auditcomputers

After execution, the computers in the specified OU will be marked as not scanned:

 Import-Module C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Start-AuditFiles.psm1 Reset-AuditComputers - TargetOU OU "OU=Test,DC=root,DC=local" 

Script description
The $ Body variable contains the text that will later be inserted into the body of the letter. In the future, this text will be sent in a letter to the end user.

 $Body = ",              .     ,            .     : 1)         2)         . 

The beginning of the function, which is given the derived name Start-AuditFiles:

 Function Start-AuditFiles { 

Description of the script, the accompanying help text. Allows you to use help, get information about examples and syntax using "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 ... #> 

We describe the variables that will be used in the function below:

$ OU - the path to the Organization Unit in Active Directory
$ ExclusionFile - list of files excluded from the search
$ ReportPath - path to the directory where reports will be duplicated
$ AdminMail - email address of the administrator from which reports are sent and where the copy intended for the administrator will be sent
$ IncludeFile - file extensions or file names that are searched
$ ExclusionFolder - list of folders excluded from the search
$ AdminOnly - the parameter corresponds to whether reports will be sent only to the administrator
$ SMTP - the address or name of the SMTP server
$ Throttle - the number of parallel threads.

 [CmdletBinding()] Param ( [String]$OU, [String[]]$RemoteComputer, [String[]]$ExclusionFile, [String]$ReportPath, [String]$AdminMail, [String[]]$IncludeFile, [String[]]$ExclusionFolder, [Switch]$AdminOnly = $false, [String]$SMTP, [String]$Throttle = 5 ) 

A check is performed if the “RemoteComputer” key was used, in this case, the scan will take place only on a specific (specified) computer, if the “OU” key is specified - in this case the list will be obtained from the defined OU AD, computers with the HomePage attribute will not be selected “Pass” (this is done in order to exclude a second search on machines that have already been searched).

 If (!$RemoteComputer) {$Hosts = (Get-ADComputer -Filter * -SearchBase $OU -Properties * | where { ( $PSItem.HomePage -notlike 'pass' )} ).name} else { $Hosts = $RemoteComputer } 

The beginning of the script execution, the Host value and the number of threads are substituted:

 invoke-parallel -InputObject $Hosts -throttle $Throttle -ImportVariables -ScriptBlock { 

Check the availability of a computer on the network:

 if(Test-Connection -ComputerName $_ -BufferSize 16 -quiet -count 2) { 

The values ​​of the variables required to perform the search are taken from the variables described above. The beginning of the search is remembered.

  $Object = $_ $ErrorActionPreference = 'SilentlyContinue' $ExclusionFolder2 = $ExclusionFolder -replace ",","|" $StartTime = (Get-Date).ToString() $Hosts 

Performing a search:

  • get a list of physical disks (Get-WMIObject Win32_LogicalDisk -filter "DriveType = 3" -ComputerName $ Object)
  • UNC path is formed with a drive letter (Get-ChildItem ('\\' + $ Object + '\' + ($ _. DeviceID) .remove (1) + '$ \ *')
  • specifies the search key for included objects and exclusions (-Include $ IncludeFile -Exclude $ ExclusionFile -Recurse -Force)
  • exception for directories, remove unnecessary directories from the search results (? {$ PSItem.FullName -notmatch $ ExclusionFolder2}}). FullName)
  • report is generated in the temporary directory (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 } 

Source: https://habr.com/ru/post/275035/


All Articles