Dans le paysage informatique moderne, la gestion et la maintenance des systèmes de fichiers sur de nombreux serveurs et postes de travail peuvent s’avérer difficiles. Les professionnels de l’informatique, en particulier ceux qui travaillent pour des fournisseurs de services gérés (MSP), ont souvent besoin de contrôler des fichiers et des répertoires spécifiques pour garantir la conformité, la sécurité et l’efficacité opérationnelle.
Un moyen efficace d’accomplir cette tâche est d’utiliser les scripts PowerShell, qui permettent d’automatiser et de contrôler en détail les environnements Windows. Le script PowerShell fourni est conçu pour alerter les utilisateurs si un fichier ou un dossier spécifié est trouvé dans un répertoire ou un sous-répertoire donné, ce qui en fait un outil précieux pour les professionnels de l’informatique.
Contexte
Le contrôle de l’existence et de l’emplacement des fichiers est crucial pour plusieurs raisons. Il contribue au maintien de la sécurité en s’assurant qu’aucun fichier non autorisé n’est présent, favorise la conformité en vérifiant que les fichiers nécessaires sont en place et contribue à l’efficacité opérationnelle en automatisant les contrôles de routine. Ce script est particulièrement utile pour les professionnels de l’informatique qui doivent gérer de grands réseaux ou des environnements clients multiples. L’automatisation du processus de recherche et d’alerte permet de gagner du temps et de réduire le risque d’erreur humaine.
Le script
#Requires -Version 5.1 <# .SYNOPSIS Alert if a specified file or folder is found in a directory or subdirectory you specify. .DESCRIPTION Alert if a specified file or folder is found in a directory or subdirectory you specify. .EXAMPLE -SearchPath "C:" -FileOrFolder "autounattend" WARNING: Backslash missing from the search path. Changing it to C:\. [Alert] File Found. C:\Users\Administrator\Desktop\ExampleFolder\Test Folder 1\autounattend.xml C:\Users\Administrator\Desktop\ExampleFolder\Test Folder 2\autounattend.xml C:\Users\Administrator\Desktop\ExampleFolder\TestFolder1\Test Folder 1\autounattend.xml C:\Users\Administrator\Desktop\ExampleFolder\TestFolder1\Test Folder 2\autounattend.xml C:\Users\Administrator\Desktop\ExampleFolder\TestFolder2\Test Folder 1\TestFolder1\autounattend.xml Attempting to set Custom Field 'multiline'. Successfully set Custom Field 'multiline'! PARAMETER: -SeachPath "C:\ReplaceMeWithAvalidPath" Enter the starting directories for the search, separated by commas. This will include all subdirectories as well. PARAMETER: -FileOrFolder "ReplaceMeWithNameToSearchFor" Specify the full or partial name of a file or folder to find. E.g., 'config' or '.exe'. PARAMETER: -SearchType "Files and Folders" Limit the search to either files, folders, or both. PARAMETER: -Timeout "30" Maximum search time in minutes, halts search if exceeded. PARAMETER: -CustomField "ReplaceMeWithNameOfMultilineCustomField" Optional multiline field to record search results. Leave blank if unused. .NOTES Minimum OS Architecture Supported: Windows 10, Windows Server 2016 Release Notes: Initial Release By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). #> [CmdletBinding()] param ( [Parameter()] [String]$SearchPath = "C:\Windows,C:\Program Files", [Parameter()] [String]$FileOrFolder, [Parameter()] [String]$SearchType = "Files and Folders", [Parameter()] [Int]$Timeout = 30, [Parameter()] [String]$CustomField ) begin { # Set parameters using dynamic script variables. if ($env:searchPath -and $env:searchPath -notlike "null") { $SearchPath = $env:searchPath } if ($env:fileNameOrFolderNameToSearchFor -and $env:fileNameOrFolderNameToSearchFor -notlike "null") { $FileOrFolder = $env:fileNameOrFolderNameToSearchFor } if ($env:searchType -and $env:searchType -notlike "null") { $SearchType = $env:searchType } if ($env:timeoutInMinutes -and $env:timeoutInMinutes -notlike "null") { $Timeout = $env:timeoutInMinutes } if ($env:customFieldName -and $env:customFieldName -notlike "null") { $CustomField = $env:customFieldName } # Error out if no search path was given. if (-not $SearchPath) { Write-Host "[Error] No search path given!" exit 1 } # If given a comma-separated list, split the paths. $PathsToSearch = New-Object System.Collections.Generic.List[String] if ($SearchPath -match ",") { $SearchPath -split "," | ForEach-Object { $PathsToSearch.Add($_.Trim()) } } else { $PathsToSearch.Add($SearchPath) } # Initialize a generic list for paths to remove or replace. $ReplacementPaths = New-Object System.Collections.Generic.List[Object] $PathsToRemove = New-Object System.Collections.Generic.List[String] # If given a drive without the backslash add it in. $PathsToSearch | ForEach-Object { if ($_ -notmatch '^[A-Z]:\\$' -and $_ -match '^[A-Z]:$') { $NewPath = "$_\" $ReplacementPaths.Add( [PSCustomObject]@{ Index = $PathsToSearch.IndexOf("$_") NewPath = $NewPath } ) Write-Warning "Backslash missing from the search path. Changing it to $NewPath." } } # Apply replacements. $ReplacementPaths | ForEach-Object { $PathsToSearch[$_.index] = $_.NewPath } # Check if the search path is valid. $PathsToSearch | ForEach-Object { if (-not (Test-Path $_)) { Write-Host -Object "[Error] $_ does not exist!" $PathsToRemove.Add($_) $ExitCode = 1 } } # Remove Paths that do not exist. $PathsToRemove | ForEach-Object { $PathsToSearch.Remove($_) | Out-Null } # Error out if no valid paths to search. if ($($PathsToSearch).Count -eq 0) { Write-Host "[Error] No valid paths to search!" exit 1 } # If we're not given a file or folder error out. if (-not $FileOrFolder) { Write-Host -Object "[Error] No file or folder given to search for!" exit 1 } # Timeout must be within a given range in minutes. if ($Timeout -lt 1 -or $Timeout -gt 120) { Write-Host -Object "[Error] Timeout is greater than 120 minutes or less than 1 minute." exit 1 } # Scope the search to either files only or folders only. $ValidSearchTypes = "Files and Folders", "Files Only", "Folders Only" if ($ValidSearchTypes -notcontains $SearchType) { Write-Host -Object "[Error] Invalid search type." exit 1 } # Test for local administrator rights. function Test-IsElevated { $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object System.Security.Principal.WindowsPrincipal($id) $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) } # Handy function to set a custom field. function Set-NinjaProperty { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [String]$Name, [Parameter()] [String]$Type, [Parameter(Mandatory = $True, ValueFromPipeline = $True)] $Value, [Parameter()] [String]$DocumentName ) $Characters = $Value | Measure-Object -Character | Select-Object -ExpandProperty Characters if ($Characters -ge 10000) { throw [System.ArgumentOutOfRangeException]::New("Character limit exceeded, value is greater than 10,000 characters.") } # If we're requested to set the field value for a Ninja document we'll specify it here. $DocumentationParams = @{} if ($DocumentName) { $DocumentationParams["DocumentName"] = $DocumentName } # This is a list of valid fields that can be set. If no type is given, it will be assumed that the input doesn't need to be changed. $ValidFields = "Attachment", "Checkbox", "Date", "Date or Date Time", "Decimal", "Dropdown", "Email", "Integer", "IP Address", "MultiLine", "MultiSelect", "Phone", "Secure", "Text", "Time", "URL", "WYSIWYG" if ($Type -and $ValidFields -notcontains $Type) { Write-Warning "$Type is an invalid type! Please check here for valid types. https://ninjarmm.zendesk.com/hc/en-us/articles/16973443979789-Command-Line-Interface-CLI-Supported-Fields-and-Functionality" } # The field below requires additional information to be set $NeedsOptions = "Dropdown" if ($DocumentName) { if ($NeedsOptions -contains $Type) { # We'll redirect the error output to the success stream to make it easier to error out if nothing was found or something else went wrong. $NinjaPropertyOptions = Ninja-Property-Docs-Options -AttributeName $Name @DocumentationParams 2>&1 } } else { if ($NeedsOptions -contains $Type) { $NinjaPropertyOptions = Ninja-Property-Options -Name $Name 2>&1 } } # If an error is received it will have an exception property, the function will exit with that error information. if ($NinjaPropertyOptions.Exception) { throw $NinjaPropertyOptions } # The below type's require values not typically given in order to be set. The below code will convert whatever we're given into a format ninjarmm-cli supports. switch ($Type) { "Checkbox" { # While it's highly likely we were given a value like "True" or a boolean datatype it's better to be safe than sorry. $NinjaValue = [System.Convert]::ToBoolean($Value) } "Date or Date Time" { # Ninjarmm-cli expects the Date-Time to be in Unix Epoch time so we'll convert it here. $Date = (Get-Date $Value).ToUniversalTime() $TimeSpan = New-TimeSpan (Get-Date "1970-01-01 00:00:00") $Date $NinjaValue = $TimeSpan.TotalSeconds } "Dropdown" { # Ninjarmm-cli is expecting the guid of the option we're trying to select. So we'll match up the value we were given with a guid. $Options = $NinjaPropertyOptions -replace '=', ',' | ConvertFrom-Csv -Header "GUID", "Name" $Selection = $Options | Where-Object { $_.Name -eq $Value } | Select-Object -ExpandProperty GUID if (-not $Selection) { throw [System.ArgumentOutOfRangeException]::New("Value is not present in dropdown") } $NinjaValue = $Selection } default { # All the other types shouldn't require additional work on the input. $NinjaValue = $Value } } # We'll need to set the field differently depending on if its a field in a Ninja Document or not. if ($DocumentName) { $CustomField = Ninja-Property-Docs-Set -AttributeName $Name -AttributeValue $NinjaValue @DocumentationParams 2>&1 } else { $CustomField = Ninja-Property-Set -Name $Name -Value $NinjaValue 2>&1 } if ($CustomField.Exception) { throw $CustomField } } $ExitCode = 0 } process { # Error out if local administrator rights are not present. if (-not (Test-IsElevated)) { Write-Host "[Error] Access Denied. Please run with Administrator privileges." exit 1 } # Initialize generic lists. $SearchJobs = New-Object System.Collections.Generic.List[object] $CustomFieldValue = New-Object System.Collections.Generic.List[string] # For each given path to search, create a PowerShell job with the provided parameters. $PathsToSearch | ForEach-Object { $SearchJobs.Add( ( Start-Job -ScriptBlock { param($SearchPath, $FileOrFolder, $SearchType) # We're going to wildcard search either files or folders depending on the parameters given. switch ($SearchType) { "Files and Folders" { Get-ChildItem -Path $SearchPath -Filter "*$FileOrFolder*" -Recurse | Select-Object -Property FullName, Attributes | ConvertTo-Csv } "Folders Only" { Get-ChildItem -Path $SearchPath -Filter "*$FileOrFolder*" -Recurse -Directory | Select-Object -Property FullName, Attributes | ConvertTo-Csv } "Files Only" { Get-ChildItem -Path $SearchPath -Filter "*$FileOrFolder*" -Recurse -File | Select-Object FullName, Attributes | ConvertTo-Csv } } } -ArgumentList $_, $FileOrFolder, $SearchType ) ) } # Convert timeout to seconds as Wait-Job requires seconds. $TimeoutInSeconds = $Timeout * 60 $StartTime = Get-Date # Wait for all jobs to complete or timeout. foreach ($SearchJob in $SearchJobs) { # Calculate the remaining time. $TimeElapsed = (Get-Date) - $StartTime $RemainingTime = $TimeoutInSeconds - $TimeElapsed.TotalSeconds # If there is no remaining time, break the loop. if ($RemainingTime -le 0) { break } # Wait for the current job with the remaining time as the timeout. $SearchJob | Wait-Job -Timeout $RemainingTime | Out-Null } # Output a warning if the job fails to complete. $IncompleteJobs = $SearchJobs | Get-Job | Where-Object { $_.State -eq "Running" } if ($IncompleteJobs) { Write-Host "[Error] The timeout period of $Timeout minutes has been reached, but not all files or directories have been searched!" $CustomFieldValue.Add("[Error] The timeout period of $Timeout minutes has been reached, but not all files or directories have been searched!") $ExitCode = 1 } # Our PowerShell Job outputs in CSV format; we'll convert it here. $MatchingItems = $SearchJobs | ForEach-Object { $_ | Get-Job | Receive-Job -ErrorAction SilentlyContinue -ErrorVariable JobErrors | ConvertFrom-Csv } # Identify whether or not we have a match for a file or folder here. $FileMatch = $MatchingItems | Where-Object { $_.Attributes -ne "Directory" } $FolderMatch = $MatchingItems | Where-Object { $_.Attributes -eq "Directory" } # If we have a match for a file we'll output that here. if ($FileMatch) { Write-Host -Object "[Alert] File Found." $CustomFieldValue.Add("[Alert] File Found.") } # If we have a match for a folder we'll output that here. if ($FolderMatch) { Write-Host -Object "[Alert] Folder Found." $CustomFieldValue.Add("[Alert] Folder Found.") } # If we have no matches we'll output that here. if (-not $FileMatch -and -not $FolderMatch) { Write-Host -Object "Unable to find $FileOrFolder." $CustomFieldValue.Add("Unable to find $FileOrFolder.") } # For each matching file we'll output their full path. $MatchingItems | ForEach-Object { Write-Host "$($_.FullName)" $CustomFieldValue.Add("$($_.FullName)") } # Output any failures or errors received. $FailedJobs = $SearchJobs | Get-Job | Where-Object { $_.State -ne "Completed" -and $_.State -ne "Running" } if ($FailedJobs -or $JobErrors) { Write-Host "" Write-Host "[Error] Failed to search certain files or directories due to an error." $CustomFieldValue.Add("") $CustomFieldValue.Add("[Error] Failed to search certain files or directories due to an error.") if ($JobErrors) { Write-Host "" $CustomFieldValue.Add("") $JobErrors | ForEach-Object { Write-Host "[Error] $($_.Exception.Message)" $CustomFieldValue.Add("[Error] $($_.Exception.Message)") } } $ExitCode = 1 } $SearchJobs | Get-Job | Remove-Job -Force # Attempt to set the custom field using the Set-NinjaProperty function, if provided. if ($CustomField) { try { Write-Host "Attempting to set Custom Field '$CustomField'." Set-NinjaProperty -Name $CustomField -Value ($CustomFieldValue | Out-String) Write-Host "Successfully set Custom Field '$CustomField'!" } catch { if (-not $_.Exception.Message) { Write-Host "[Error] $($_.Message)" } else { Write-Host "[Error] $($_.Exception.Message)" } $ExitCode = 1 } } exit $ExitCode } end { }
Description détaillée
Le script fonctionne en plusieurs étapes, chacune contribuant à sa fonctionnalité globale. En voici la description étape par étape :
1. Initialisation des paramètres : Le script commence par définir plusieurs paramètres :
- SearchPath: Les répertoires à rechercher.
- FileOrFolder : Le nom du fichier ou du dossier à rechercher.
- SearchType: Recherche de fichiers, de dossiers ou des deux.
- Timeout: Durée maximale de la recherche.
- CustomField: Un champ facultatif pour enregistrer les résultats de la recherche.
2. Remplacement des variables d’environnement: Le script vérifie si des variables d’environnement sont définies et doivent remplacer les paramètres d’entrée. Cela permet des ajustements dynamiques en fonction des différents environnements.
3. Validation : Le script effectue plusieurs contrôles de validation :
- S’assurer qu’un chemin de recherche est fourni.
- Validation et formatage du chemin de recherche.
- Vérifier l’existence des chemins spécifiés.
- Validation du nom du fichier ou du dossier.
- S’assurer que la valeur du délai d’attente se situe dans une fourchette acceptable.
- Confirmation de la validité du type de recherche.
4. Vérification des droits de l’administrateur: Le script comprend une fonction permettant de vérifier s’il est exécuté avec des privilèges d’administrateur, ce qui est nécessaire pour certaines opérations sur les fichiers.
5. Exécution de la recherche: La fonctionnalité principale consiste à créer des tâches PowerShell pour chaque chemin à rechercher. Ces tâches effectuent des recherches récursives sur la base des paramètres spécifiés et renvoient les résultats au format CSV.
6. Traitement des résultats: Le script recueille et traite les résultats de la recherche :
- Il identifie les correspondances pour les fichiers et les dossiers.
- Produit les chemins complets de tous les éléments correspondants.
- Erreurs d’enregistrement ou recherches incomplètes.
7. Réglage du champ personnalisé: Si un champ personnalisé est spécifié, le script tente de le définir avec les résultats de la recherche, en s’appuyant sur une fonction d’aide pour gérer les différents types de champs.
8. Gestion des erreurs et nettoyage: Le script s’assure que toutes les erreurs sont enregistrées et que tous les travaux sont correctement nettoyés avant de se terminer avec un code de sortie approprié.
Cas d’utilisation potentiels
Prenons l’exemple d’un professionnel de l’informatique qui gère un réseau de postes de travail pour le compte d’une entreprise cliente. Ils doivent s’assurer qu’aucun fichier exécutable non autorisé n’est présent dans les répertoires des utilisateurs, dans le cadre d’un audit de sécurité. En déployant ce script, ils peuvent automatiser la recherche sur tous les postes de travail, identifier rapidement toutes les occurrences de fichiers non autorisés et prendre les mesures nécessaires. Cette automatisation permet non seulement de renforcer la sécurité, mais aussi de libérer un temps précieux pour que l’équipe informatique se concentre sur des tâches plus stratégiques.
Comparaisons
Ce script offre une approche complète et automatisée de la recherche de fichiers et de dossiers par rapport aux méthodes manuelles ou aux scripts batch de base. Les méthodes traditionnelles impliquent souvent une navigation manuelle dans les répertoires ou l’utilisation de scripts simples dépourvus de fonctions avancées telles que la gestion des délais, l’enregistrement de champs personnalisés et l’exécution de tâches multithread. La capacité du script PowerShell à gérer des environnements complexes et à fournir des résultats détaillés en fait un choix de premier ordre pour les professionnels de l’informatique.
FAQ
1) Comment puis-je spécifier plusieurs répertoires pour la recherche ?
Utilisez une liste séparée par des virgules pour le paramètre SearchPath, par exemple « C:NPath1,C:NPath2 ».
2) Puis-je limiter la recherche aux fichiers ou aux dossiers ?
Oui, utilisez le paramètre SearchType avec les valeurs « Files Only » ou « Folders Only ».
3) Que se passe-t-il si la recherche dépasse le délai spécifié ?
Le script mettra fin à toute recherche incomplète et enregistrera un message d’erreur.
4) Ai-je besoin de privilèges d’administrateur pour exécuter ce script ?
Oui, le script vérifie les droits d’administrateur et se termine s’il n’est pas exécuté avec des privilèges suffisants.
Implications
Les résultats de ce script ont des implications importantes pour la sécurité informatique. En identifiant les fichiers et dossiers non autorisés ou égarés, les professionnels de l’informatique peuvent prendre des mesures immédiates pour atténuer les risques potentiels en matière de sécurité. L’exécution régulière de ce script dans le cadre d’une routine de maintenance peut contribuer à garantir la conformité avec les politiques de l’organisation et les exigences réglementaires, améliorant ainsi la posture de sécurité globale.
Recommandations
Lors de l’utilisation de ce script, il est important de suivre les bonnes pratiques suivantes :
- Mettez régulièrement le script à jour pour tenir compte des changements dans votre environnement.
- Intégrez le script dans des routines de maintenance automatisées.
- Examinez les résultats de la recherche et agissez rapidement pour maintenir la sécurité.
- Personnalisez les paramètres du script pour répondre à des besoins et à des environnements spécifiques.
Conclusion
Ce script PowerShell est un outil puissant pour les professionnels de l’informatique, offrant des capacités de recherche de fichiers et de dossiers automatisées et efficaces. En exploitant ce script, les équipes informatiques peuvent améliorer leur efficacité opérationnelle, maintenir la sécurité et assurer la conformité avec les politiques de l’organisation. Pour ceux qui utilisent NinjaOne, l’intégration de ce script peut rationaliser davantage les tâches de gestion, en permettant un contrôle et une surveillance centralisés sur plusieurs terminaux.