PI Services

Le blog des collaborateurs de PI Services

Azure : La valse des CLI ... (Partie 1)

Bonjour à tous !

Le management d'une plateforme de cloud computing publique est souvent à l'image de la vie de celle-ci, c'est à dire mouvementée !
La plateforme de Microsoft, Azure, n'échappe pas a cette règle. Il est donc temps de faire un point sur les differents outils de ligne de commande mis a notre disposition pour gérer cette plateforme.

Preparation de l'environnement PowerShell

PowerShell

Powershell a été jusqu'à une période assez récente le langage privilégié pour gérer la plateforme Azure en ligne de commande, c'est pourquoi nous commencerons par lui.
La manière la plus simple de récupérer la dernière version des modules Powershell Azure est d'utiliser PowerShellGet.

La disponibilité de PowerShellGet varie suivant la version de Powershell utilisée, à toute fin utile, voici les versions de PowerShell qui sont installées par défaut avec votre OS Microsoft Windows :

  • PowerShell 2 : Windows Server 2008 R2 / Windows 7 SP1
  • PowerShell 3 : Windows Server 2012 / Windows 8
  • PowerShell 4 : Windows Server 2012 R2 / Windows 8.1
  • PowerShell 5 : Windows 10 1507 et 1511
  • PowerShell 5.1 : Windows Server 2016 et 2019 / Windows 10 1607 et supérieur

Les modules PowerShell Azure ne fonctionnant qu'à partir de la version 5 de PowerShell, si vous êtes sous Windows 7 SP1, 8.1 ou Windows Server 2008 R2 SP1, 2012, 2012 R2, il est nécéssaire de passer à la version 5.1 directement en installant le package MSI suivant : Windows Management Framework 5.1

Note : Pour ceux étant sous Windows 7 SP1 ou Windows Server 2008 R2 SP1, c'est un peu moins simple, il faut au préalable installer la version 4.5.2 du .NET Framework avant d'installer le WMF 5.1. Celle-ci est disponible ici : .NET Framework 4.5.2

Note 2 : Vous pouvez vérifier la version de PowerShell installée sur votre OS Windows avec la commande suivante :

$PSVersionTable.PSVersion

PowerShellGet

Pour ceux qui ne sont pas familier avec PowerShellGet, celui-ci permet d'installer des modules ou des scripts PowerShell à partir du repository PowerShell Gallery contenant les modules PowerShell nous intéréssant (mais beaucoup d'autres également). Les personnes connaissant l'univers des distributions Linux retrouveront ici des notions familières.

PowerShellGet, qui est lui même un module PowerShell, est présent par défaut à partir de la version 5 de PowerShell. Celà étant dit, il est préférable de le mettre à jour avant de récupérer des modules avec celui-ci. Pour se faire, deux étapes sont nécéssaires.

On installe d'abord le fournisseur de package .NET Framework NuGet (Equivalent de Apt-Get dédié au .NET Framework pour les connaisseurs). Celui-ci permet à PowerShellGet de pouvoir récupérer des modules à partir du repository PowerShell Gallery qui est de type NuGet. Il permet également à PowerShellGet de se mettre a jour puisque le module est publié lui même sur PowerShell Gallery. Voici la commande pour installer NuGet :

Install-PackageProvider Nuget –Force

Une fois que NuGet est installé, on va pouvoir mettre à jour le module PowerShellGet avec la commande suivante :

Update-Module -Name PowerShellGet

Si pour une raison X ou Y, le module n'est pas déjà installé, vous pouvez le faire avec la commande suivante :

Install-Module –Name PowerShellGet –Force

Nous pouvons donc à présent passer au coeur du sujet à savoir les modules PowerShell Azure.

Modules Azure pour PowerShell

Si vous êtes sous la version 5.X de PowerShell, il vous faut au préalable mettre à jour la version de votre .NET Framework vers la version 4.7.2 via l'installeur suivant .NET Framework 4.7.2 avant de pouvoir installer les modules PowerShell dédiés à Azure.

Module AzureRM (ARM only)

