PI Services

Le blog des collaborateurs de PI Services

Powershell : Credentials Test Function

 

Une petite fonction Powershell pour vous permettre de vérifier des identifiants.

Function Test-Credential {
    PARAM (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [STRING]$User,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [STRING]$Pass
        )
    Process {
        # Test Credentials
        Add-Type -AssemblyName system.DirectoryServices.AccountManagement
        $DirectoryService = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
        $Credentials = $DirectoryService.ValidateCredentials($User,$Pass)

        # Return
        If ($Credentials -eq $true) {
            Write-Host "Credentials are correct" -ForegroundColor Green
            }
        Else {
            Write-Host "Credentials are no good" -ForegroundColor Red
            }
        }
    }

 

Sharepoint Online : Problème d'accès

Il m'est arrivé il y a peu de temps un cas un peu spécifique, quelqu'un qui bénéficiait de droit "Full Control" sur Sharepoint online se voyait gratfié d'un "Access Denied" à chaque tentative de connexion sur l'url de son site et ceux même en cliquant sur le lien envoyé par Sharepoint lorsqu'on lui attribuait les droits.

La Cause:

En fait cette personne avait bénéficié de droits divers et variés (en accès direct, via l'appartenance à des groupes, via les demandes, Full Control, Edit, Read Only, Limited Access...) car personne parmis les gestionnaires Shrepoint ne parvenait à lui donner les droits.

Par conséquent nous sommes tombé dans un conflit de droits d'accès qui n'a pu être résolu de manière graphique, mais via Powershell.

 

La Solution: 

Afin de solder le problème il a fallu passer par Powershell et supprimer les dépendances de ce compte via la suppression du compte de la "UserInfo List".

Pour ce faire nous avons utilisé les commandes ci-dessous (Attention le module Sharepoint Online pour Powershell est nécessaire):

# Connexion à Sharepoint Online
Connect-SPOService -Url https://monnorganisation.sharepoint.com -Credential "adm-maade@monorganisation.com"

# Vérification des groupes d'appartenance pour mon Utilisateur
$AllUsers = Get-SPOUser -Site https://monnorganisation.sharepoint.com/sites/Toto
$FilterMonUser = $AllUsers.where({$_.loginname -eq "tartanpion@globule.net"})

# Afficher les groupes de mon Utilisateur
$FilterMonUser.groups

# Suppression de mon utilisateur de la UserInfo List
Remove-SPOUser -Site "https://monnorganisation.sharepoint.com/sites/Toto" -LoginName "tartanpion@globule.net"

 

Une fois la commande terminée, j'ai pu réaffecter les bons droits au travers du bon groupe dans l'interface du site Sharepoint.

Info supplémentaires:

https://docs.microsoft.com/en-us/sharepoint/remove-users

 

SCOM - SCRIPT - Fonction powershell pour lister toutes les instances d'une machine

Le script ci-dessous intègre la requête SQL de récupération des instances d'une machine, sous forme de fonction.

All_InstancesForOneAgent_vSQLQuery.ps1

 

# SCRIPT THAT QUERY SCOM DATABASE TO GET ALL INSTANCES OF ALL CLASSES FOR A GIVEN COMPUTER ($TargetAgent)
# $TargetComputer must be Short name of computer because we look for this specific string in name, displayname and path of instances.

param(
#Short name of computer
$TargetComputer = "MyComputer"
)


# Function  Invoke-InstancesFromSQL

Function Invoke-InstancesFromSQL
                            {
                                param(
                                    [string]$dataSource = "MyScomSQLServer\OPSMGR",
                                    [string]$database = "OperationsManager",
                                    [string]$TargetComputer,
                                    [string]$sqlCommand = 
                                            $("Use $Database
                                                SELECT 
                                                   MTV.DisplayName as ClassName
	                                               ,MEGV.Path as Instance_Path
	                                               ,MEGV.Id as Instance_Id
	                                               ,MEGV.[DisplayName] as 'Entity_DisplayName'
	                                               ,MEGV.[Name] as 'Entity_Name'
	                                               ,MEGV.[FullName] as Entity_FullName
                                                   ,[IsManaged]
                                                   ,[IsDeleted]
                                                   ,HealthState = 
													CASE WHEN InMaintenanceMode = '0'
														  THEN 
															CASE [HealthState]
															WHEN '0' THEN 'Not Monitored'
															WHEN '1' THEN 'OK'
															WHEN '2' THEN 'Warning'
															WHEN '3' THEN 'Critical'
															END
														WHEN InMaintenanceMode = '1'
														THEN 
															CASE [HealthState]
															WHEN '0' THEN 'In Maintenance Mode'
															WHEN '1' THEN 'OK'
															WHEN '2' THEN 'Warning'
															WHEN '3' THEN 'Critical'
															END
													END
                                                  ,Is_Available = CASE [IsAvailable]
			                                            WHEN '1' THEN 'YES'
			                                            WHEN '2' THEN 'NO'
			                                            END
                                                  
                                                  ,In_MaintenanceMode = CASE [InMaintenanceMode]
			                                            WHEN '0' THEN 'NO'
			                                            WHEN '1' THEN 'YES'
			                                            END
                                                  
                                                  ,Start_Of_Maintenance = CASE WHEN InMaintenanceMode = '0' 
			                                            THEN null
			                                            ELSE MMV.StartTime
			                                            END
	                                              ,End_Of_Maintenance = CASE WHEN InMaintenanceMode = '0'
			                                            THEN null
			                                            ELSE MMV.ScheduledEndTime
			                                            END
                                                  	                                              
                                                  ,Maintenance_RootCause = 
														CASE WHEN InMaintenanceMode = '0'
														  THEN null
															ELSE 
																CASE MMV.ReasonCode
																WHEN '0' THEN 'Other (Planned)'
																WHEN '1' THEN 'Other (Unplanned)'
																  WHEN '2' THEN 'Hardware: Maintenance (Planned)'
																  WHEN '3' THEN 'Hardware: Maintenance (Unplanned)'
																  WHEN '4' THEN 'Hardware: Installation (Planned)'
																  WHEN '5' THEN 'Hardware: Installation (Unplanned)'
																  WHEN '6' THEN 'Operating System: Reconfiguration (Planned)'
																  WHEN '7' THEN 'Operating System: Reconfiguration (Unplanned)'
																  WHEN '8' THEN 'Application: Maintenance (Planned)'
																  WHEN '9' THEN 'Application: Maintenance (Unplanned)'
																  WHEN '10' THEN 'Application: Installation (Planned)'
																  WHEN '11' THEN 'Application: Unresponsive'
																  WHEN '12' THEN 'Application:  Unstable'
																  WHEN '13' THEN 'Security Issue'
																  WHEN '14' THEN 'Loss of network connectivity (Unplanned)'
																END
															END
                                                  
		                                            ,Maintenance_Reason =   
														CASE WHEN InMaintenanceMode = '0' 
														    THEN null
															    ELSE MMV.Comments
														END
	  
      
                                              FROM [OperationsManager].[dbo].[ManagedEntityGenericView] MEGV

                                              INNER JOIN [dbo].[ManagedTypeView] MTV on MEGV.MonitoringClassId = MTV.Id
                                              INNER JOIN [OperationsManager].[dbo].[MaintenanceModeView] MMV on MEGV.id = MMV.BaseManagedEntityId
                                              WHERE (MEGV.Name  like '%$TargetComputer%' OR MEGV.DisplayName  like '%$TargetComputer%' OR MEGV.Path  like '%$TargetComputer%')
                                              and MTV.LanguageCode = 'ENU'
                                              and MEGV.HealthState is not null
                                              and MEGV.IsDeleted <> '1'
                                              ORDER BY MTV.DisplayName
                                            ")
                                           )


                                    $connectionString = "Data Source=$dataSource; " +
                                    "Integrated Security=SSPI; " +
                                    "Initial Catalog=$database"

                                $connection = new-object system.data.SqlClient.SQLConnection($connectionString)
                                $command = new-object system.data.sqlclient.sqlcommand($sqlCommand,$connection)
                                
                                try
                                {
                                $connection.Open()
                                }
                                catch
                                {
                                write-host -F Red $("Error during sql connection - check the credentials used").ToUpper()
                                exit 1
                                }

                                $adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
                                $dataset = New-Object System.Data.DataSet
                                $adapter.Fill($dataSet) | Out-Null

                                $connection.Close()
                                $dataSet.Tables

                              }


[array]$table = Invoke-InstancesFromSQL -TargetComputer $TargetComputer


write-host "`nCOMPUTER: $TargetComputer"

Write-Host "`nNb Of Objects:"$table.count""

$table | ft -Property ClassName,Instance_Path,Entity_DisplayName,Entity_Name,Entity_FullName,HealthState,Is_Available,In_MaintenanceMode,Start_Of_Maintenance,End_Of_Maintenance,Maintenance_RootCause,Maintenance_Reason



# TO FILTER ON SPECIFIC CLASS
# $table | Where-Object {$_.CLASS_NAME -in ('Windows computer','Health Service Watcher','VMware vSphere Host','VMWare Virtual Machine','HPE ProLiant Server')} | ft -AutoSize



# SIMULATE START OF MAINTENANCE MODE FOR THE INSTANCES
#$table | foreach {Get-SCOMClassInstance -id $_.instance_id} | foreach {start-SCOMMaintenanceMode -instance $_ -WhatIf -EndTime $((Get-Date).AddHours(1))}

 

 

 

 

 

 

Script Powershell pour Zabbix - Gerer les items d'un host

L'exemple ci-dessous utilise le module PowerShell PSBBIX qui encapsule les commandes de l'API Zabbix.

Parmi les nombreuses commandes, pour exemple, le script propose de désactiver ou activer les items (points de supervision) d'un host spécifique.

N'hésitez pas pour plus de détails a aller voir le github de PSBBIX (https://github.com/yubu/psbbix-zabbix-api)

ZabbixManageHostItems.ps1

#########################################################
### ENABLE/DISABLED ALL SPECIFIC ITEMS FOR A ZABBIX HOST
### Params:
### $ZabbixSrv: Target Zabbix Server
### $TargetHost: Target host to treat
### $ItemPattern: Pattern of Items to check
### $Action: Disable or Enable
#########################################################


Param(
[Parameter(Mandatory=$true)] $ZabbixSrv,
[Parameter(Mandatory=$true)] $TargetHost,
[Parameter(Mandatory=$true)] $ItemPattern,
[Parameter(Mandatory=$true)] $Action
)


if ($PSBoundParameters.Count -lt 4)
    {
    write-host -F Red "Too few arguments!"
    write-host -F Yellow "ZabbixSrv: Target Zabbix Server`nTargetHost: Target host to treat`nItemPattern: Pattern of Items to check`nAction: Disable or Enable"
    exit 1
    } 

switch($Action)
{
"disable" {$status = 1}
"enable" {$status = 0}
default {Write-Host -F Red "Possible value for Action is 'enable' or 'disable' - END OF SCRIPT" ; exit 0}
}



# Check if psbbix module is loaded
if (!(get-module psbbix))
    {
    write-host -F Yellow "psbbix module is not loaded...search module to load it..."
    $modulepath = $env:PSModulePath -split ‘;’ | foreach {Get-ChildItem -Recurse -Path $_ | Where-Object {$_.name -eq "psbbix.psm1"}}
    if (!$modulepath)
        {
        write-host -F Yellow "unable to find psbbix.psm1...get it on https://github.com/yubu/psbbix-zabbix-api"
        exit 1
        }
    Else
        {
            try
            {
            Import-Module $($modulepath | select -Unique).fullname
            }
            catch
            {
            write-host -F error "Error during import of psbbix module"
            exit 1
            }
        }
    Write-Host -F Green "...Module loaded"

    }


$cred = Get-Credential -Credential "myaccount"


# Connect to zabbix server
try
{
Connect-Zabbix $ZabbixSrv -PSCredential $cred
}
catch
{
Write-Host -F Red "Error during connection to $ZabbixSrv"
exit 1
}


# Get all items that we want to disable
$items = Get-ZabbixItem -hostid $(Get-ZabbixHost -HostName $TargetHost).hostid | Where-Object {$_.name -like $ItemPattern}

if ($items.Count -eq 0)
    {
    Write-Host -F Yellow "No items found with '$ItemPattern' pattern for $TargetHost host"
    }

Else
    {
        try
        {
        $items | foreach {Set-ZabbixItem -itemid $_.itemid -status $status}
        }
        catch
        {
        write-host -F Red "Error during modification of Items status"
        exit 1
        }

    }


Write-host "`nFollowing items have been treated for $TargetHost`:`n"
$items = Get-ZabbixItem -hostid $(Get-ZabbixHost -HostName $TargetHost).hostid | Where-Object {$_.name -like $ItemPattern}
$items | foreach {$_.name +" -- "+  $(switch($_.status){0{"enabled"}1{"disabled"}})}


 

Script Powershell - Fonction de test de ports reseaux

Ci-dessous une fonction dont le besoin est récurrent, pour tester la disponibilité d'un ou plusieurs ports réseaux sur une ou plusieurs machines.

 test-port.ps1 (10,05 kb)

# TEST-PORT
<#
.SYNOPSIS
         CHECK AVAILABILITY OF NETWORK PORT CONNECTION ON REMOTE COMPUTERS
.OUTPUTS 
         Return Open Status and details
.PARAMETER 
        $computer: One or more target computers
        $port: One or more port to test
        $TCPtimeout: Timeout for TCP, in ms
        $UDPtimeout: Timeout for UDP, in ms
        $TCP: Precise TCP connection
        $UDP: Precise UDP connection
                 

.USAGE:
#Test-Port -computer (get-content .\servers.txt) -port 3389
#Test-Port -computer (get-content .\servers.txt) -port 80,443,23000,17990 -TCP -TCPtimeout 1000
#Test-Port -computer myserver -port 8080,8443,10443,3389,443,80,22 -TCP -UDP
#>



function Test-Port{  




 
[cmdletbinding(  
    DefaultParameterSetName = '',  
    ConfirmImpact = 'low'  
)]  
    Param(  
        [Parameter(  
            Mandatory = $True,  
            Position = 0,  
            ParameterSetName = '',  
            ValueFromPipeline = $True)]  
            [array]$computer,  
        [Parameter(  
            Position = 1,  
            Mandatory = $True,  
            ParameterSetName = '')]  
            [array]$port,  
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [int]$TCPtimeout=500,  
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [int]$UDPtimeout=500,             
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [switch]$TCP,  
        [Parameter(  
            Mandatory = $False,  
            ParameterSetName = '')]  
            [switch]$UDP                                    
        )  
    Begin {  
        If (!$tcp -AND !$udp) {$tcp = $True}  
        
        $ErrorActionPreference = "SilentlyContinue"  
        $report = @()  
    }  
    Process {     
        ForEach ($c in $computer) {  
            ForEach ($p in $port) {  
                If ($tcp) {    
                    #Create temporary holder   
                    $temp = "" | Select hostname,Server, Port, TypePort, Open, Notes  
                    #Create object for connecting to port on computer  
                    $tcpobject = new-Object system.Net.Sockets.TcpClient  
                    #Connect to remote machine's port                
                    $connect = $tcpobject.BeginConnect($c,$p,$null,$null)  
                    #Configure a timeout before quitting  
                    $wait = $connect.AsyncWaitHandle.WaitOne($TCPtimeout,$false)  
                    #If timeout  
                    If(!$wait) {  
                        #Close connection  
                        $tcpobject.Close()  
                        Write-Verbose "Connection Timeout"  
                        #Build report  
                        $temp.hostname= [System.Net.dns]::Resolve($c).hostname
                        $temp.Server = $c  
                        $temp.Port = $p  
                        $temp.TypePort = "TCP"  
                        $temp.Open = "False"  
                        $temp.Notes = "Connection to Port Timed Out"  
                    } Else {  
                        $error.Clear()  
                        $tcpobject.EndConnect($connect) | out-Null  
                        #If error  
                        If($error[0]){  
                            #Begin making error more readable in report  
                            [string]$string = ($error[0].exception).message  
                            $message = (($string.split(":")[1]).replace('"',"")).TrimStart()  
                            $failed = $true  
                        }  
                        #Close connection      
                        $tcpobject.Close()  
                        #If unable to query port to due failure  
                        If($failed){  
                            #Build report 
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname 
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "TCP"  
                            $temp.Open = "False"  
                            $temp.Notes = "$message"  
                        } Else{  
                            #Build report
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "TCP"  
                            $temp.Open = "True"    
                            $temp.Notes = ""  
                        }  
                    }     
                    #Reset failed value  
                    $failed = $Null      
                    #Merge temp array with report              
                    $report += $temp  
                }      
                If ($udp) {  
                    #Create temporary holder   
                    $temp = "" | Select Server, Port, TypePort, Open, Notes                                     
                    #Create object for connecting to port on computer  
                    $udpobject = new-Object system.Net.Sockets.Udpclient
                    #Set a timeout on receiving message 
                    $udpobject.client.ReceiveTimeout = $UDPTimeout 
                    #Connect to remote machine's port                
                    Write-Verbose "Making UDP connection to remote server" 
                    $udpobject.Connect("$c",$p) 
                    #Sends a message to the host to which you have connected. 
                    Write-Verbose "Sending message to remote host" 
                    $a = new-object system.text.asciiencoding 
                    $byte = $a.GetBytes("$(Get-Date)") 
                    [void]$udpobject.Send($byte,$byte.length) 
                    #IPEndPoint object will allow us to read datagrams sent from any source.  
                    Write-Verbose "Creating remote endpoint" 
                    $remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any,0) 
                    Try { 
                        #Blocks until a message returns on this socket from a remote host. 
                        Write-Verbose "Waiting for message return" 
                        $receivebytes = $udpobject.Receive([ref]$remoteendpoint) 
                        [string]$returndata = $a.GetString($receivebytes)
                        If ($returndata) {
                           Write-Verbose "Connection Successful"  
                            #Build report
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "UDP"  
                            $temp.Open = "True"  
                            $temp.Notes = $returndata   
                            $udpobject.close()   
                        }                       
                    } Catch { 
                        If ($Error[0].ToString() -match "\bRespond after a period of time\b") { 
                            #Close connection  
                            $udpobject.Close()  
                            #Make sure that the host is online and not a false positive that it is open 
                            If (Test-Connection -comp $c -count 1 -quiet) { 
                                Write-Verbose "Connection Open"  
                                #Build report
                                $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                                $temp.Server = $c  
                                $temp.Port = $p  
                                $temp.TypePort = "UDP"  
                                $temp.Open = "True"  
                                $temp.Notes = "" 
                            } Else { 
                                <# 
                                It is possible that the host is not online or that the host is online,  
                                but ICMP is blocked by a firewall and this port is actually open. 
                                #> 
                                Write-Verbose "Host maybe unavailable"  
                                #Build report
                                $temp.hostname= [System.Net.dns]::Resolve($c).hostname  
                                $temp.Server = $c  
                                $temp.Port = $p  
                                $temp.TypePort = "UDP"  
                                $temp.Open = "False"  
                                $temp.Notes = "Unable to verify if port is open or if host is unavailable."                                 
                            }                         
                        } ElseIf ($Error[0].ToString() -match "forcibly closed by the remote host" ) { 
                            #Close connection  
                            $udpobject.Close()  
                            Write-Verbose "Connection Timeout"  
                            #Build report 
                            $temp.hostname= [System.Net.dns]::Resolve($c).hostname 
                            $temp.Server = $c  
                            $temp.Port = $p  
                            $temp.TypePort = "UDP"  
                            $temp.Open = "False"  
                            $temp.Notes = "Connection to Port Timed Out"                         
                        } Else {                      
                            $udpobject.close() 
                        } 
                    }     
                    #Merge temp array with report              
                    $report += $temp 
					$temp
                }                                  
            }  
        }                  
    }  
    End {  
        #Generate Report  
        $report |ft -AutoSize 
    }
}





 

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

Bonjour à tous !

Dans cette seconde partie, nous allons voir quels sont les outils cross-platformes en ligne de commande étant à notre disposition afin de gérer la plateforme Microsoft Azure.

PowerShell 6

Cette version de PowerShell est une petite révolution dans le monde Microsoft, car elle est construite autour de .NET Core (au lieu de .NET Framework) ce qui lui permet d'être nativement cross-platforme. Appelée quelque peu abusivement PowerShell 6, il vaudrait mieux parler de PowerShell Core 6 pour être exact.

La dernière version stable en date est la version 6.1 (Janvier 2019).

Compatibilité (en Janvier 2019)

Pour Windows :

  • Windows 7 SP1, 8.1 et 10
  • Windows Server 2008 R2, 2012, 2012 R2, 2016, 2019

Pour Linux :

  • Ubuntu 18.04
  • Debian 8.7+ et Debian 9
  • CentOS 7
  • RHEL 7
  • OpenSuse 42.3
  • Fedora 27, 28

Pour MacOS :

  • macOS 10.12+

Les avantages

  • Compatibilité cross-plateforme native
  • Performances accrues comparées à Windows PowerShell 5.1
  • Peut coexister à coté de Windows PowerShell 5.1 (Exécutable powershell.exe pour la version 5.1 et pwsh.exe pour la version 6)
  • S'installe rapidement et facilement (Package MSI sous Windows ou ZIP sous Mac/Linux)
  • Sera utilisée pour toutes les futures évolutions de PowerShell (au contraire de la version 5.1 qui restera figée au niveau des nouvelles fonctionnalités)
  • La version 6.1 apporte un module de compatibilité (Microsoft.Windows.Compatibility) permettant d'utiliser certaines fonctionnalités de PowerShell 5.1 pas encore portées sous PowerShell Core.

Les inconvénients

  • Ne reprends pas encore 100% des fontionnalités de Windows PowerShell 5.1
  • N'est pas encore installée par défaut sous aucun OS (même Windows)
  • Cycle de vie réduit (Doit être mis à jour dans les six mois de la sortie d'une branche 6.X pour être supportée)

Installation des modules Azure

Une fois PowerShell Core 6 installé sur votre OS, l'installation des modules PowerShell Azure se déroule de la même manière qu'avec PowerShell 5.1. Par exemple, pour installer le module Az, vous pouvez toujours utiliser la commande suivante :

Install-Module -Name Az -AllowClobber

Les commandes des modules restent également les mêmes.

Azure CLI

Comme vu précédemment, PowerShell Core 6 a permis d'apporter une solution de gestion d'Azure cross-plateforme à base de PowerShell. Microsoft a décidé d'aller encore plus loin avec Azure CLI, en conservant toujours une approche cross-plateforme mais cette fois en pouvant s'affranchir complètement de PowerShell.

Azure CLI représente un environnement complet en ligne de commande permettant de gérer des ressources Azure. Je vais détailler ci-après les principales différences par rapport aux outils PowerShell existant :

  • Environnement complet écrit en Python. Concrètement, vous pouvez le lancer à partir d'une invite de commande Cmd Windows ou d'une console Bash ou d'un Terminal Mac, c'est un CLI dans un CLI.
  • Les commandes de gestion s'affranchissent de la logique PowerShell Verb-AzNoun. Elles sont de la forme az resource action --options

Azure CLI 1.0 (ASM + ARM)

Azure CLI 1.0 / Azure Xplat CLI ou connue également sous le nom de Azure CLI Classic a été la première itération de Azure CLI et sera abandonée par Microsoft à terme.

Contrairement à sa version 2.0, elle n'est pas écrite avec une base Python mais avec une base NodeJS.

Le seul intérêt de son utilisation réside aujourd'hui en sa capacité à pouvoir gérer des ressources Azure de type Classic (ou ASM pour les intimes).

Il existe actuellement trois méthodes d'installation :

  • Installation à partir d'un package NPM (Version 4.0 min recommandée pour Node JS)
npm install -g azure-cli
  •  Installation à partir d'un programme d'installation MSI (Windows) TAR (Linux) ou DMG (Mac)

Sources

  • Déploiement d'un conteneur Docker (La version 0.10.17 est la dernière supportant Azure CLI Classic pour Docker)
docker run -it microsoft/azure-cli:0.10.17

Une fois Azure CLI Classic installé, vous pouvez utiliser le même type de commande que pour Azure CLI 2.0, elles sont de la forme azure resource action --options (az est donc remplacé par azure). Voici un exemple :

azure vm create [options] <resource-group> <name> <location> -y "Windows"

Bien que nous vous conseillons fortement l'utilisation d'Azure CLI 2.0 pour gérer des ressources Azure de type ARM, vous pouvez les gérer depuis Azure CLI 1.0 avec la commande suivante :

azure config mode arm

Pour retourner en mode ASM, vous pouvez taper la commande suivante :

azure config mode asm

Il est à noter que vous ne pouvez gérer qu'un seul type de ressource (ASM ou ARM) suivant le mode que vous activez.

Azure CLI 2.0 (ARM only)

Azure CLI 2.0 ou connue également sous le nom de Azure CLI tout court est la deuxième itération de Azure CLI qui est désormais basée sur Python. Il est important de noter qu'elle ne permet de gérer que des ressources de type ARM.

4 méthodes de déploiement sont disponibles :

  • Installation à partir d'un programme d'installation MSI (Windows)

Source et Procédure

  • Installation à partir de Homebrew (Mac)

Procédure

  • Installation à partir d'un repository ou manuellement (Linux)

Via Apt Via Yum Via Zypper Manuellement

  • Déploiement d'un conteneur Docker
docker run -it microsoft/azure-cli

Une fois Azure CLI installée, vous pouvez utiliser des commandes de la forme az resource action --options. Voici un exemple :

az vm create [options] <resource-group> <name> <location> -y "Windows"

La liste complète des commandes est disponible ici : Azure CLI ARM commands

Il est à noter que cette version d'Azure CLI dispose également d'un mode Interactif facilitant la prise en main de l'interfaçe avec une saisie semi-automatique, des exemples et des descriptions de commande. Pour l'activer, vous pouvez utiliser la commande suivante :

az interactive

Azure Cloud Shell

Le petit dernier dans la liste des outils pour administrer Azure en ligne de commande est Azure Cloud Shell, et c'est celui que je vous recommanderai s'il fallait en choisir un parmis ceux présentés précédemment.

Azure Cloud Shell est une interfaçe en ligne de commande vous permettant d'administrer votre plateforme Azure directement depuis un navigateur web ou même depuis une application mobile (Azure Mobile App). Elle est accéssible depuis le portail d'administration Azure (https://portal.azure.com) ou directement à l'URL suivante : https://shell.azure.com/.

Voici ses principales caractéristiques et avantages :

  • Uniquement basée sur un navigateur web (plus de dépendances à installer ni de RSSI à soudoyer pour ouvrir des flux)
  • Offre un CLI complet basé sur Bash ou PowerShell Core 6 et toujours à jour
  • Est déjà fournie avec tous les outils existants en ligne de commande permettant de gérer Azure (modules PowerShell AzureRM, Azure et Az, ainsi que les CLI Azure CLI Classic et Azure CLI (2.0))
  • Possibilité d'envois de scripts/fichiers pour de la persistance de données via un partage Azure Files
  • Persistance du $HOME (que ce soit avec Bash/PowerShell) (taille de l'image de base 5 GB)
  • Possibilité d'éditer des fichiers présents sur le partage de fichiers via l'éditeur Monaco (commande code)
  • La liste des fonctionnalités complète se trouve ici : https://docs.microsoft.com/fr-fr/azure/cloud-shell/features#tools

Il faut cependant faire attention aux points suivants :

  • L'environnement CLI n'est pas persistant (mais le stockage et le $HOME oui)
  • Toute session se ferme au bout de 20 minutes d'inactivité
  • Le partage Azure Files utilisé pour la persistance est payant (Tarif standard d'un compte de stockage Azure)

Voilà pour ce tour d'horizon des différents outils utilisable pour gérer la plateforme Azure en ligne de commande.

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 l'API, 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()
}