PI Services

Le blog des collaborateurs de PI Services

PowerBI - Coloration conditionnel - Exemple avancé

Derrière le terme un peu technique "Coloration conditionnel" se cache simplement le fait, dans un dashboard PowerBI d'afficher un changement de couleur dans le champ d'un tableau, en fonction de condition.

En plus des fonctions natives de mise en forme dans PowerBI Desktop, il est possible de creer une mesure (code DAX) , avec des conditions, que l'on va utiliser pour afficher une valeur dans un champ. Et cette valeur peut etre un code couleur.

Dans l'exemple du code ci-dessous, on a une application "MyApp" pour laquelle, dans PowerBI nous avons trois datasets (deux correspondrait par exemple a deux tables de la base de donnée associée a "MyApp", et un troisième qui est une connexion vers un outil qui reference la criticité de mes machines (ex: outil de gestion des asset ou cmdb)):

- MyAppAgents, qui contiens des infos sur les agents de mon application

MyAppVersions, qui contiens un historique des numeros de version de mon application

-  MyServers qui est donc la table qui liste la criticité des mes serveurs.

Objectif: On veux dans un dashboard, colorer un champ en rouge ou jaune (exemple, le nom du serveur), pour lequel la version ((MyAppAgents[MyApp_agent_version]) serai differente de la dernière version, ET dont le serveur serait un serveur de Production ou de test.

 

Color_MyApp_agent = 

   VAR Result1 = SELECTEDVALUE(MyAppAgents[MyApp_agent_version])
   VAR Result2 = SELECTEDVALUE(MyAppVersions[MyApp_Last_version_available])
   VAR Result3 = SELECTEDVALUE(MyServers[Server_Criticity])

-- SI MyApp_agent EST DIFFERENT DE MyApp_Last_version_available ET SI Server_Criticity = "PROD" alors on renvoi "CRITICAL"
   VAR Critical = IF(AND(Result1<>Result2, Result3="PROD"),"CRITICAL")
   
-- SI MyApp_agent EST DIFFERENT DE MyApp_Last_version_available ET SI Server_Criticity <> "TEST" alors on renvoi "WARNING"  
   VAR Warning = IF(AND(Result1<>Result2, Result3<>"TEST"),"WARNING")
   
-- SI MyApp_agent EST EGALE A MyApp_Last_version_available   
   VAR OK = IF(Result1=Result2,"OK")

   
 -- SELON LE CAS ON RENVOI UN CODE COULEUR DIFFERENT
RETURN
SWITCH(
True(),
 Critical="CRITICAL" , "#FEA19E",
Warning="WARNING" , "#F9FF28",
 OK="OK" , "#96FF93" 
)

 

On crée donc une mesure (measure) dans laquelle on va coller le code ci-dessus

Apres validation la mesure apparait dans le dataset ou elle a été crée

On selectionne ensuite le champ que l'on veut colorer puis Conditional formatting/Background color

 

On selectionne Format by "Field Value", et on va venir selectionner notre mesure (Color_MyApp_agent)

 

Apres validation, Les valeurs de la colonne seront formatée selon la condition rencontrée (Les code couleurs '#FEA19E' peuvent bien sur etre modifié dans le code pour choisir une autre coloration):

 

Module Powershell SecretStore - Exemple avec API Crowdstrike

SecretStore est un module Powershell permettant de facilement stocker et gérér des Credentials.

Dans l’exemple ci-dessous nous le mettons en place et l’utilisons pour demander, dans cet exemple, un token d’acces au cloud de l’EDR Crowdstrike.

NB : Le module SecretStore requiert l’installation du module SecretManagement

Comme tout module Powershell, si ils ne peut pas être directement téléchargé en ligne sur un repository avec la commande Install-Module, il est possible de le(s) récupérer sur Github ou encore sur Powershell Galery

(https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretStore)

(https://www.powershellgallery.com/packages/Microsoft.PowerShell.SecretManagement)

Une fois les module décompressé et stocké dans un des dossier contenant en standard des modules (ex : «C:\Program Files\WindowsPowerShell\Modules»)

NB : Si le module n’est pas stocké dans un chemin déclaré dans la variable $env:PSModulePath, son chemin complet devra être renseigné lors de l’import. Ceci peut être problématique aussi après l’import, dans le cadre de certaines commandes du module. Pour cela il est préférable qu’il soit stocké dans un chemin reconnu par la variable $env:PSModulePath

NB : Pour qu’un module soit correctement reconnu par Powershell, le nom du dossier le contenant doit être identique au nom du fichier psd1 ou psm1 :

 C:\Program Files\WindowsPowerShell\Modules \microsoft.powershell.secretstore

Dans une fenêtre powershell, exécuter Import-module en spécifiant le chemin d’accès aux fichiers indiqué ci-dessous

*****

Import-module microsoft.powershell.secretmanagement

Import-module microsoft.powershell.secretstore

*****

Par défaut le module SecretStore requiert un password pour accéder a chaque au coffre-fort de mot passe. La commande suivante permet de spécifier que l’authentification ne sera pas demandée , uniquement pour l’utilisation actuel (CurrentUser)

 

Set-SecretStoreConfiguration -Scope CurrentUser -Authentication None -Interaction None

La commande demande un password qui ne sera pas redemandé

La commande suivante permet de créer le coffre-fort (vault). Indiquer un nom explicite pour l’usage de ce coffre. (La commande requiert d’indiquer le module SecretStore)

Register-SecretVault -ModuleName microsoft.powershell.secretstore -Name MyVault

A présent que le coffre est créé, nous allons y stocker les informations requise, dans cet exemple, pour récupérer un token Crowdstrike.

Dans une variable de type hashtable ($ApiClient) On renseigne le Client Id, Le Client Secret, et le Hostname (url de l’api). On ‘range’  ($ApiClient) dans le coffre MYVault. (Set-secret)

$ApiClient = @{
    ClientId     = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    ClientSecret = ' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    Hostname     = 'https://api.eu-1.crowdstrike.com'
}

Set-Secret -Name MyApiClient -Secret $ApiClient -Vault MyVault

On peut tester a présent que le credential peut être récupéré :

Get-Secret -Name MyApiClient -Vault MyVault -AsPlainText

Et dans notre exemple, on peut maintenant récupérer notre token Crowdstrike

Get-Secret -Name MyApiClient -Vault MyVault -AsPlainText | ForEach-Object { Request-FalconToken @_ }

Notre token doit etre valable

$(Test-FalconToken).token

Les cmdlets du module PSFalcon peuvent maintenant être exécutées, par exemple :

Get-FalconHost -All -Detailed

NB : Les modules powershell SecretStore et SecretManagement peuvent même etre supprimé (Remove-Module). Le coffre crée sera toujours accessible par le compte l’ayant crée.

Crowdstrike EDR - Scan de machines cibles

L’EDR Crowdstrike integre bien sur une fonction de scan de machines cibles, a la demande ou planifiés.

Aller sur le lien Endpoint security/On-demand scans

Cliquer Create a scan

Sélectionner Now ou In the future pour planifier un scan

Laisser coché l’option «Limit how long this scan runs» pour limiter la durée du scan. NB : La durée de 2 heures par défaut pourra être adapté au vu de la durée observée en moyenne dans le résultat final du/des scans.

Sélectionner une ou plusieurs et/ou des groupes de machines (la selection dans le champs affiche automatiquement les éléments)

Inscrire un ou plusieurs chemin a scanner (1 par ligne)

Inscrire éventuellement un ou plusieurs chemin a exclure

Le champ «Exclusions to test against above pattern» peut être utilisé pour tester les exclusions.

Dans l’exemple ci-dessus, on veut scanner le lecteur D:\, en excluant tout le contenu (*) de D:\App1

(la coche et la couleur du test effectué  valide le fait que la syntaxe de l’exclusion est correcte. Au contraire, par exemple  ne sera pas exclu)

Le bouton  peut être utilisé pour charger une liste d’exclusion en respectant une ligne par exclusion.

Renseigner une description explicite

Laisser par défaut la niveau de détection et de prévention a Moderate

Pour information la description des niveau de détection est la suivante :

Level

Description

Disabled

Disable all detections or preventions.

Cautious

Detect or prevent only when our machine learning system has high confidence that something is malicious.

Moderate

Detect or prevent when our machine learning system has moderate confidence that something is malicious. We recommend this setting for most use cases. This setting also detects and prevents activity that would be detected or prevented by Cautious.

Aggressive

Detect or prevent when our machine learning system has low confidence that something is malicious. This setting also detects and prevents activity that would be detected or prevented by Moderate and Cautious.

Extra Aggressive

Detect or prevent when our machine learning system has the lowest confidence that something is malicious. This setting also detects and prevents activity that would be detected or prevented by Aggressive, Moderate, and Cautious. 

 

 

Selectionner un niveau maximum de consommation CPU a utiliser (par defaut Low up to 25%)

Décocher Show notifications to end user si vous ne voulez pas que l’utilisateur connecté sur la machine reçoive une notification de Scan (le paramètre Pause duration permet d’autoriser l’utilisateur connecté à reporter le scan pour une durée donnée)

Cliquer Create Scan

 

Le scan apparait dans la liste et passez en état Completed lorsque le scan est fini.

Un clic sur le scan permet d’avoir le détail

Cliquer sur See full details pour afficher les détails complets

Dans notre exemple, la machine cible est bien dans les «Hosts without detection»

Le lien  permet d’accéder si besoin a des éléments de diagnostique/remédiation, dans le cas d’une machine ou des détections ont été remontées :

 

 

 

 

Power BI - Dax - Exemple d'utilisation de OR (II) dans un Filtre

Problématique: Nous voulons a travers une requete Dax compter un nombre de machines possedant une application 'MyApp' dans une table nommée MY_HOSTS_AND_APPS

Mais une partie des machines n'a pas cette info renseignée dans la colonne [AppName]. Cette info est inscrite sous la forme d'un texte libre dans une autre colonne [HostUsage]

Il est possible dans ce cas d'utiliser un OR (preferer l'alias '||') dans la clause FILTER.

On compte donc (DISTINCTCOUNT), les HostName où [AppName] est égale a "MyApp" ou bien (||) la chaine "MyApp" est presente dans [HostUsage]  (SEARCH)

 

WithMyApp = 
VAR Result = CALCULATE(
    DISTINCTCOUNT(MY_HOSTS_AND_APPS[HostName]),
        FILTER(MY_HOSTS_AND_APPS,MY_HOSTS_AND_APPS[AppName]="MyApp"
        ||
        SEARCH("MyApp",MY_HOSTS_AND_APPS[HostUsage],1,0)
        )               
)
RETURN(
    IF (Result=BLANK(),0,Result))

 

 

Script - Exemple de recherche d'ancien fichier de log dans une arborescence de dossier

Le script ci-dessous propose de rechercher dans une liste de dossiers specifiques (expression regulière FolderPattern) des fichiers de logs plus ou moins anciens, selon un seuil prédéfini (TimeDelta), ajoute un statut au tableau selon le cas rencontré, et exporte le resultat en fichier csv.

 

##############################################
### CHECK OLD LOG FILES ###
##############################################


<# 

    .SYNOPSIS 
        VERIFICATION DE LA PRESENCE ET DE L'ANCIENNETE DU LOG LE PLUS RECENT DANS UNE LISTE DE DOSSIER; 
        UN TABLEAU DES DOSSIERS DONT LE LOG LE PLUS RECENT EST PLUS ANCIEN QUE J-$TimeDelta EST AFFICHE ET EXPORTE EN FICHIER CSV.  

    .PARAMETER  
        RootFolder : Chemin du dossier racine
        TimeDelta : Seuil d'ancienneté du fichier de log le plus recent
        FolderPattern: Expression reguliere pour selectionner des noms de sous dossier
        

 
    .EXAMPLE 
     .\Check_Old_Log_Folders.ps1 -RootFolder "C:\MyRootFolder" -TimeDelta 15
#>




[CmdletBinding()]
param(
[Parameter(Mandatory,HelpMessage="Chemin du dossier racine")]
$RootFolder,

[Parameter(Mandatory,HelpMessage="Seuil d'ancienneté du fichier de log le plus recent")]
$TimeDelta,

[Parameter(HelpMessage="Expression reguliere pour selectionner des noms de sous dossier")]
[regex]$FolderPattern = "^(SRV|DEV).*"

)



# Date du jour - TimeDelta
$DateTimeMinus = (get-date).AddDays(-$TimeDelta)

# Verification que RootFolder existe 
if (!(Test-Path -Path $RootFolder))
    {
    write-host -B White -F Red "UNABLE TO FIND $RootFolder - CHECK PATH - END OF SCRIPT"
    EXIT 1
    }


# Creation d'un PSobject pour stocker les futurs valeurs LogFolder,LastLog et Status
$FileTab = @()

# Recuperation des noms de dossier
$Folders = Get-ChildItem -Path $RootFolder -Directory | Where-Object {$_.Name -match $FolderPattern}


# Pour chacun des dossier on cherche le dernier fichier modifié
$Folders | foreach {


$LogFolder = $_.Name
$LastLog = Get-ChildItem -Path $_.FullName | select -Last 1 -Property LastWriteTime -ExpandProperty LastWriteTime

# Si le dossier est vide (pas de Lastlog) le status est KO
 if (!$LastLog) {$status = "KO - DOSSIER VIDE"}

# Si LastLog est plus ancien ou plus recent que $DateTimeMinus  
 switch($LastLog)
    {
        
        {$LastLog -lt $DateTimeMinus} {$status = "KO - OLDER THAN $DateTimeMinus"}
        {$LastLog -gt $DateTimeMinus} {$status = "OK - NEWER THAN $DateTimeMinus"}
        default {$status = "UNKNOWN"}
    }


# remplissage du tableau
$FileTab += New-Object -TypeName psobject -Property @{LogFolder=$LogFolder;LastLog=$LastLog;Status=$status}

}


# Affiche le tableau $FileTab des dossiers dont le fichier le plus recent est plus ancien que $TimeDelta ou pour lequel il n'y a pas de log
Write-Host -F red -B White "LOG FOLDERS DONT LE FICHIER DE LOG LE PLUS RECENT EST PLUS ANCIEN QUE J $TimeDelta OU POUR LESQUELS IL N'EXISTE PAS DE LOG (DOSSIER VIDE)"
$KOFiles = $FileTab | Where-Object {$_.status -like 'KO*'} | select LogFolder,LastLog,Status | Sort LastLog
$KOFiles | ft -AutoSize


# Export CSV du tableau en supprimant les caractere '"'
$KOFilesCSV = $KOFiles | ConvertTo-Csv -Delimiter "," -NoTypeInformation | ForEach {$_.replace('"','')}


# Export du fichier CSV
$KOFilesCSV | Out-File "$($pwd.ProviderPath)\OldLogFolders.csv" -Force
write-host -F Blue -B White "Old Log Folders CSV File Exported as $($pwd.ProviderPath)\OldLogFolders.csv"


# Ouverture du fichier CSV
notepad.exe "$($pwd.ProviderPath)\OldLogFolders.csv"

 

Scripting - Exemple de la recuperation de la date de modification du password d'un compte AD

Problématique:  Récuperer et rendre lisible un timestamp représentant la date de modification du password d'un compte AD, renvoyé par l'outil Dsquery (https://ss64.com/nt/dsquery.html)

 

$user = "johndoe"
$domain = "mydomain.com"

# Executer la commande dsquery pour recuperer l'attribut pwdLastSet
$obj = dsquery * -filter "samaccountname=$user" -attr displayName pwdLastSet -d $domain

# Decouper $obj pour ne recuperer que la chaine correspondant au timestamp
$TimeStamp = $($obj[1] -split " ") | Where-Object {$_ -match '^\d+$'}

# Convertir le timestamp en date avec l'outil w32tm.exe
w32tm.exe /ntte $TimeStamp



Script - Crowdstrike - Exemple d'utilisation du module PSFalcon

Falcon Crowdsrike est l'un des antivirus next generation (EDR) les plus en vue et les plus performants du marché.  Le script ci-dessous propose un exemple de requetage de l'API de Crowdstrike via l'utilisation du module Powershell PSFalcon (https://www.powershellgallery.com/packages/PSFalcon)

L'acces et l'utilisation de l'Api Crowdstrike requiert bien sur un abonnement Crowdstrike (https://www.crowdstrike.com/)

Le script utilisant pour l'acces a l'API, une clé d'encryptage (AES.key), un fichier contenant le SecID (SecID.txt), et un fichier contenant la passphrase (pass.txt), il est necessaire de recreer ces éléments avec le contexte de l'abonnement auquel on se connecte (SecID).

Le script interroge l'API pour recuperer des éléments tel que les hotes, les comportements (Behaviors), les detections, les incidents.

 

QueryCrowdstrike.ps1 (11,62 kb)

 

##############################################
### QUERY CROWDSTRIKE WITH PSFALCON MODULE ###
##############################################


<# 

    .SYNOPSIS 
        INTERROGATION D'UN TENANT FALCON CROWDSTRIKE VIA L'UTILISATION DU MODULE POWERSHELL PSFALCON
        NB: LES DONNEES SONT EXTRAITES SOUS FORME D'UN FICHIERS CSV 

    .PARAMETER  
        ClientID : Client ID crowdstrike
        Pass : Password du compte d'acces a l'API
        
        ExportFolder : Dossier d'export du fichier CSV
        LogFolder : Chemin du dossier où creer le log du script

 
    .EXAMPLE 
     .\QueryCrowdstrike.ps1 -CloudUrl "https://api.eu-1.crowdstrike.com"  -ClientID <clientid> -Pass <pass> -ExportFolder ./ -LogFolder ./
#>


[CmdletBinding()]
param(
[Parameter(Mandatory,HelpMessage="Cloud URL")]
$CloudUrl,

[Parameter(HelpMessage="Client ID crowdstrike")]
$ClientID,

[Parameter(HelpMessage="Password du compte d'acces a l'API")]
$Pass,

[Parameter(Mandatory,HelpMessage="Dossier d'export du fichier CSV")]
[string]$ExportFolder="./CSV",

[Parameter(Mandatory,HelpMessage="Chemin du dossier où creer le log du script")] 
[string]$LogFolder="./",

[Parameter(Mandatory,HelpMessage="Chemin de la clé utilisée pour l'encryption du CID et de la passphrase")] 
$KeyFile = "D:\MyFolder\CROWDSTRIKE\AES.key",

[Parameter(Mandatory,HelpMessage="Chemin du fichier contenant le SecID du client")] 
$SecIDFile = "D:\MyFolder\CROWDSTRIKE\SecID.txt",

[Parameter(Mandatory,HelpMessage="Chemin du fichier contenant la passphrase")] 
$PassFile = "D:\MyFolder\CROWDSTRIKE\Pass.txt"


)


# TO RENEW KEY FILE USED FOR CLIENT ID AND PASSPHRASE ENCRYPTION
<#
    # Generate Key File
    $Key = New-Object Byte[] 16   # You can use 16, 24, or 32 for AES
    [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($Key)
    $Key | out-file $KeyFile

#>


# TO RENEW SECID ENCRYPTION, EXECUTE THE CODE BETWEEN <# #>

<#
    # Create secure string object
    $SecIDFile = "D:\MyFolder\CROWDSTRIKE\SecID.txt"
    $Key = Get-Content $KeyFile
    $SecID = read-host  -Prompt "Enter client SecID" -AsSecureString
    $SecID | ConvertFrom-SecureString -key $Key | Out-File $SecIDFile

#>


# TO RENEW PASSPHRASE ENCRYPTION, EXECUTE THE CODE BETWEEN <# #>

<#
    # Create secure string object
    $PassFile = "D:\MyFolder\CROWDSTRIKE\Pass.txt"
    $Key = Get-Content $KeyFile
    $Pass = read-host  -Prompt "Enter client PassPhrase" -AsSecureString
    $Pass | ConvertFrom-SecureString -key $Key | Out-File $PassFile

#>






# OTHER VARIABLES
# Script Name
$ScriptName = "QueryCrowdstrike.ps1"
# LogName = ScriptName without extension
$Log = $ScriptName.Split('.')[0]


# FUNCTIONS


function Write-Log 
{ 
    <# 
    .SYNOPSIS 
        This function creates or appends a line to a log file. 
 
    .PARAMETER  Message 
        The message parameter is the log message you'd like to record to the log file. 
 
    .EXAMPLE 
        PS C:\> Write-Log -Message 'Value1' 
        This example shows how to call the Write-Log function with named parameters. 
    #> 
    [CmdletBinding()] 
    param ( 
        [Parameter(Mandatory)] 
        [string]$Message,
        [Parameter(Mandatory)] 
        [string]$LogPath, 
        [Parameter(Mandatory)] 
        [string]$LogName
        
    ) 
     
    try 
    { 
        $DateTime = Get-Date -Format ‘MM-dd-yy HH:mm:ss’ 
        Add-Content -Value "$DateTime - $Message" -Path "$LogPath\$LogName.log" 
    } 
    catch 
    { 
        Write-Error $_.Exception.Message 
    } 
} 



            




# Requis si l'import du module echoue en en renvoyant plusieurs messages 'Unable to find [System.Net.Http.(...)*]' 
Add-Type -AssemblyName System.Net.Http 

# Import Module
Import-Module -Name D:\MyFolder\CROWDSTRIKE\Powershell_Module\psfalcon-master\PSFalcon.psm1



$Key = Get-Content $KeyFile

# Get file content and Decrypt ClientID
$ClientID = Get-Content $SecIDFile | ConvertTo-SecureString -Key $key
$ClientID = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ClientID))

# Get file content and Decrypt Pass
$Pass = Get-Content $PassFile | ConvertTo-SecureString -Key $key
$Pass = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Pass))