Plus récement, jusqu'en Décembre 2018, c'est le module AzureRM qui est utilisé afin de gérer la plateforme.

Officiellement, Microsoft continue de le supporter après cette date pour des corrections de bugs mais aucune nouvelle fonctionnalité Azure ne sera administrable avec. Pour cela il faudra passer par le module Az.
Les commandes sont de la même forme que celles de PowerShell, à savoir de type Verb-Noun. Exemple :

Get-AzureRmLoadBalancer

Le module est installable avec la commande suivante :

Install-Module -Name AzureRM -AllowClobber

Module Azure (alias ASM)

Pour ceux ayant débuté Azure avant l'arrivée du mode de gestion ARM (Azure Ressource Management), il y avait le mode de gestion ASM (Azure Service Management) dont le portail aujourd'hui n'est plus en activité (RIP !).

Cependant, vous avez pu remarquer que certaines ressources de type ASM persistent au sein du portail ARM sous le nom de Classic. Il est toujours possible de gérer ce type de ressources via PowerShell mais il faut installer un module supplémentaire qui vient en complément du module AzureRm. Il est installable via la commande suivante :

Install-Module -Name Azure -AllowClobber

Module Az (ASM + ARM)

C'est donc le remplaçant officiel du module AzureRm ainsi que du module Azure depuis fin 2018.
Il est installable via la commande suivante :

Install-Module -Name Az -AllowClobber

Les changements concernent principalement la partie technique du module, son utilisation est très similaire à celle de l'ancien module.
Il suffit de remplacer toutes les commandes Verb-AzureRMNoun par Verb-AzNoun. Exemple :

Get-AzLoadBalancer

Microsoft, afin de faciliter la transition, a ajouté des alias qui vous permettront d'utiliser vos anciens scripts adaptés au module AzureRm. L'activation des alias se fait via la commande suivante :

Enable-AzureRmAlias

Cependant, attention, si vous décidez d'utiliser les alias du nouveau module Az, il est impératif de désinstaller toutes les versions précédentes du module AzureRM. 

Il est possible techniquement de faire cohabiter les deux modules en les installant tous les deux sur la même machine mais Microsoft recommande la désinstallation de toutes les versions des modules AzureRm avant d'installer le module Az.

Et pour les autres OS/langages ?

Microsoft, devant le succès de sa plateforme Azure, a constaté le besoin de pouvoir administrer celle-ci depuis un poste autre que Windows ou depuis des périphériques BYOD.

C'est ainsi que son nés PowerShell 6, l'environnement Azure CLI ou encore Cloud Shell. Ces moyens d'adminitration cross-plateformes feront donc l'objet de la deuxième partie de ce billet.

PowerShell/SQL–Script générant un .csv contenant le résultat d’une requête

Introduction

Le script suivant permet :

  • de récupérer le résultat d’une requête SQL dans un fichier .csv,
  • de renseigner les actions réalisées dans l’EventViewer,
  • de retourner un code d’erreur (0 ou 1) suite à l’exécution du script.

Prérequis

  • Powershell
  • SQL

Présentation du script

Variables :

