PI Services

Le blog des collaborateurs de PI Services

SCCM – Le déploiement du client sur un Management Point additionnel échoue

 

Lors du déploiement du client SCCM sur un serveur hébergeant déjà le rôle Management Point, l’installation échoue.

Une analyse du log c:\windows\ccmsetup\logs\ccmsetup.log révèle le message d’erreur suivant :

"MSI: Setup was unable to register the CCM_Service_HostingConfiguration endpoint
The error code is 80041002"

"File C:\Windows\ccmsetup\{72875A95-4007-4DAC-88D8-66366F9A5045}\client.msi installation failed. Error text: ExitCode: 1603
Action: CcmRegisterHostingConfiguration.
ErrorMessages:
Setup was unable to register the CCM_Service_HostingConfiguration endpoint
The error code is 80041002"

Une rapide recherche permet de trouver la KB suivante, qui indique de supprimer le rôle MP, d’installer le client puis de réinstaller le rôle MP : https://support.microsoft.com/en-us/help/2905359/installation-of-the-configuration-manager-client-agent-fails-with-error-code-80041002

Cette solution, bien que fonctionnelle, n’est pas très satisfaisante : d’autres clients SCCM peuvent dépendre de ce serveur MP, qui peut se trouver dans un réseau isolé etc.

Heureusement, une autre solution existe : il faut installer le client via la ligne de commande, en spécifiant manuellement le chemin vers le patch le plus récent disponible, copié manuellement dans un dossier de votre choix au préalable :

C:\SMS\Client\ccmsetup.exe /mp;SERVEURMP.LAN.LOCAL INSTALL="ALL" SMSSITECODE="XXX" CCMHTTPPORT="80" CCMHTTPSPORT="443" CCMHTTPSSTATE="480" FSP="XXX.LAN.LOCAL" CCMFIRSTCERT="1" patch="C:\SMS\Client\configmgr2012ac-sp2r2sp1-kb3100144-x64.msp"

Et le tour est joué !

WSUS – La synchronisation échoue pour un nouveau déploiement

Suite à un nouveau déploiement de WSUS, toutes les tentatives de synchronisation échouent :

clip_image002

