Sujet
Ce script importe un fichier CSV contenant les vulnérabilités exportées à partir de divers outils et plateformes d'analyse des vulnérabilités.
Environnement
NinjaOne Automation
Description
Pour utiliser ce script, enregistrez-le sous forme de fichier batch et téléchargez-le dans votre bibliothèque de scripts. Vous pouvez ensuite l'exécuter selon vos besoins à partir du bouton Lecture de la page Détails de l'appareil ou à partir de la vue Recherche d'appareils. Consultez la section Automatisation du processus d'importation des vulnérabilités pour obtenir des instructions et des ressources.
<#
.SYNOPSIS
Ce script est utilisé pour importer un fichier CSV de vulnérabilités exporté à partir de divers outils/plateformes d'analyse des vulnérabilités.
.DESCRIPTION
Ce script est utilisé pour importer un fichier CSV de vulnérabilités exporté à partir de divers outils/plateformes d'analyse des vulnérabilités.
Une fois le fichier importé, le fichier CSV est supprimé. Si l'importation échoue en raison d'un problème
avec le fichier CSV, la mention « FAILED_ » est ajoutée au nom du fichier CSV. Si l'importation initiale réussit, mais que les vérifications d'état
ultérieures échouent, la mention « UNKNOWN_ » est ajoutée au nom du fichier CSV. Vous devrez alors vérifier l'état dans l'interface utilisateur NinjaOne.
# ---------------------------------------------------------------
# Auteur : Mark Giordano
# Date : 03/05/2025
# Description : Vulnérabilité NinjaOne Importation et journalisation
# ---------------------------------------------------------------
.NOTES
Les URL API standard sont les suivantes :
app.ninjarmm.com
eu.ninjarmm.com
oc.ninjarmm.com
ca.ninjarmm.com
us2.ninjarmm.com
Si vous souhaitez configurer cette tâche comme une tâche récurrente dans NinjaOne, il est recommandé de le faire sur un appareil sécurisé qui n'est pas
utilisé à des fins quotidiennes. Les informations d'identification doivent être stockées dans des champs personnalisés sécurisés et récupérées à partir de ceux-ci.
Consultez la configuration recommandée par Luke Whitelock (Pour commencer) ici : https://docs.mspp.io/ninjaone/getting-started
#>
<# Décommentez cette section si vous prévoyez d'extraire vos identifiants API à partir des champs personnalisés sécurisés de NinjaOne
$ClientID = Ninja-Property-Get NinjaOneAPIClientID
$ClientSecret = Ninja-Property-Get NinjaOneAPISecret
#>
# Ajustez BaseURL à l'instance applicable #
$BaseURL = 'ca.ninjarmm.com'
# Spécifiez l'ID du groupe d'analyse #
$ScanGroupID = 'ENTREZ LE NUMÉRO D'IDENTIFICATION DU GROUPE D'ANALYSE'
# Répertoire des fichiers CSV à importer #
$PathtoCSV = "$env:SystemDrivetemp"
# Début du nom des fichiers CSV à rechercher #
$CSVName = 'VulnExport_'
## Configuration de la fonction ##
function Get-NinjaToken {
$Headers = @{
"Content-Type" = "application/x-www-form-urlencoded"
}
$Scope = "monitoring management offline_access"
if ([string]::IsNullOrWhiteSpace($ClientID) -or [string]::IsNullOrWhiteSpace($ClientSecret)) {
if ($($PSVersionTable.PSVersion -lt [version]'7.0')) {
$ClientID = Read-Host -Prompt "ClientID"
$ClientSecret = Read-Host -Prompt "ClientSecret"
}
else {
$ClientID = Read-Host -MaskInput "ClientID"
$ClientSecret = Read-Host -MaskInput "ClientSecret"
}
}
$GrantType = 'client_credentials'
$params = @{
Uri = "https://$($BaseURL)/ws/oauth/token"
Method = 'POST'
Body = "grant_type=$GrantType&client_id=$ClientID&client_secret=$ClientSecret&scope=$Scope"
}
$Response = Invoke-RestMethod @Params -Headers $Headers
return $Response.access_token
}
function Get-BaseSettings {
[CmdletBinding()]
param (
[Parameter()]
[String]$Method = 'Get',
[Parameter()]
[String]$Body,
[switch]$Paginate,
[string]$After,
[string]$ContentType
)
if ($Paginate) {
$URL = "https://$($BaseURL)/api/v2/$($Request)?pageSize=$($PageSize)&after=$($After)"
}
else {
$URL = "https://$($BaseURL)/api/v2/$($Request)"
}
$Params = @{
Uri = "$URL"
Method = "$Method"
Headers = @{
'Authorization' = 'Bearer {0}' -f "$Token"
"Content-Type" = 'application/json'
}
}
if ($Body) {
$Params['Body'] = $Body
}
return $Params
}
function Get-VulScanGroups {
[CmdletBinding()]
param (
[Parameter()]
[int]$SGID
)
if ($SGID) {
$Request = "/vulnerability/scan-groups/$SGID"
}
else {
$Request = '/vulnerability/scan-groups'
}
$Params = Get-BaseSettings
try {
$ScanGroupRaw = Invoke-WebRequest @Params
return $ScanGroupRaw.Content | ConvertFrom-Json
}
catch {
return $false
}
}
function New-VulScanImport {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[int]$SGID,
[Parameter(Mandatory = $true)]
[string]$CSV
)
if ($($PSVersionTable.PSVersion -ge [version]'7.0')) {
$Request = "vulnerability/scan-groups/$SGID/upload"
$Params = Get-BaseSettings -Method 'Post'
$Form = @{
"csv" = Get-Item $CSV
}
try {
$Response = Invoke-WebRequest @Params -Form $Form
Write-LogEntry -Message ($Response.Content | Out-String)
}
catch {
Write-LogEntry -Message 'Échec de l'importation du fichier CSV.'
Write-LogEntry -Message "$($_.Exception.Message)"
exit 1
}
}
else {
$APIURL = "https://$BaseUrl/api/v2/vulnerability/scan-groups/$SGID/upload"
Add-Type -AssemblyName System.Net.Http
$HTTPClient = New-Object System.Net.Http.HttpClient
$HTTPClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $Token)
# Créer le contenu multipart form-data
$MPFormData = New-Object System.Net.Http.MultipartFormDataContent
# Ouvrir le fichier CSV en tant que flux et préparer le StreamContent
$FileStream = [System.IO.File]::OpenRead($CSV)
$FileName = [System.IO.Path]::GetFileName($CSV)
$StreamContent = New-Object System.Net.Http.StreamContent($FileStream)
$StreamContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("text/csv")
# Ajoutez le contenu du fichier CSV avec le nom de champ de formulaire « csv »
$MPFormData.Add($StreamContent, "csv", $FileName)
# Envoyer le contenu multipart et attendre le résultat
$APICall = $HTTPClient.PostAsync($APIURL, $MPFormData)
$APICall.Wait()
$Response = $APICall.Result
# Lire le contenu de la réponse
$APIResponse = $Response.Content.ReadAsStringAsync()
$APIResponse.Wait()
if (!($Response.IsSuccessStatusCode)) {
Write-LogEntry -Message 'Échec de l'importation du fichier CSV.'
Write-LogEntry -Message ($APIResponse.Result | Out-String)
$FileStream.Close()
$HTTPClient.Dispose()
exit 1
}
Write-LogEntry -Message ($APIResponse.Result | Out-String)
$FileStream.Close()
$HTTPClient.Dispose()
}
}
function Write-LogEntry {
param (
[Parameter(Mandatory = $true)]
[string]$Message
)
$LogPath = "$PathtoCSVNinjaOneVulnerabiltyImport.log"
$TimeStamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
Add-Content -Path $LogPath -Value "$TimeStamp $Message"
Write-Host "$TimeStamp $Message"
}
## End Function Setup ##
$Token = Get-NinjaToken
if (!(Test-Path $PathtoCSV)) {
Write-LogEntry -Message 'Le chemin d'accès aux fichiers CSV n'existe pas. Quittez.'
exit 1
}
$CSVFiles = Get-ChildItem $PathtoCSV | Where-Object { $_.Extension -eq '.csv' -and $_.Name -match "^$CSVName" }
if (!($CSVFiles)) {
Write-LogEntry -Message 'Aucun fichier CSV à importer. Exit.
exit 0
}
foreach ($CSV in $CSVFiles) {
Write-LogEntry -Message "CSV trouvé : $($CSV.Name)"
Write-LogEntry -Message « Importation vers ScanGroup $ScanGroupID »
try {
New-VulScanImport -SGID $ScanGroupID -CSV $($CSV.FullName)
}
catch {
Write-LogEntry -Message « Échec de l'importation du fichier CSV. »
Write-LogEntry -Message ($_.Exception.Message)
exit 1
}
$ImportStatus = (Get-VulScanGroups -SGID $ScanGroupID).Status
if (!($ImportStatus)) {
Write-LogEntry -Message 'Impossible de récupérer le statut de l'importation, mais celle-ci peut tout de même aboutir.'
Write-LogEntry -Message 'Veuillez vérifier le statut de l'importation des vulnérabilités dans l'interface utilisateur NinjaOne.'
Write-LogEntry -Message 'Ajout de UNKNOWN_ au nom du fichier CSV'
try {
Rename-Item -Path $($CSV.FullName) "UNKNOWN_$($CSV.Name)" -ErrorAction Stop
Write-LogEntry -Message "Renommé avec succès en UNKNOWN_$($CSV.Name)."
exit 1
}
catch {
Write-LogEntry -Message « Échec de la renommage du fichier CSV. »
exit 1
}
}
while ($ImportStatus -ne 'Complete') {
Write-LogEntry -Message « État de l'importation : $ImportStatus »
Start-Sleep 10
$ImportStatus = (Get-VulScanGroups -SGID $ScanGroupID).Status
}
if ($ImportStatus -eq 'Complete' ) {
Write-LogEntry -Message "Import Status: $ImportStatus"
Write-LogEntry -Message "Import Successful"
Write-LogEntry -Message "Deleting $($CSV.Name)..."
try {
Remove-Item -Path $($CSV.FullName) -Force -ErrorAction Stop
Write-LogEntry -Message "Successfully deleted."
exit 0
}
catch {
Write-LogEntry -Message « Échec de la suppression du fichier CSV. »
exit 1
}
}
else {
Write-LogEntry -Message « État de l'importation : $ImportStatus »
Write-LogEntry -Message « Échec de l'importation ! »
Write-LogEntry -Message "Renaming $($CSV.Name)..."
try {
Rename-Item -Path $($CSV.FullName) "FAILED_$($CSV.Name)" -ErrorAction Stop
Write-LogEntry -Message "Successfully renamed to FAILED_$($CSV.Name)."
exit 1
}
catch {
Write-LogEntry -Message « Échec de la renommage du fichier CSV. »
exit 1
}
}
}
Ressources supplémentaires
Automatisation du processus d'importation des vulnérabilités