[string] $server = "localhost" : Serveur SQL
[string] $database = "base" : Base SQL
[string] $query = "SELCT * FROM Table" : Requête ou procédure stockée
[string] $extractFilePath = "C:\test.csv" : Emplacemnt de génération du fichier .csv
[string] $delimiter = ";" : Délimiteur à utiliser pour la génération du fichier .csv
[int] $skipline = 1 : Nombre de ligne à tronquer (utile si l’on souhaite retirer la ligne d’entête.

Script

 

# Variables d'entrée
param
    (
    [string] $server = "localhost", 
    [string] $database = "base",
    [string] $query = "SELECT * FROM Table",
    [string] $extractFilePath = "C:\test.csv",
    [string] $delimiter = ";",
    [int] $skipline = 1

    )

#MAIN
function main
{

#Variables calculées
$full_date = Get-Date;
$custom_date = $full_date.ToString("yyyyMMdd");
$DataSet = New-Object System.Data.DataSet;
$eventsource ="CsvFromSQL_Powershell";
$eventlogname = "Application";
[int] $errorcode = 1;


#Génération de la source d'evenements si inexistante
if ([System.Diagnostics.EventLog]::SourceExists($eventsource) -eq $false) 
    {
        #write-host "Creating event source $eventsource on event log $eventlogname"
        [System.Diagnostics.EventLog]::CreateEventSource($eventsource, $eventlogname)
        #write-host -foregroundcolor green "Event source $eventsource created"
    }
        else
    {
        #write-host -foregroundcolor yellow "Warning: Event source $eventsource already exists. Cannot create this source on Event log $eventlogname"
    }

<#
$logFileExists = Get-EventLog -list | Where-Object {$_.logdisplayname -eq $eventlogname} 
if (! $logFileExists) 
    {
        New-EventLog -LogName $eventlogname -Source $eventsource
    }
#>

#Execution des fonctions et récupération du code d'erreur
$errorcode = Test-SQLConn -_server $server -_database $database;
$errorcode = sql_to_dataset -_server $server -_database $database -_query $query -_dataset $DataSet;
$errorcode = dataset_to_csv -_dataset $DataSet -_extractFilePath $extractFilePath;
return $errorcode;
}

#Test connection SQL
Function Test-SQLConn ($_server, $_database)
{
    $errorcode=0;
    $connectionString = "Data Source=$_server;Integrated Security=true;Initial Catalog=$_database;Connect Timeout=3;";
    $sqlConn = new-object ("Data.SqlClient.SqlConnection") $connectionString;
    trap
        {
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 1 -EntryType Error -Message "Cannot connect to server $_server or database $_database.";
        $errorcode=1;
        Write-Host $errorcode;
        exit
        }
    $sqlConn.Open()
    if ($sqlConn.State -eq 'Open')
        {
        $sqlConn.Close();
        Start-Sleep -s 1;
        Write-EventLog  -LogName $eventlogname -Source $eventsource -EventID 0 -EntryType Information -Message "Connected to server $_server on database $_database.";
        }
}

#Rempli un dataset à partir d'une requête SQL
function sql_to_dataset
    {
    param($_server, $_database, $_query, $_dataset);
    $connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};";
    $connectionString = [string]::Format($connectionTemplate, $_server, $_database);
    $errorcode=0;
    try
        {
        $connection = New-Object System.Data.SqlClient.SqlConnection -ErrorAction stop;
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 0 -EntryType Information -Message "The SQL connection has been created successfully.";
        }
    catch
        {
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 1 -EntryType Error -Message "An error occurred when creating the SQL connection. Error Message : $_.Exception.Message";
        $errorcode=1;
        Write-Host $errorcode;
        exit;
        }

    $connection.ConnectionString = $connectionString;

    try
        {
        $command = New-Object System.Data.SqlClient.SqlCommand -ErrorAction stop;
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 0 -EntryType Information -Message "The SQL command has been created successfully.";
        }
    catch
        {
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 1 -EntryType Error -Message "An error occurred when creating the SQL command. Error Message : $_.Exception.Message";
        $errorcode=1;
        Write-Host $errorcode;
        exit;
        }
    
    $command.CommandText = $_query;
    $command.Connection = $connection;

    try
        {
        $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter -ErrorAction stop;
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 0 -EntryType Information -Message "The SQL DataMapper has been created successfully.";
        }
    catch
        {
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 1 -EntryType Error -Message "An error occurred when creating the SQL DataMapper. Error Message : $_.Exception.Message"; 
        $errorcode=1;
        Write-Host $errorcode;
        exit;
        }
    
    $SqlAdapter.SelectCommand = $command;

    try
        {
        $SqlAdapter.Fill($_dataSet) | Out-Null -ErrorAction stop;
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 0 -EntryType Information -Message "The Dataset has been filled successfully.";
        }
    catch
        {
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 1 -EntryType Error -Message "An error occurred when filling the Dataset. Error Message : $_.Exception.Message";
        $errorcode=1;
        Write-Host $errorcode;
        exit;
        }

    $connection.Close();
    return $errorcode;
    }

#Rempli un csv avec un dataset
function dataset_to_csv
{
param($_dataset, $_extractFilePath);
$errorcode=0;
    try
        {
        $_dataSet.Tables[0] | ConvertTo-Csv -NoTypeInformation -Delimiter $delimiter | select -Skip $skipline  | Set-Content $_extractFilePath -ErrorAction stop;
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 0 -EntryType Information -Message "The CSV file has been created successfully.";
        }
    catch
        {
        Start-Sleep -s 1;
        Write-EventLog -LogName $eventlogname -Source $eventsource -EventID 1 -EntryType Error -Message "An error occurred when generating the CSV file. Error Message : $_.Exception.Message";
        $errorcode=1;
        Write-Host $errorcode;
        exit;
        }
        return $errorcode;
}

main



 

Zabbix API with Powershell – Example

L'API de Zabbix, basée sur le standard JSON-RPC 2.0 peux bien sur etre intérrogée aussi avec Powershell.

 Ci-dessous un script montrant le principe d'interrogation de lA'PI, en recuperant certaines infos.

 Vous devez renseigner un compte (<my_zabbix_account>) avec au minimum les droits de lecture sur Zabbix, et le nom ou l'ip du serveur Front Web (<zabbix_frontweb_server>).

 

### Query Zabbix Through native zabbix json api

    $credential = Get-Credential -Credential "my_zabbix_account"

$baseurl = 'https://<name_or_ip_of_front_server>/zabbix'
$params = @{
    body =  @{
        "jsonrpc"= "2.0"
        "method"= "user.login"
        "params"= @{
            "user"= $credential.UserName
            "password"= $credential.GetNetworkCredential().Password
        }
        "id"= 1
        "auth"= $null
    } | ConvertTo-Json
    uri = "$baseurl/api_jsonrpc.php"
    headers = @{"Content-Type" = "application/json"}
    method = "Post"
}

[System.Net.ServicePointManager]::SecurityProtocol = 'tls12'
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

$result = Invoke-WebRequest @params -UseBasicParsing



$params.body = @{
    "jsonrpc"= "2.0"
    "method"= "host.get"
    "params"= @{
        output = "extend"
		selectFunctions = "extend"
		selectLastEvent = "extend"
		selectGroups = "extend"
		selectHosts = "extend"
    }
    auth = ($result.Content | ConvertFrom-Json).result
    id = 2
} | ConvertTo-Json

$result = Invoke-WebRequest @params -UseBasicParsing
$result = $result.Content | ConvertFrom-Json

 

 

 

Script Powershell – SCOM – Suppression d’overrides de monitor

Le script suivant supprime tout les overrides correspondant a une propriété donné, pour un ou plusieurs monitors.

 

 

### DELETE OVERRIDES FROM MONITORS ###

Param(
$MS
,$cred = $(Get-Credential "MyDomain\Myself")
,$targetMonitorsPatern = "Check My App*"
,$OverrideProp = "GenerateAlert"
)

# Import of SCOM module
try
{
Import-Module -Name OperationsManager -ErrorAction stop
}
catch
{
write-host -ForegroundColor red "Error during import of SCOM module"
}

# Connection to management server $MS
New-SCOMManagementGroupConnection -ComputerName $MS -Credential $cred


#The monitors
$targetMonitors = Get-SCOMMonitor -DisplayName $targetMonitorsPatern


foreach ($Monitor in $targetMonitors)
{
[array]$OverrideToDelete += Get-SCOMOverride -Monitor $Monitor | Where {$_.Property -eq $OverrideProp}
}


$OverrideToDelete | foreach {

[array]$targetMP += $_.GetManagementPack()
$targetMP.FindManagementPackElementByName($_.Name).status='PendingDelete'
$targetMP.AcceptChanges()
}










Script Powershell – SCOM - Création d’override de monitor pour une instance specifique de la classe cible

Le script suivant crée un override pour un ou plusieurs monitors, en ciblant une instance de la classe cible, correspondant a un nom de machine donné en paramètre.

 

 