SoapException: Fault occurred
at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   at Microsoft.UpdateServices.ServerSyncWebServices.ServerSync.ServerSyncProxy.GetUpdateData(Cookie cookie, UpdateIdentity[] updateIds)
   at Microsoft.UpdateServices.ServerSync.CatalogSyncAgentCore.WebserviceGetUpdateData(UpdateIdentity[] updateIds, List`1 allMetadata, List`1 allFileUrls, Boolean isForConfig)
   at Microsoft.UpdateServices.ServerSync.CatalogSyncAgentCore.GetUpdateDataInChunksAndImport(List`1 neededUpdates, List`1 allMetadata, List`1 allFileUrls, Boolean isConfigData)
   at Microsoft.UpdateServices.ServerSync.CatalogSyncAgentCore.ExecuteSyncProtocol(Boolean allowRedirect)

L’étude des fichiers de log Wsus et IIS montrent que l’appel au ServerSyncWebService échoue avec une erreur 500 :

clip_image004

clip_image006

Mais il s’agit d’une fausse piste.

En réalité, le problème provient d’une installation précédente de WSUS sur ce serveur. Bien qu’il ait été désinstallé proprement préalablement à la réinstallation (suppression du rôle Windows et de la base WID), le dossier C:\Windows\Softwaredistribution était toujours présent.

Après sa suppression (ou son renommage) et le redémarrage du service wuauserv, la synchronisation se lance.

 

clip_image009

clip_image007

clip_image011

Le fichier NetLogon.dns

Bonjour à tous,

Aujourd'hui nous allons partir à la découverte du fichier NetLogon.dns

Vous trouverez ce fichier, propre à chaque contrôleur de domaine, dans le dossier %systemroot%\System32\Config.

Comme vous le savez surement, les services AD et DNS sont très étroitement liés car les informations relatives aux différents contrôleurs de domaine ou aux différents sites AD sont stockées dans la zone DNS de la forêt AD correspondante. 

Celle-ci contient donc des enregistrements A faisant référence directement aux adresses IP des contrôleurs de domaine ou des enregistrements SRV permettant aux postes clients de la forêt AD de pouvoir accéder aux services essentiels AD.

Ce fichier intéressera donc en premier lieu :

  • Ceux qui doivent résoudre des problèmes relatifs au fonctionnement des services AD et qui ont localisé des irrégularités au niveau des enregistrements stockés dans la zone DNS AD
  • Ceux dont la zone DNS correspondant à la forêt AD se trouve sur un serveur DNS non Microsoft (gestion "manuelle" des enregistrements)

Un exemple valant mieux que 1000 mots, vous trouverez ci-dessous le contenu de ce fichier avec l'ensemble des enregistrements mentionnés ci-dessus :

Bonne exploration !

Windows : Error 997 Overlapped I/O operation is in progress.

Problème :

Alors que je voulais installer "microsoft report viewer 2008" sur mon Server WSUS un petit message d'erreur est apparu.

 

Impossible d'installer le package même après reboot du serveur.

Ce problème connu chez Microsoft serait lié à la KB2918614, cette dernière utiliserait des certificats et clés de cryptage pour hacher les fichiers d'installation avec le profil utilisateur connecté.

Cependant un utilisateur avec un profil temporaire ou le Default profil, n'est pas autorisé à utiliser des certificats et clés de cryptage, par conséquent lorsqu'un utilisateur avec un profil temporaire ou le Default profil tente d'installer un package msi, l'installation échoue et retourne le message d'erreur.

Solution :

Pour solutionner ce problème il suffit de renommer le répertoire ci dessous :

C:\ProgramData\Microsoft\Crypto\RSA\S-1-5-18 

En

C:\ProgramData\Microsoft\Crypto\RSA\S-1-5-18_BAK

Relancez l'installation, ça fonctionne.

SCOM – SQL – Trois requêtes d’inventaires des overrides par type d’objet.

Un peu a la manière du précédent post sur l’inventaire des overrides, les trois requêtes suivantes permettent de lister les overrides selon les types d’objet “Rule”, “Monitor” et “Discovery”.

 

 

/* All Overrides for Rules of language code EUN and FRA, with Original MP and Rule, Parameter Name, Override Value, Scope and Containing MP */ Use OperationsManager SELECT mpv2.DisplayName as Rule_MP_Name ,rv.DisplayName as Rule_Name ,IsEnabledByDefault = CASE WHEN rv.Enabled = '0' THEN 'NO' WHEN rv.Enabled <> '0' THEN 'YES' END ,op.OverrideableParameterName as Overrideable_Parameter ,mo.Value as Override_Value ,IsEnforced = CASE WHEN mo.Enforced = '0' THEN 'NO' WHEN mo.Enforced = '1' THEN 'YES' END ,mt.TypeName as Override_Scope ,bme.DisplayName as Override_InstanceName ,bme.Path as Override_InstancePath ,mpv.DisplayName as Override_MP_Name FROM ModuleOverride mo INNER JOIN managementpackview mpv on mpv.Id = mo.ManagementPackId INNER JOIN ruleview rv on rv.Id = mo.ParentId INNER JOIN ManagedType mt on mt.managedtypeid = mo.TypeContext INNER JOIN [dbo].[OverrideableParameter] op on op.OverrideableParameterId = mo.OverrideableParameterId INNER JOIN managementpackview mpv2 on mpv2.Id = rv.ManagementPackId LEFT JOIN BaseManagedEntity bme on bme.BaseManagedEntityId = mo.InstanceContext WHERE mpv.Sealed = 0 AND rv.LanguageCode in ('ENU','FRA') AND mpv.LanguageCode in ('ENU','FRA') AND mpv2.LanguageCode in ('ENU','FRA') --ORDER BY mpv2.DisplayName

 

/*All Overrides for Monitor of language code EUN and FRA, with Original MP and Monitor, Parameter Name, Override Value, Scope and Containing MP */ Use OperationsManager SELECT mpv2.DisplayName as Monitor_MP_Name ,mv.DisplayName as Monitor_Name ,IsEnabledByDefault = CASE WHEN mv.Enabled = '0' THEN 'NO' WHEN mv.Enabled <> '0' THEN 'YES' END ,op.OverrideableParameterName as Overrideable_Parameter ,mo.Value as Override_Value ,IsEnforced = CASE WHEN mo.Enforced = '0' THEN 'NO' WHEN mo.Enforced = '1' THEN 'YES' END ,mt.TypeName as Override_Scope ,bme.DisplayName as Override_InstanceName ,bme.Path as Override_InstancePath ,mpv.DisplayName as Override_MP_Name FROM MonitorOverride mo INNER JOIN managementpackview mpv on mpv.Id = mo.ManagementPackId INNER JOIN monitorview mv on mv.Id = mo.MonitorId INNER JOIN ManagedType mt on mt.managedtypeid = mo.TypeContext INNER JOIN [dbo].[OverrideableParameter] op on op.OverrideableParameterId = mo.OverrideableParameterId INNER JOIN managementpackview mpv2 on mpv2.Id = mv.ManagementPackId LEFT JOIN BaseManagedEntity bme on bme.BaseManagedEntityId = mo.InstanceContext WHERE mpv.Sealed = 0 AND mv.LanguageCode in ('ENU','FRA') AND mpv.LanguageCode in ('ENU','FRA') AND mpv2.LanguageCode in ('ENU','FRA') ORDER BY mpv2.DisplayName

 

-- All Overrides for Discoveries of language code EUN and FRA, with Original MP and Discovery, Parameter Name, Override Value, Scope and Containing MP */ Use OperationsManager SELECT mpv2.DisplayName as Discovery_MP_Name ,dv.DisplayName as Discovery_Name ,mo.ParentType ,IsEnabledByDefault = CASE WHEN dv.Enabled = '0' THEN 'NO' WHEN dv.Enabled <> '0' THEN 'YES' END ,op.OverrideableParameterName as Overrideable_Parameter ,mo.Value as Override_Value ,IsEnforced = CASE WHEN mo.Enforced = '0' THEN 'NO' WHEN mo.Enforced = '1' THEN 'YES' END ,mt.TypeName as Override_Scope ,bme.DisplayName as Override_InstanceName ,bme.Path as Override_InstancePath ,mpv.DisplayName as Override_MP_Name FROM ModuleOverride mo INNER JOIN managementpackview mpv on mpv.Id = mo.ManagementPackId INNER JOIN DiscoveryView dv on dv.Id = mo.ParentId INNER JOIN ManagedType mt on mt.managedtypeid = mo.TypeContext INNER JOIN [dbo].[OverrideableParameter] op on op.OverrideableParameterId = mo.OverrideableParameterId INNER JOIN managementpackview mpv2 on mpv2.Id = dv.ManagementPackId LEFT JOIN BaseManagedEntity bme on bme.BaseManagedEntityId = mo.InstanceContext WHERE mpv.Sealed = 0 AND dv.LanguageCode in ('ENU','FRA') AND mpv.LanguageCode in ('ENU','FRA') AND mpv2.LanguageCode in ('ENU','FRA') AND mo.ParentType = 'Discovery' ORDER BY mpv2.DisplayName

SCOM - Requête SQL pour afficher toutes les règles ayant au moins un override ou aucun

 

 

-- QUERY TO DISPLAY ALL RULES WITH TARGET CLASSES AND THEIR OVERRIDE STATE (ANYWAY MORE THAN 1 OVERRIDE PER RULE) - (FILTERED ON 'ENU' AND 'FRA' LANGUAGES) -- THE FINAL 'GROUP BY' CLAUSE IS TO DISPLAY ONLY ONE RULE OCCURENCE (WE ONLY WANT TO KNOW THAT RULE IS OVERRIDEN AT LEAST ONE TIME. -- THAT IS EQUIVALENT TO DELETE THE 'ALL' STATEMENT IN UNION CLAUSE. PRINT 'ALL RULES THAT IS OVERRIDEN AT LEAST 1 TIME OR NOT'; WITH TEMP1 (Rule_Name,Target_Class,Override_State) AS ( SELECT rv.DisplayName as Rule_Name ,MTV.DisplayName as Target_Class ,Override_State = CASE WHEN mo.ParentId is null THEN 'NOT OVERRIDEN' WHEN mo.ParentId is not null THEN 'OVERRIDEN' END FROM RuleView rv FULL JOIN ModuleOverride mo on rv.Id = mo.ParentId INNER JOIN [dbo].[ManagedTypeView] MTV on rv.TargetMonitoringClassId = MTV.Id WHERE rv.Id not in (select mo.ParentId from ModuleOverride mo) AND rv.LanguageCode in ('ENU','FRA') AND MTV.LanguageCode in ('ENU','FRA') UNION ALL SELECT rv.DisplayName as Rule_name ,MTV.DisplayName as Target_Class ,Override_State = CASE WHEN mo.ParentId is null THEN 'NOT OVERRIDEN' WHEN mo.ParentId is not null THEN 'OVERRIDEN' END FROM RuleView rv FULL JOIN ModuleOverride mo on rv.Id = mo.ParentId JOIN [dbo].[ManagedTypeView] MTV on rv.TargetMonitoringClassId = MTV.Id WHERE rv.Id in (select mo.ParentId from ModuleOverride mo) AND rv.LanguageCode in ('ENU','FRA') AND MTV.LanguageCode in ('ENU','FRA') ) SELECT Rule_Name ,Target_Class ,Override_State FROM TEMP1 GROUP BY Rule_Name,Target_Class,Override_State ORDER BY Rule_Name

SQL Server–Optimiser les paramètre d’exécution parallèle (1/2)

Introduction :

SQL Server est conçu pour optimiser l’exécution des requêtes de manière transparente, l’une des méthodes utilisé par SQL est le calcul parallélisé de requêtes.

Cette méthode d’optimisation consiste à diviser l’exécution d’une requête à travers threads afin d’accélérer l’exécution de la requête.

Ce post composé de deux parties à pour objectif de présenter les paramètres suivants :

  • MAXDOP (partie 1/2),
  • Cost Threshold for Parallelism (partie 2/2)

Explication :

Le paramètre MAXDOP signifie “Max Degree of Parallelism”, ce paramètre permet de maitriser le nombre maximum de processeurs pouvant intervenir dans l’exécution d’une requête parallélisée, il est important de comprendre que ce paramètre ne peut être utilisé pour limiter le nombre de processeurs utilisés par SQL.

Ce lien liste les valeur recommandées par Microsoft afin de paramétrer cette valeur en fonction des versions de SQL Serveur, ce lien peut également être utilisé pour calculer la valeur optimale à positionner. Il est bien entendu nécessaire de tester les différentes configurations en fonction de la charge soumise par l’application qui utilise le serveur SQL.

Réalisation

Le paramètre MAXDOP peut être modifié depuis les propriétés de l’instance SQL concernée dans la partie Advanced->Parallelism :

image

Ou via la requête suivante (en remplaçant VALEUR par la valeur désirée) :

sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
dbo.sp_configure 'max degree of parallelism', VALEUR;
GO

RECONFIGURE;
GO

La modification du paramètre MAXDOP est prise en compte sans qu’il n’y a à redémarrerredemmarer l’instance.

Sources :

SQL Server–Optimiser les paramètre d’exécution parallèle (2/2)

Introduction :

Comme vu dans le post précèdent, SQL Server dans sa configuration par défaut optimise automatiquement l’exécution des requêtes, notamment en répartissant son exécution à travers différents threads.

Ce post composé de deux parties à pour objectif de présenter les paramètres suivants :

  • MAXDOP (partie 1/2),
  • Cost Threshold for Parallelism (partie 2/2)

La partie 2/2 de ce post auras pour objectif de configurer SQL afin que seules les requêtes considérées comme volumineuses ne soient traitées en parallèle.

Explication :

Le paramètre “Count Threshold for Parallelism” permet à SQL de ne paralléliser que les requêtes dont le coût d’exécution d’un plan en série (mesuré en secondes) serait plus élevé que la valeur définie.

L’optimisation réalisée par SQL, bien qu’efficace pour les requêtes “volumineuses”, peut se révéler désastreuse pour l’exécution concurrente d’un grand nombre de petites requêtes, il est donc judicieux de revoir la valeur de ce paramètre en fonction du type de charge exercée sur le serveur SQL.

En règle générale, une valeur comprise entre 20 et 50 est un bon début qui doit être affiné par des tests spécifiques à l’application.

Par défaut, la valeur du paramètre “Count Threshold for Parallelism” est de 5, cette valeur provient du temps nécessaire à l’exécution d’une requête sur le poste de “Nick” un employé de Microsoft membre de l’équipe Query Optimizer lorsque le Query Optimizer était en cours de développement pour SQL Server 7.

Nick devait – entre autre – développer la méthode de calcul du coût d’une requête, il a donc utilisé son ordinateur comme étalon, depuis, le coût d’exécution d’une requête estimé par SQL est….le temps d’exécution qu'aurais pris la machine de Nick.

Bien entendu, les machines utilisées ne sont plus les mêmes et les temps d’exécutions sont donc plus courts :

image

La machine de Nick.

SQL Server ignore la valeur de l'option cost threshold for parallelism dans les cas suivants :

  • L'ordinateur ne dispose que d'un seul processeur.
  • Un seul UC est disponible pour SQL Server en raison de l'option de configuration affinity mask.
  • L'option max degree of parallelism a la valeur 1.

 

Réalisation

Le paramètre Count Threshold for Parallelism peut être modifié depuis les propriétés de l’instance SQL concernée dans la partie Advanced->Parallelism :

image

Ou via la requête suivante (en remplaçant VALEUR par la valeur désirée) :

sp_configure 'show advanced options', 1;
GO
reconfigure;
GO
sp_configure 'cost threshold for parallelism', VALEUR;
GO
reconfigure;
GO

La modification du paramètre Count Threshold for Parallelism est prise en compte sans qu’il n’y a à redémarrer l’instance.

Sources :

SQL Server–Rapport de découverte (Discovery Report)

Introduction

Le rapport de découverte SQL Server est utile afin de lister toutes les informations relatives à aux composants installés sur votre serveur SQL, leur version et leur édition.

L’objectif de ce post est de générer un rapport en mode GUI et en via un script.

Réalisation

  • En mode GUI :

Exécuter “SQL Server Installation Center :

image

Aller dans l’onglet “Tool” et cliquer sur “Installed SQL Server features discovery report” :

image

Le rapport de découverte SQL Server est enregistré dans “C:\Program Files\Microsoft SQL Server\VERSION\Setup Bootstrap\Log\AAAAMMDD_XXXXX

  • En mode script :

Depuis un invite de commande, exécuter la commande suivante: “Setup.exe /q /Action=RunDiscovery” depuis le répertoire “C:\Program Files\Microsoft SQL Server\VERSION\Setup Bootstrap\SQLServer2012”

image

Sources

SCOM – Script Mode Maintenance des Health Service Watcher

Lors du basculement d’agents gérés par une gateway vers une gateway de secours (failover), il peut arriver que quelques alertes “Heartbeat Failure” indésirables soit générées.

Le script suivant propose de lister, démarrer  ou arrêter les modes maintenance des instances Health Service Watcher des agents gérés par une gateway/management server donné.

Cette action est bien entendu possible pour une autre classe, dans la mesure ou le DisplayName  de l’instance correspond a celui de l’agent.

 

 

########################################################################################################################################### ## SCRIPT TO START/STOP/DISPLAY MAINTENANCE MODE FOR HEALTH SERVICE WATCHER OBJECTS THAT DEPENDS FROM SPECIFIC MANAGEMENT SERVER OR GATEWAY ## Author: CJOURDAN ########################################################################################################################################### #Parameters # $MGroup : Management Group to connect to # $MStoConnect : Management server to connect to # $PrimMS : Management server or Gateway FQDN from which depend agents. # $Reason : Reason for Maintenance Mode (Valid Reason are: PlannedOther # UnPlannedOther # PlannedHardwareMaintenance # UnplannedHardwareMaintenance # PlannedHardwareInstallation # UnplannedHardwareInstallation # PlannedOperatingSystemReconfiguration # UnplannedOperatingSystemReconfiguration # PlannedApplicationMaintenance # ApplicationInstallation # ApplicationUnresponsive # ApplicationUnstable # SecurityIssue # LossOfNetworkConnectivity) # # $Comment : Comment for Maintenance Mode # $MMAction : START or STOP OR DISPLAY Maintenance Mode # $MMDuration : Maintenance Mode Duration in minutes (only valuable for START $MMAction) # EXAMPLES: # Display Health Service Watcher Maintenance Mode Status for agents primary managed by MyPriMaryMS.mydomain.home # .\ScomSwitchMM.ps1 -MGroup MyMGROUP -MStoConnect MyMS.mydomain.home -PrimMS MyPriMaryMS.mydomain.home -MMAction display # Start Health Service Watcher Maintenance Mode for agents primary managed by MyPriMaryMS.mydomain.home during 60 min # .\ScomSwitchMM.ps1 -MGroup MyMGROUP -MStoConnect MyMS.mydomain.home -PrimMS MyPriMaryMS.mydomain.home -MMAction start -MMDuration 60 Param( [Parameter(Mandatory=$false)] $ClassName = "Health Service Watcher", [Parameter(Mandatory=$false)] $MGroup = "MyMGROUP", [Parameter(Mandatory=$false)] $MStoConnect= "MyMS.mydomain.home", [Parameter(Mandatory=$true)] $PrimMS= "MyPriMaryMS.mydomain.home", [Parameter(Mandatory=$false)] $Reason = "PlannedOther", [Parameter(Mandatory=$false)] $Comment = "", [Parameter(Mandatory=$true)] $MMAction = $(Read-Host -Prompt "PROVIDE TYPE OF MAINTENANCE MODE ACTION: START or STOP OR DISPLAY"), [Parameter(Mandatory=$false)] # $MMDuration in minutes $MMDuration = 0 ) if ($MMAction -notmatch ".*STOP.*|.*START.*|.*DISPLAY.*") { write-host -ForegroundColor Yellow "YOU MUST PROVIDE VALID MAINTENANCE MODE ACTION TYPE: START or STOP or DISPLAY" EXIT 1 } $cred = $(Get-Credential -Credential "MyDomain\") #Import du module SCOM try { Import-Module -Name OperationsManager -ErrorAction stop } catch { write-host -ForegroundColor red "Error during import of SCOM PS Module" EXIT 1 } #Connection au management group $MGroup try { New-SCOMManagementGroupConnection -ComputerName $MStoConnect -Credential $cred } catch { write-host -ForegroundColor red "Error during connection to MS $MStoConnect - CHECK CREDENTIALS USED" EXIT 1 } # Get Scom agents that have $PrimMS as Primary Management Server $agents = get-scomagent | where {$_.PrimaryManagementServerName -eq $PrimMS} if (!($agents)) { write-host "NO SCOM AGENTS FOUND FOR SERVER $PrimMS - CHECK THAT THE NAME IS CORRECT (YOU MUST PROVIDE FQDN) OR THAT THIS SERVER BELONG TO $MGroup MANAGEMENT GROUP" EXIT 1 } # Get "Health Service Watcher" Class Instances $class = Get-SCOMClass -DisplayName $ClassName switch($MMAction) { START { # Get instances of "Health Service Watcher" Class $ClassInstances = Get-SCOMClassInstance -Class $class # Class to put in MM Table $ClassInstToMM = @() # Get class instances that match agents foreach ($agent in $agents) { $ClassInstToMM += ($ClassInstances | Where-Object {$_.DisplayName -eq $agent.displayname}) } $starttime = $(get-date).ToUniversalTime() $endtime = $(get-date).AddMinutes($MMDuration).ToUniversalTime() # PUT IN Maintenance Mode $($ClassInstToMM | foreach {$_.schedulemaintenancemode($starttime,$endtime,$Reason,$Comment)}) # Display effective instances in MM write-host "EFFECTIVE INSTANCES OF CLASS '$ClassName' IN MAINTENANCE MODE (Primary Managed by $PrimMS):" # Get instances of "Health Service Watcher" Class $ClassInstances = Get-SCOMClassInstance -Class $class $ClassInstInMM = @() # Get class instances that match agents foreach ($agent in $agents) { $ClassInstInMM += ($ClassInstances | Where-Object {$_.DisplayName -eq $agent.displayname}) } $ClassInstInMM } STOP { # Get instances of "Health Service Watcher" Class $ClassInstances = Get-SCOMClassInstance -Class $class # Class to put out of MM Table $ClassInstOoMM = @() # Get class instances that match agents foreach ($agent in $agents) { $ClassInstOoMM += ($ClassInstances | Where-Object {$_.DisplayName -eq $agent.displayname}) } # Only Endttime in necessary and it is now $endtime = $(get-date).ToUniversalTime() # PUT IN Maintenance Mode $($ClassInstOoMM | foreach {$_.stopmaintenancemode($endtime)}) # Display effective instances in MM write-host "EFFECTIVE INSTANCES OF CLASS '$ClassName' OUT OF MAINTENANCE MODE (Primary Managed by $PrimMS):" # Get instances of "Health Service Watcher" Class $ClassInstances = Get-SCOMClassInstance -Class $class $ClassInstOoMM = @() # Get class instances that match agents foreach ($agent in $agents) { $ClassInstOoMM += ($ClassInstances | Where-Object {$_.DisplayName -eq $agent.displayname}) } $ClassInstOoMM } DISPLAY { # Display effective instances in MM write-host "`n INSTANCES OF CLASS '$ClassName' MAINTENANCE MODE STATE (Primary Managed by $PrimMS):" # Get instances of "Health Service Watcher" Class $ClassInstances = Get-SCOMClassInstance -Class $class $ClassInstMM = @() # Get class instances that match agents foreach ($agent in $agents) { $ClassInstMM += ($ClassInstances | Where-Object {$_.DisplayName -eq $agent.displayname}) } $ClassInstMM } }