PI Services

Le blog des collaborateurs de PI Services

SCOM - Script - Fonction de suppression d'agent a distance

La fonction ci-dessous permet de supprimer un agent scom en se connectant a distance a un management server donné.

Function Remove-SCOMAgent
{

 [CmdletBinding()]
 Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$MServer,
        [Parameter(Mandatory=$true, Position=1)]
        [string]$AgentName,
        [Parameter(Mandatory=$true, Position=2)]
        [string]$User,
        [Parameter(Mandatory=$true, Position=3)]
        [string]$Domain

    )

$Cred = Get-Credential -Message "password" -Username "$Domain\$User"
	


#Import-Module OperationsManager            
New-SCOMManagementGroupConnection -ComputerName $MServer -Credential $Cred
Start-Sleep -s 3

$administration = (Get-SCOMManagementGroup).GetAdministration();
Start-Sleep -Milliseconds 500
$agentManagedComputerType = [Microsoft.EnterpriseManagement.Administration.AgentManagedComputer];
$genericListType = [System.Collections.Generic.List``1]
$genericList = $genericListType.MakeGenericType($agentManagedComputerType)
$agentList = new-object $genericList.FullName
$agent = Get-SCOMAgent *$AgentName*

If(!($agent))
{
$message = "No agent found with name `"$AgentName`""
$message
exit
}

Start-Sleep -S 5
$agentList.Add($agent);

$genericReadOnlyCollectionType = [System.Collections.ObjectModel.ReadOnlyCollection``1]
$genericReadOnlyCollection = $genericReadOnlyCollectionType.MakeGenericType($agentManagedComputerType)
$agentReadOnlyCollection = new-object $genericReadOnlyCollection.FullName @(,$agentList);    
$administration.DeleteAgentManagedComputers($agentReadOnlyCollection);
}

Remove-SCOMAgent -MServer MyMS -AgentName serv1 -User Myaccount -Domain MyDomain



 

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
            }
        }
    }

 

WSUS Server : Connection Error

Vous venez de déployer un serveur WSUS, finir sa configuration et rattacher les premières machine à se dernier; mais lorsque vous vous rexonnectez sur le serveur afin d'exécuter la console "Windows Server Update Services" vous avez le droit au message d'erreur suivant :

La cause :

Bien souvent cette erreur est lié à IIS, je vous invite donc à exécuter "Internet Information Services (IIS) Manager" puis déroulez le serveur et sélectionnez "Application Pool", ici vous pourrez constater que le "WsusPool" est arrêté.

La solution :

Sélectionnez le et faite "Advanced Settings..." dans le volet de droite, sélectionnez le "Start Mode" pour le passer de "OnDemand" à "AlwaysRunning" puis validez en faisant "OK".

Démarrez le "WsusPool", puis vous pourrez fermer la console IIS.

NB : Tant que le serveur WSUS n'aura pas redémarrez la configuration du IIS ne sera pas effective (vous devrez relancez le "WsusPool" Manuellement à chaque nouvelle connexion au serveur).

Retournez dans la console "Windows Server Update Services" et faites "Reset Server Node"

Enjoy

 

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

 

Azure DNS : Du mieux dans les zones privées !

Bref rappel : Azure DNS est un service PaaS qui propose d’héberger vos zones DNS sans que vous n’ayez besoin de vous préoccuper de la gestion de l’infrastructure sous-jacente.

De plus, comme tout service « cloud », il est possible de gérer ces zones et leurs entrées à l’aide d’une API REST, ce qui apporte une flexibilité certaine face au fonctionnement encore un peu archaïque du service Windows DNS dont la gestion passe nécessairement par la MMC DNS ou par Powershell, voire par l’utilisation de dnscmd.exe pour ceux d’entre vous qui lisent cet article en 1992.

Microsoft a introduit il y a quelques mois un type de zone « Private » à ce service, permettant ainsi d’héberger des zones qui ne seraient pas résolvables depuis l’extérieur de votre environnement ; mais ce mode souffrait de lacunes sérieuses, qui le rendaient inexploitable dans un contexte réel :

  • Il n’était disponible qu’en Powershell
  • Seules les requêtes provenant d’un unique VNet avaient le droit de créer des entrées dans une zone donnée
  • Seuls 10 VNet avaient le droit de lire les entrées d’une zone donnée
  • Ces VNet devaient être déclarés manuellement
  • Ces VNet ne devaient contenir aucune ressource au moment de leur déclaration (!!)
  • Les enregistrements étaient invisibles via powershell ou l’API REST, mais étaient bel et bien resolvables
  • … etc.

Une mise à jour majeure est apparue la semaine dernière, et elle corrige la majorité de ces défauts :

  • On peut désormais créer les zones privées directement dans le portail Azure
  • On peut lier jusqu’à 1000 VNet à une zone privée, même si ces VNet contiennent déjà des ressources ; et les notions de VNet « enregistreur » ou « requêteur » disparaissent
  • On peut désormais accéder aux enregistrements via l’API ou Powershell

Bref, on peut maintenant raisonnablement utiliser ce service dans un environnement réel… mais attention tout de même, il est toujours en Public Preview et susceptible d’évoluer n’importe quand !

Notez également que la résolution dans une zone Private DNS ne fonctionne par défaut que si votre VNet est configuré pour utiliser la résolution DNS native d’Azure.

Si vous utilisez une résolution « Custom » (un contrôleur de domaine déployé dans votre tenant par exemple), il faudra créer un conditional forwarder pour votre zone Private DNS, pointant vers l’IP d’Azure DNS (unique pour tous les tenant et toutes les régions) : 168.63.129.16

Enfin, comme la résolution dans une zone Private DNS n’est possible que depuis un VNet, il faudra également prévoir un forwarder différent sur les serveurs DNS de votre Lan si vous souhaitez pouvoir résoudre depuis cet emplacement !

L’occasion idéale pour mettre en place une partition DNS à réplication limitée

Windows DNS - créer un scope de réplication limité

Les partitions DNS intégrées à Active Directory, permettant ainsi leur réplication et leur gestion sur tous les contrôleurs de domaine disposant du rôle DNS, sont depuis longtemps la norme dans un environnement Windows.

Il existe cependant une possibilité moins connue de ce mécanisme : la partition DNS « custom », qui permet de définir un scope de réplication limité à seulement un ou quelques contrôleurs de domaine de votre choix.

J’ai eu recours à cette possibilité afin de résoudre un problème lié à l’utilisation de Conditional Forwarders que vous pourriez bien rencontrer dans votre propre environnement sans même le savoir : les Conditional Fowarders sont répliqués dans toute votre forêt de la même façon que les zones DNS « classiques » (foward et reverse), mais si vous en utilisez il est fort possible que seuls certains de vos contrôleurs de domaine soient réellement en mesure de les atteindre en raison de contraintes réseaux propres à votre environnement.

Il devient alors nécessaire de limiter la réplication de ces Conditional Forwarders aux seuls serveurs DNS qui y ont accès !

La création d’une partition DNS se fait uniquement en powershell (en tant qu’administrateur) :

Add-DnsServerDirectoryPartition -Name "ConditionalForwardersPartition"

Il est ensuite nécessaire d’y ajouter les contrôleurs de domaine qui répliqueront cette partition :

Register-DnsServerDirectoryPartition -Name "ConditionalForwardersPartition" -ComputerName "DC01.cogip.com"

Répétez cette opération pour chaque contrôleur pour lequel vous souhaitez activer la réplication sans trop prêter attention aux possibles erreurs que vous pourriez obtenir : ca marche quand même (… normalement !) ; puis vérifiez justement que les serveurs sont bien ajoutés à la zone :

Get-DnsServerDirectoryPartition -Name "ConditionalForwardersPartition"

clip_image002

Il est désormais possible de créer ou de déplacer un conditional forwarder dans cette partition, simplement à l’aide de la MMC DNS :

clip_image004

Attention : dans le cas du déplacement d’un forwarder existant déjà, assurez -vous qu’il n’est pas protégé contre la suppression ! Si c’est le cas, le déplacement échouera avec un message peu explicite, et toutes les tentatives successives échoueront également même si vous retirez la protection entre temps ! La seule solution sera ici d’utiliser ADSI Edit pour aller faire le ménage dans la partition DC=ConditionalForwardersPartition …

Azure Storage - Failed to create blob container

La création d’un conteneur Blob dans un compte Azure Storage est une opération devenue plutôt banale, mais qui peut toutefois encore donner lieu à des messages d’erreur assez cryptiques :

clip_image002

Failed to create blob container.

Details : This request is not authorized to perform this operation.

Le message d’erreur pourrait laisser penser à un manque de droits, mais l’utilisateur dispose bien de permissions suffisantes dans ce Storage Account.

Par contre, dans le cas qui nous intéresse ici, le filtrage par adresse IP est activé sur le Storage Account utilisé, et la VM qui tente d’accéder au Storage Account est déployée dans un tenant Azure différent ; le filtrage est donc réalisé à l’aide de l’IP publique de la VM :

clip_image004

Or, les connexions entrantes dans le storage account sont s-natées à l’intérieur d’un même Datacenter Azure, comme indiqué ici : https://github.com/MicrosoftDocs/azure-docs/issues/10359 . Ce n’est donc pas l’IP publique de la VM qui se présente en entrée du Storage Account, et cela explique le blocage rencontré…

Deux solutions sont toutefois disponibles :

  • Déployer le Storage Account dans une région Azure différente de celle de la VM : ca sera alors bien l’IP publique de cette dernière qui se présentera en entrée du Storage Account
  • Utiliser le filtrage par VNET à la place du filtrage par IP. Cette solution reste possible même si le VNET de la VM est dans un tenant différent de celui du Storage Account, mais il faudra que l’utilisateur qui réalise la configuration dispose de permissions spécifiques sur le VNET de la VM : https://docs.microsoft.com/en-us/azure/storage/common/storage-network-security#grant-access-from-a-virtual-network

Supervision - Zabbix - Script - Utilisation des Regex pour rechercher des données numeriques dans une chaine de caractère

Le cas présenté ici, peut être indépendant de l'outil de monitoring. Il s'agit de devoir alerter et donc définir des seuil sur une donnée qui est présenté par défaut sous forme de texte.

Pour être un peu plus clair, dans le contexte, un équipement snmp renvoi l'information d'utilisation de son file system sous la forme suivante:

"/tmp=0, /var=26, /var/log=0, /=22"

il s'agit en effet de la valeur de l'OID atpDiskUsage dans la MIB ATP-SNMP-MIB (Symantec Advanced Threat Protection)

Pour pouvoir traiter les valeurs d'utilisation du file system comme des nombre, les expressions régulières sont d'un grand secours.

Supposons que l'on veuille alerter sur le fait que "/var" soit au dessus de 80.

Dans ce cas l'expression proposé est la suivante régulière sera la suivante:

^.*(\/var=)[8-9][0-9]|100,.*$

Explication --> ^ : début de chaine

                      .*(\/var=) : n caractère n fois, suivi de "/var="  (on doit échapper le caractère '/' avec '\')

                      [8-9][0-9] : le range de chiffre est de 80 a 99

                      |100 : ou bien sur 100.

                       ,.* : la virgule présente, suivi de n caractère n fois

                       $: fin de chaine

Ce qui, pour nos 4 points de montage du file system donnera:

^.*(\/var=)[8-9][0-9]|100,.*$

^.*(\/tmp=)[8-9][0-9]|100,.*$

^.*(\/var\/log=)[8-9][0-9]|100,.*$

^.*(\/=)[8-9][0-9]|100.*$

Dans le contexte de Zabbix, Les expressions régulières ci-dessus ont donc été intégrées chacune dans un trigger utilisant la fonction "regexp() - Regular expression matching last value in period (1- match, 0 - not match)".

Indépendamment de l'outil de supervision, n'hésitez pas a user et abuser d'un outil de construction de regex tel que https://regex101.com/

 

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"}})}