### CREATE OVERRIDES FOR MONITORS, FOR SPECIFIC INSTANCE OF TARGET CLASS ###

Param(
$MS
,$cred = $(Get-Credential "MyDomain\Myself")
,$targetMPName = "My App - Overrides"
,$TargetContextComputer = "MyServer.MyDomain.home"
,$targetMonitorsPatern = "Check My App*"
,$OverrideProp = "Enabled"
,$OverrideValue = "True"
,$OverrideSuffix = ".Override_$OverrideProp`_$TargetContextComputer"
)

# Import of SCOM module
try
{
Import-Module -Name OperationsManager -ErrorAction stop
}
catch
{
write-host -ForegroundColor red "Error during import of SCOM module"
}

# Connection to management server $MS
New-SCOMManagementGroupConnection -ComputerName $MS -Credential $cred


# The target MP
$targetMP = Get-SCOMManagementPack -DisplayName $targetMPName

# The monitors
$targetMonitors = Get-SCOMMonitor -DisplayName $targetMonitorsPatern


foreach ($Monitor in $targetMonitors)
{

if( $Monitor.AlertSettings.AlertOnState -ne $null)

{
$Target= Get-SCOMClass -id $Monitor.Target.Id
$TargetContextInstance = Get-SCOMMonitoringObject -Class $Target | Where-Object {$_.'[Microsoft.Windows.Computer].PrincipalName'.value -eq $TargetContextComputer} # Get the Monitoring Object that match the $TargetContextComputer

# Name of the override
$overridename=$Monitor.name+$OverrideSuffix

# Create Override and fill its properties
$override = New-Object Microsoft.EnterpriseManagement.Configuration.ManagementPackMonitorPropertyOverride($targetMP,$overridename)
$override.Monitor = $Monitor
$override.Property = $OverrideProp
$override.Value = $OverrideValue
$override.Context = $Target
$override.ContextInstance = $TargetContextInstance.Id
$override.DisplayName = $overridename
}

}

# Verify and commit the change
$targetMP.Verify()
$targetMP.AcceptChanges()

Script Powershell – SCOM – Creation d’overrides de monitor pour une classe entiere

Le script suivant crée un override pour un groupe de monitor en ciblant toute la classe cible du monitor.

 

### CREATE OVERRIDES FOR MONITORS, FOR ENTIRE MONITOR TARGET CLASS ###

Param(
$MS
,$cred = $(Get-Credential "MyDomain\Myself")
,$targetMPName = "My App - Overrides"
,$targetMonitorsPatern = "Check My App*"
,$OverrideProp = "GenerateAlert"
,$OverrideValue = "false"
,$OverrideSuffix = ".Override_$OverrideProp`_WholeTarget"
)

# Import of SCOM module
try
{
Import-Module -Name OperationsManager -ErrorAction stop
}
catch
{
write-host -ForegroundColor red "Error during import of SCOM module"
}

# Connection to management server $MS
New-SCOMManagementGroupConnection -ComputerName $MS -Credential $cred



# The target MP
$targetMP = Get-SCOMManagementPack -DisplayName $targetMPName

# The monitors
$targetMonitors = Get-SCOMMonitor -DisplayName $targetMonitorsPatern


foreach ($Monitor in $targetMonitors)
{

if($OverrideProp -eq "GenerateAlert" -AND $OverrideValue -eq "false" -AND $Monitor.AlertSettings.AlertOnState -ne $null) # If we want to change the "GenerateAlert" Property to "false", we check also that Alert is activated

{
$Target= Get-SCOMClass -id $Monitor.Target.id
$overridename=$Monitor.name+$OverrideSuffix

$override = New-Object Microsoft.EnterpriseManagement.Configuration.ManagementPackMonitorPropertyOverride($targetMP,$overridename)
$override.Monitor = $Monitor
$override.Property = $OverrideProp
$override.Value = $OverrideValue
$override.Context = $Target
$override.DisplayName = $overridename
}

}
$targetMP.Verify()
$targetMP.AcceptChanges()


 

Script Powershell – Transfert Files Through SFTP

 

Dans l’exemple suivant, Le script transfert les extracts scom du précedent article (https://blog.piservices.fr/post/2018/09/27/Script-Powershell-Extract-SCOM-to-CSV) vers un partage unix en utilisant les commandes du module Posh SSH.

Pour plus d’information sur le module Posh-SSH:

https://github.com/darkoperator/Posh-SSH

https://www.it-connect.fr/posh-ssh-connexion-ssh-depuis-powershell-sous-windows/

 

### TRANSFER CSV FILES THROUGH SFTP TO UNIX SHARE ###

# Requirement: POSH SSH Powershell Module (https://github.com/darkoperator/Posh-SSH)


param(
$UnixServ = ("MyLinuxSrv1","MyLinuxSrv2"),
$FilePath="D:\*.csv",
$SftpPath = "/home/myself/scomextract",
$FileToKeepCount = 2
)

$ScriptName = "SendCSVBySftp.ps1"


$SshModuleFile = "D:\Posh-SSH-master.zip"
$PSModulePath = "$($env:WINDIR)\system32\WindowsPowerShell\v1.0\Modules"



#FUNCTIONS

#Check for the existence of an event source with script name in operation manager eventlog to log some events
Function NewEventSource {
if(!(Test-Path "HKLM:\SYSTEM\CurrentControlSet\services\eventlog\Operations Manager\$ScriptName"))
{
New-EventLog -LogName "Operations Manager" -Source $ScriptName
}
}

#END FUNCTIONS

# LOG THE EXECUTION OF SCRIPT
$Message = "Execution of $ScriptName script"
write-host $Message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1000 -EntryType Information -Message "$Message"



# Get the credentials from the previously secured passfile.txt
$CredPath = "D:\passfile.txt"
if (!(Test-Path $CredPath))
{
$message = "Credential secured file is not present - Unable to authenticate"
Write-Host -F Red $message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1001 -EntryType Warning -Message $message
}


# Import the credential file
$Credential = Import-Clixml -Path $CredPath



# Check that $PSModulePath path exist
if (!(Test-Path $PSModulePath))
{
$message = "$PSModulePath path doesn't exist"
Write-Host -F Red $message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1002 -EntryType Warning -Message $message
exit 1
}


# Check if module has already been installed, and if not, install it as $PSModulePath+"\Posh-SSH"
If (!(Test-Path ($PSModulePath+"\Posh-SSH")))

{


#Unzip the SSH Powershell Module to Standard Module Path

$shell_app=new-object -com shell.application
$zip_file = $shell_app.namespace($SshModuleFile)
Write-Host "Uncompressing the Zip file to $($PSModulePath)" -ForegroundColor Cyan
$destination = $shell_app.namespace($PSModulePath)
$destination.Copyhere($zip_file.items(), 0x10)


# Rename the SSH Module Folder
Write-Host "Renaming folder" -ForegroundColor Cyan
Rename-Item -Path ($PSModulePath+"\Posh-SSH-master") -NewName "Posh-SSH" -Force

Write-Host "Module has been installed" -ForegroundColor Green

}


# Import of SSH Module
Write-Host -F Cyan "SSH Powershell Module already installed"
Write-Host -F Cyan "Import of SSH Powershell Module..."
Import-Module -Name posh-ssh


$UnixServ | foreach {

# Establish the SFTP connection
$SftpSession = New-SFTPSession -ComputerName $_ -Credential $Credential -AcceptKey



# Check that $SftpPath exist
if (!(Test-SFTPPath -SessionId $SftpSession.SessionId -Path "/$_$SftpPath"))
{
$message = "Unable to find `"/$_$SftpPath`" path"
Write-Host -F Red $message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1003 -EntryType Warning -Message $message
exit 1
}


# Upload each file to the SFTP path
try
{
Get-ChildItem -Path $FilePath | foreach {Set-SFTPFile -SessionId $SftpSession.SessionId -LocalFile $_ -RemotePath $SftpPath -Overwrite}
}
catch
{
$message = "Error during Sftp transfer To `"/$_$SftpPath`" path"
Write-Host $message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1004 -EntryType Warning -Message $message
exit 1
}


# Check that files are effectively present on sftp path
Get-ChildItem -Path $FilePath | foreach {

If (!(Test-SFTPPath -SessionId $SftpSession.SessionId -Path "$SftpPath/$($_.Name)"))
{
$message = "KO - The $($_.Name) file has not been copied on $($SftpSession.Host)$SftpPath"
Write-Host -F Red $message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1005 -EntryType Warning -Message $message
exit 1
}
Else
{
$message = "OK - The $($_.Name) file has well been copied on $($SftpSession.Host)$SftpPath"
Write-Host -F Green $message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1006 -EntryType Information -Message $message
}

}

}


#Delete older remote files
Get-SFTPSession | foreach {
$FileToDelete = Get-SFTPChildItem -SessionId ($_.SessionId) -Path $SftpPath | Where-Object {$_.fullname -like "*.csv"} | sort lastwritetime | select -First $FileToKeepCount
$FileToDelete
}




# Remove SftpSessions
Get-SFTPSession | foreach {Remove-SFTPSession -SessionId ($_.SessionId)}









Script Powershell – Extract SCOM to CSV

 

Le script suivant extrait les agents, management servers et gateways vers un fichier CSV en ne conservant que le plus recent.

Decommentez la section <# + $datetime#> pour ajouter au nom du fichier, la date de création.

 

#############################################################
### EXTRACT OF ALL AGENTS MONITORED BY SCOM TO A CSV FILE ###
#############################################################



Param(
[Parameter(Mandatory=$true)]$MS,
[Parameter(Mandatory=$true)]$OutPutPath
)

$ScriptName = "ExtractAssetFromSCOM.ps1"

#FUNCTIONS

#Check for the existence of an event source with script name in operation manager eventlog to log some events
Function NewEventSource {
if(!(Test-Path "HKLM:\SYSTEM\CurrentControlSet\services\eventlog\Operations Manager\$ScriptName"))
{
New-EventLog -LogName "Operations Manager" -Source $ScriptName
}
}

#END FUNCTIONS

# LOG THE EXECUTION OF SCRIPT
$Message = "Execution of $ScriptName script"
write-host $Message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1000 -EntryType Information -Message "$Message"


#Import of SCOM PS Module
try
{
Import-Module -Name OperationsManager -ErrorAction stop
}
catch
{
$Message = "Error during import of SCOM PS Module"
write-host -ForegroundColor red $Message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1001 -EntryType Warning -Message "$Message"
exit 1
}

#Connection to $MS
try
{
$ScomCon = New-SCOMManagementGroupConnection -ComputerName $MS -PassThru
}
catch
{
$Message = "Error during connection to $MS - Check the credentials used"
write-host -ForegroundColor red $Message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1002 -EntryType Warning -Message "$Message"
exit 1
}


$MGroup = $ScomCon.ManagementGroupName

#AGENT INFO SECTION

#$Agents - ALL AGENTS + ALL MS AND GW, SORTED BY DISPLAYNAME
try
{
$Agents = Get-SCOMAgent | select DisplayName -ExpandProperty DisplayName
$Agents += Get-SCOMManagementServer | select DisplayName -ExpandProperty DisplayName
$Agents = $Agents | Sort-Object -Property DisplayName
}
catch
{
$Message = "Error during retrieve of agents list"
write-host -ForegroundColor red $Message
NewEventSource
Write-EventLog -LogName "operations manager" -Source $ScriptName -EventId 1003 -EntryType Warning -Message "$Message"
exit 1
}




# Initiate the table
$export_array = @();

# Fill of table
foreach ($Ag in $Agents)
{
$export_array += @($Ag+','+$MGroup);
}


# Naming of CSV file
$datetime = Get-Date -Format "yyyy.MM.dd_HH-mm-ss";
$file_name = "Extract_SCOM_$MGroup`_"<# + $datetime#> + ".csv"; # Uncoment "<# + $datetime#>" to add creation date of file
$file_path = "$OutPutPath\" + $file_name;


# Fill of CSV File
foreach($ColItem in $export_array)
{
$csv_string = "
";
foreach($item in $ColItem)
{
$csv_string = $csv_string + $item ;
}
[array]$content += $csv_string
}

Set-Content $file_path $content


# Check of File Creation
if (Test-Path $file_path)
{
$Message = "
Creation of CSV file`"$file_path`" OK!"
write-host -ForegroundColor Green $Message
NewEventSource
Write-EventLog -LogName "
operations manager" -Source $ScriptName -EventId 1004 -EntryType Information -Message "$Message"

}
else
{
$Message = "
Creation of CSV file KO!"
write-host -ForegroundColor Red $Message
NewEventSource
Write-EventLog -LogName "
operations manager" -Source $ScriptName -EventId 1005 -EntryType Warning -Message "$Message"
exit 1
}

Write-Host $file_name
Write-Host $MGroup


# Deletion of oldest files (keep only the newest)

Write-Host "
...Deletion of oldest files..."
if ($(Get-ChildItem "
$OutPutPath\*SCOM_$MGroup*").count -gt 1)
{
$Newest = Get-ChildItem "
$OutPutPath\*SCOM_$MGroup*" | sort creationtime | select -last 1
Get-ChildItem "
$OutPutPath\*SCOM_$MGroup*" | Where-Object {$_.Name -ne $Newest.Name} | Remove-Item
}


Sécurité : Local Admin Password Solution (LAPS) - Partie 4

Réinitialisation du mot de passe :

Avec un compte faisant partie de "Read_Laps"

Normalement lorsqu'un compte ne fait pas partie du groupe "Reset_Laps", il ne bénéficie pas du droit de réinitialiser le mot de passe, vérifions cela.

Avec le client lourd  :

En effet nous pouvons constater que lorsque l'on essaie de réinitialiser le mot de passe le client affiche au bas de la fenêtre un message "Failed to request password reset"

Avec Powershell :

Avec Powershell le résultat est le même (voir plus explicite), l'utilisateur ne possède pas les droits.

Avec un compte faisant partie de "Reset_Laps"

 

Maintenant nous allons utiliser un compte qui se trouve dans le groupe "Reset_Laps" mais pas dans le groupe "Read_Laps", par conséquent il n'est pas possible de lire le mot de passe mais je devrais pouvoir le réinitialiser. 

Avec le client lourd  :

Cette fois-ci nous pouvons constater que le mot de passe n'est pas lisible, et au bas du client lourd le message "Password reset request was successful".

Avec Powershell :

Conclusion :

Grâce à cette solution gratuite de Microsoft vous pouvez améliorer la sécurité de votre parc informatique en réduisant la portée d'attaque d'une intrusion à l'aide du compte administrateur local.

 

https://www.microsoft.com/en-us/download/details.aspx?id=46899&wt.mc_id=fy16techdays

https://technet.microsoft.com/en-us/library/security/3062591?wt.mc_id=fy16techdays

https://blogs.msdn.microsoft.com/laps/

https://support.microsoft.com/en-us/kb/3062591

[Powershell] Héritage des GPO bloqué

 

Voici quelques commandes Powershell pour trouver les OU dont l'héritage est bloqué:

$GPOBlocked = Get-ADOrganizationalUnit -SearchBase "DC=LAB,DC=Org" -Filter * | Get-GPInheritance | Where-Object {$_.GpoInheritanceBlocked}
$GPOBlocked.count
$GPOBlocked | Select Name,Path

Le DistinguishedName (Path) n'est pas toujours agréable à lire je fais donc une conversion en CanonicalName, ensuite vous pouvez afficher les données à l'écran ou les écrire dans un fichier:

$GPOBlocked | ForEach-Object {
        $Name = $_."Name"
        $Path = $_."Path"
        $CanonicalName = Get-ADOrganizationalUnit -Filter {DistinguishedName -like $Path} -Properties CanonicalName | Select-Object -ExpandProperty CanonicalName
        Write-Output "$Name; $CanonicalName" | Add-Content C:\Temp\GpoInheritanceBlocked.txt
    }