# Query for Falcon Token
try
    {
    Request-FalconToken -ClientId $ClientID -ClientSecret $Pass -Hostname $CloudUrl
    }
catch
    {
    $Message = "Error during Request-FalconToken - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Test if Test-FalconToken return True
if ($(Test-FalconToken).token -eq $true)  
      {
      $Message = "Token is valid - OK"
      write-host -F Green $Message
      Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
      }
Else
    {
    $Message = "Problem with Token - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }




# GET DATA

#region Falcon Hosts 

# Get Falcon Hosts (exemple option -filter:  –Filter "hostname:'SRV'")
try
    {
    $FalconHosts = Get-FalconHost -All -Detailed
    #$FalconHosts | select hostname,os_version,agent_version,status | ft -AutoSize
    }
catch
    {
    $Message = "Problem during Get-FalconHost - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconHostsCSV = $($FalconHosts | select `
        hostname,
        os_version,
        platform_name,
        status,
        agent_version,
        config_id_base,
        config_id_build,
        @{Name='First_Seen';Expression={[datetime]::ParseExact($_.first_seen.Replace('T','').Replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},
        @{Name='Last_Seen';Expression={[datetime]::ParseExact($_.last_seen.Replace('T','').Replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},
        reduced_functionality_mode | ConvertTo-Csv -Delimiter ',' -NoTypeInformation).Replace('"','')
    
    # Display CSV File
    $FalconHostsCSV
    }
catch
    {
    $Message = "Error during convert to CSV file for FalconHosts - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconHostsCSV | Out-File -FilePath "$ExportFolder`/FalconHosts.csv" -Force
    }
catch
    {
    $Message = "Error during export of CSV file for FalconHosts - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


#endregion Falcon Hosts 







#region Falcon Detection




#Get-FalconDetection
write-host "FALCON DETECTION EVENTS" -B White -f Blue
try
    {
    $FalconDetection = Get-FalconDetection -Detailed -All  
    $FalconDetection | sort created_timestamp -Descending
    }
catch
    {
    $Message = "Error during Get-FalconDetection - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconDetectionCSV = $($FalconDetection | select `
    detection_id,
    @{Name='Detection_Creation_Date';Expression={[datetime]::ParseExact($_[0].created_timestamp.split('.')[0].Replace('T',''),'yyyy-MM-ddHH:mm:ss',$null)}},
    @{Name='HostName';Expression={$_.device.hostname}},
    @{Name='HostIP';Expression={$_.device.local_ip}},
    @{Name='DeviceID';Expression={$_.device.device_id}},
    @{Name='BehaviorID';Expression={$_.Behaviors.Behavior_id}},
    @{Name='Behavior_FileName';Expression={$_.Behaviors.filename}},
    @{Name='Behavior_FilePath';Expression={$_.Behaviors.filepath}},
    @{Name='Behavior_cmdline';Expression={$_.Behaviors.cmdline}},
    status,
    max_severity_displayname,
    email_sent | ConvertTo-Csv -Delimiter ',' -NoTypeInformation).Replace('"','')
    }
catch
    {
    $Message = "Error during convert to CSV file for FalconDetection - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconDetectionCSV | Out-File -FilePath "$ExportFolder`/FalconDetection.csv"
    }
catch
    {
    $Message = "Error during export of CSV file for FalconDetection - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }




#endregion Falcon Detection




#region Falcon Behavior

# Get-FalconBehavior (NB: Transform TimeStamp to more readable format with [datetime]::ParseExact)
write-host "FALCON BEHAVIOR EVENTS" -B White -f Blue

try
    {
    $FalconBehavior = Get-FalconBehavior | foreach {Get-FalconBehavior -Ids $_} | Select -property  behavior_id,display_name,objective,@{Name='BehaviorDate';Expression={[datetime]::ParseExact($_.timestamp.Replace('T','').replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},cmdline,filepath,user_name,incident_id,tactic_id,tactic,technique_id,technique | sort BehaviorDate -Descending
    $FalconBehavior
    }
catch
    {
    $Message = "Error during Get-FalconBehavior - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconBehaviorCSV = $($FalconBehavior | select BehaviorDate,cmdline,filepath,user_name | ConvertTo-Csv -Delimiter ';' -NoTypeInformation).Replace('"','')
    }
catch
    {
    $Message = "Error during convert to CSV file for FalconBehavior - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconBehaviorCSV | Out-File -FilePath "$ExportFolder`/FalconBehavior.csv"
    }
catch
    {
    $Message = "Error during export of CSV file for FalconBehavior - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }



#endregion Falcon Behavior





#region Falcon Incident

write-host "FALCON INCIDENTS EVENTS" -B White -f Blue
try
    {
    $FalconIncidents= Get-FalconIncident -Detailed -All  
    $FalconIncidents | sort created -Descending
    }
catch
    {
    $Message = "Error during Get-FalconIncident - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }


# Convert To CSV
 try
    {
    $FalconIncidentsCSV = $($FalconIncidents | select `
    incident_id,
    @{Name='Creation_Date';Expression={[datetime]::ParseExact($_.created.Replace('T','').Replace('Z',''),'yyyy-MM-ddHH:mm:ss',$null)}},
    state,
    email_state  | ConvertTo-Csv -Delimiter ',' -NoTypeInformation).Replace('"','')



        

    }
catch
    {
    $Message = "Error during convert to CSV file for FalconIncident - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }

# Export CSV
 try
    {   
    $FalconIncidentsCSV | Out-File -FilePath "$ExportFolder`/FalconIncidents.csv"
    }
catch
    {
    $Message = "Error during export of CSV file for FalconIncidents - END OF SCRIPT"
    write-host -F Red $Message
    Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
    EXIT 1
    }




#endregion Falcon Incident

 

Power BI Desktop - Exemple d'extraction et utilisation d'une date issue d'un champ texte

Problématique :  Un champ texte apparenté au contenu d’un log, melange de texte et de chiffre, contiens une date que l’on veut récupérer.

 

  • On veut récupérer la date de création (Creation Date) et la mettre dans une nouvelle colonne
  • On remarque que la date est situé entre deux chaines de delimitation : ‘Creation Date : "’ et ‘"’

De ce fait on selectionne la colonne et dans le menu ‘Add Column’ on clique sur Extract/Text Between Delimiters

 

Dans le champ ‘Start delimiter’ on renseigne notre premiere chaine (ici Creation Date : ") (NB : attention aux espaces)

Dans le champ ‘End delimiter’ on renseigne notre deuxième chaine (ici ")

On valide.

Une colonne est crée automatiquement au format chaine (ABC) a la fin du tableau avec le nom ‘Text Between Delimiters’

On fait un clic droit sur la colonne et on selectionne Change Type\Date/Time/Timezone

Attention. Dans cet exemple la conversion fonctionne car le système detecte que le pattern de la chaine est de type Date/Time/Timezone.  Dans d’autres cas il peut être nécessaire de tester d’autres format et/ou bien de sélectionner a l’origine, une portion differente de texte dans la colonne d’origine.

La conversion est OK :

Il ne reste plus qu’a renommer la colonne de manière plus explicite. (Ex : Custo_Creation_Date)

Power BI Report Server - Script - Export de dashboards en PBIX

Le script ci-dessous prend en paramètre un fichier contenant une liste de dashboard a exporter au format pbix.

 

#####################################################################
###### PwBI_Export_Dashboards.ps1 - EXPORT DE RAPPORTS EN PBIX ######
#####################################################################


<# 

    .SYNOPSIS 
        CONNEXION A L'API SQL POWERBI REPORTING SERVICES ET EXPORT DE UN OU PLUSIEURS DASHBOARD
 

    .PARAMETER  
        webServiceUrl : Url Racine du serveur de rapport
        CatalogUrl : Url du catalogue des items
        DashboardListFile : Fichier des noms de dashboard a exporter
        BackupFolder : Dossier d'export des dashboards
        LogFolder : Chemin du dossier où creer le log du script

 
    .EXAMPLE 
        .\PwBI_Export_Dashboards.ps1 -webServiceUrl "http://MyServer/Reports"  -DashboardListFile ".\PwBI_Export_Dashboards_List.txt" -BackupFolder ".\BACKUP" -LogFolder ".\BACKUP" 
     
#>


[CmdletBinding()]
param(
[Parameter(Mandatory,HelpMessage="Url Racine du serveur de rapport")]
[string]$webServiceUrl,

[Parameter(Mandatory=$false,HelpMessage="Url du catalogue des items")]
[string]$CatalogUrl = "$webServiceUrl/api/v2.0/CatalogItems",


[Parameter(Mandatory,
HelpMessage="Fichier des noms de dashboard a exporter")]
[ValidateScript({
if( -Not ($_ | Test-Path) ){
                throw "File does not exist"
            }
            return $true 
})]
[string]$DashboardListFile,

[Parameter(Mandatory,HelpMessage="Dossier d'export des dashboards")]
[string]$BackupFolder,

[Parameter(Mandatory,HelpMessage="Chemin du dossier où creer le log du script")]
[string]$LogFolder = "D:\Indicateurs_Securité"

)




# GET DASHBOARD LIST CONTENT
[array]$DashboardList = Get-Content -Path $DashboardListFile



# SCRIPT NAME
$ScriptName = "PwBI_Export_Dashboards.ps1"

# LogName = ScriptName without extension
$Log = $ScriptName.Split('.')[0]

# GET CREDENTIALS TO CONNECT TO REPORT SERVER
$cred = $(Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME")



function Write-Log 
{ 
    <# 
    .SYNOPSIS 
        This function creates or appends a line to a log file. 
 
    .PARAMETER  Message 
        The message parameter is the log message you'd like to record to the log file. 
 
    .EXAMPLE 
        PS C:\> Write-Log -Message 'Value1' 
        This example shows how to call the Write-Log function with named parameters. 
    #> 
    [CmdletBinding()] 
    param ( 
        [Parameter(Mandatory)] 
        [string]$Message,
        [Parameter(Mandatory)] 
        [string]$LogPath, 
        [Parameter(Mandatory)] 
        [string]$LogName
        
    ) 
     
    try 
    { 
        $DateTime = Get-Date -Format ‘MM-dd-yy HH:mm:ss’ 
        Add-Content -Value "$DateTime - $Message" -Path "$LogPath\$LogName.log" 
    } 
    catch 
    { 
        Write-Error $_.Exception.Message 
    } 
} 




# TABLEAU DES CATALOG ITEMS
$Message = "Recuperation du catalogue des items..."
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            $CatalogItems = Invoke-RestMethod -Uri $CatalogUrl -ContentType 'application/json' -UseDefaultCredentials -Method get
            }
            catch
            {
            $Message = "Error during query of Catalog Items"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }



# COMPARAISON DE $CatalogItems ET $ReportList POUR DETERMINER LA LISTE DES DASHBOARDS A EXPORTER

$FoundReport = $CatalogItems.value | Where-Object {$_.name -in $DashboardList}

if (!($FoundReport))
{
$Message = "The required Dashboards have not been found in the catalog of the report server - END OF SCRIPT"
write-host -f Yellow $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
Exit 1
}



# EXPORT DES DASHBOARDS
try
{

    foreach ($Report in $FoundReport)
    {
    $url = "$CatalogUrl($($Report.Id))/Content/`$value"
    Invoke-WebRequest -UseDefaultCredentials -Uri $url -OutFile "$BackupFolder\$($Report.name).pbix"
    }

}
catch
{
$Message = "KO - The required Dashboards have not been found in the catalog of the report server - END OF SCRIPT"
write-host -f Yellow $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
Exit 1
}


$Message = "OK - The required Dashboards have been Exported to $BackupFolder - END OF SCRIPT"
write-host -f Green $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
Exit 0



 

Power BI Report Server - Script - Execution d'un plan de refresh d'un rapport

Le script ci-dessous se connecte sur l'api de Reporting Services (Power BI) pour executer le rafraichissement des données d'un rapport spécifique.

 

###### PWBI_REFRESH_PLAN.PS1 ######

<# 

    .SYNOPSIS 
        CONNEXION A L'API SQL POWERBI REPORTING SERVICES ET EXECUTION DU PLAN DE RAFRAICHISSEMENT D'UN RAPPORT SPECIFIQUE
 

    .PARAMETER  
        webServiceUrl : Url Racine du serveur de rapport
        CatalogUrl : Url du catalogue des items
        ReportFolderPath : Chemin du dossier contenant le rapport a rafraichir (NB: apres la racine du serveur de rapport)
        ReportName : Nom du dashboard a rafraichir
        RefreshPlan : nom du plan de Refresh
        LogFolder : Chemin du dossier où creer le log du script

 
    .EXAMPLE 
        .\PwBI_Refresh_Plan.ps1 -webServiceUrl "http://MyServer/Reports" -ReportFolderPath "/MySpecFolder/" -ReportName "MySpecDashboard" -RefreshPlan "Refresh_Every_2H" -LogFolder "C:\Temp"   
     
#>


[CmdletBinding()]
param(
[Parameter(Mandatory,HelpMessage="Url Racine du serveur de rapport")]
[string]$webServiceUrl,

[Parameter(Mandatory=$false,HelpMessage="Url du catalogue des items")]
[string]$CatalogUrl = "$webServiceUrl/api/v2.0/CatalogItems",

[Parameter(Mandatory,HelpMessage="Chemin du dossier contenant le rapport a rafraichir (NB: apres la racine du serveur de rapport)")]
[string]$ReportFolderPath,

[Parameter(Mandatory,HelpMessage="Nom du dashboard a rafraichir")]
[string]$ReportName,

[Parameter(Mandatory,HelpMessage="nom du plan de Refresh")]
[string]$RefreshPlan,

[Parameter(Mandatory,HelpMessage="Chemin du dossier où creer le log du script")]
[string]$LogFolder = "D:\Indicateurs_Securité"

)

# FULL REPORT PATH
$FullPath = "`'$ReportFolderPath$ReportName`'"  # Backtick to escape "'" character.

# REFRESH PLAN URL
$RefreshPlanUrl = "$webServiceUrl/api/v2.0/PowerBIReports(Path=$FullPath)/CacheRefreshPlans"


# SCRIPT NAME
$ScriptName = "PwBI_Refresh_Plan.ps1"

# LogName = ScriptName without extension
$Log = $ScriptName.Split('.')[0]

# GET CREDENTIALS TO CONNECT TO REPORT SERVER
$cred = $(Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME")



function Write-Log 
{ 
    <# 
    .SYNOPSIS 
        This function creates or appends a line to a log file. 
 
    .PARAMETER  Message 
        The message parameter is the log message you'd like to record to the log file. 
 
    .EXAMPLE 
        PS C:\> Write-Log -Message 'Value1' 
        This example shows how to call the Write-Log function with named parameters. 
    #> 
    [CmdletBinding()] 
    param ( 
        [Parameter(Mandatory)] 
        [string]$Message,
        [Parameter(Mandatory)] 
        [string]$LogPath, 
        [Parameter(Mandatory)] 
        [string]$LogName
        
    ) 
     
    try 
    { 
        $DateTime = Get-Date -Format ‘MM-dd-yy HH:mm:ss’ 
        Add-Content -Value "$DateTime - $Message" -Path "$LogPath\$LogName.log" 
    } 
    catch 
    { 
        Write-Error $_.Exception.Message 
    } 
} 



            




# TABLEAU DES CATALOG ITEMS
$Message = "Recuperation du catalogue des items..."
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            $CatalogItems = Invoke-RestMethod -Uri $CatalogUrl -ContentType 'application/json' -UseDefaultCredentials -Method get
            }
            catch
            {
            $Message = "Error during query of Catalog Items"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }




# RECUPERATION DU RAPPORT SPECIFIQUE
$Message = "Recuperation du rapport $ReportName"
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            $CatalogItem = $CatalogItems.value | Where-Object Name -eq $ReportName
            if (!($CatalogItem))
            {
            $Message = "Unable to find $ReportName"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }
 


# RECUPERATION DU PLAN DE RAFRAICHISSEMENT
$Message = "Recuperation du plan de rafraichissement"
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            $refreshplans = 
            
            Invoke-RestMethod -Uri $RefreshPlanUrl -Method get -UseDefaultCredentials
            
            }
            catch
            {
            $Message = "Error during query of Refresh Plan"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }



# RECUPERATION DE L'ID DU PLAN DE RAFRAICHISSEMENT
$refreshplanId = $($refreshplans | Where-Object {$_.value.Description -eq $refreshplan}).value.Id




# EXECUTION DU PLAN DE RAFRAICHISSEMENT
$Message = "Execution du plan de rafraichissement"
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            
            Invoke-RestMethod -Uri "$webServiceUrl/api/v2.0/CacheRefreshPlans($refreshplanId)/Model.Execute" -Method post -UseDefaultCredentials
            
            }
            catch
            {
            $Message = "Error during execution of Refresh Plan"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }