PI Services

Le blog des collaborateurs de PI Services

SCOM 2019 – An Item With The Same Key Has Already Been Added après l’installation de l’UR1


SCOM 2019 UR1 a introduit de nouveaux management packs universels pour Linux. Ils vont permettre de simplifier la supervision des différentes distributions : plus besoin d’un MP différent pour chaque distribution et chaque version de cette distribution, les MP universels pourront gérer toutes les futures versions des distributions supportées.

Cependant, le déploiement de ces MP universels dans un environnement disposant encore du MP RHEL 6 et de serveurs découverts dans cette version provoque un petit souci : la vue Unix/Linux Computers ne se charge plus et l’erreur suivante apparait :

clip_image001

Par ailleurs, l’erreur suivante est visible dans le journal d’événements :

System.ArgumentException: An item with the same key has already been added.
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.MonitoringObjectPathToMonitoringObjectDictionary..ctor(IEnumerable`1 monitoringObjects)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.UnixComputerOperatingSystemHelper.JoinCollections(IEnumerable`1 managementServers, IEnumerable`1 resourcePools, IEnumerable`1 unixcomputers, IEnumerable`1 operatingSystems)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.UnixComputerOperatingSystemHelper.GetUnixComputerOperatingSystemInstances(String criteria)
   at Microsoft.SystemCenter.CrossPlatform.UI.OM.Integration.Administration.UnixAgentQuery.DoQuery(String criteria)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.DoQuery(String criteria, Nullable`1 lastModified)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.FullUpdateQuery(CacheSession session, IndexTable& indexTable, Boolean forceUpdate, DateTime queryTime)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.InternalSyncQuery(CacheSession session, IndexTable indexTable, UpdateReason reason, UpdateType updateType)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.InternalQuery(CacheSession session, UpdateReason reason)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Cache.Query`1.TryDoQuery(UpdateReason reason, CacheSession session)
   at Microsoft.EnterpriseManagement.Mom.Internal.UI.Console.ConsoleJobExceptionHandler.ExecuteJob(IComponent component, EventHandler`1 job, Object sender, ConsoleJobEventArgs args)

La raison en est simple : le nouveau MP universel a découvert une nouvelle fois vos serveurs RHEL 6, ce qui crée des doublons dans la base de données et un plantage de la console lorsqu’elle tente de les lister.

Vous disposez maintenant de deux solutions :

- Supprimer le MP RHEL 6 qui, par ailleurs, n’est plus supporté dans SCOM 2019. Vos serveurs RHEL6 seront toujours supervisés par le MP universel, mais ce dernier ne contient pas exactement les même moniteurs, il faudra donc vérifier qu’il répond à vos besoins. Par ailleurs, si vous aviez développé des règles et moniteurs qui ciblent spécifiquement la classe RHEL 6, il faudra les réécrire.
Par ailleurs, vous ne pourrez plus découvrir de nouveaux serveurs RHEL 6 si vous supprimez ce management pack.

- Désactiver la découverte des serveurs RHEL 6 via un override, puis exécuter la commande Remove-SCOMDisabledClassInstance.

Une fois débarrassé de ces doublons, tout devrait rentrer dans l’ordre !

SCOM – Création et peuplement dynamique de groupes à partir d’une clé de registre (le retour en mieux)

J’avais publié il y a quelques années un article montrant comment créer et peupler automatiquement des groupes à partir d’une clé de registre, à l’aide d’un script vbs : Création et peuplement dynamique de groupes à partir d’une clé de registre.

J’ai récemment rencontré un besoin similaire, et j’ai cette fois voulu expérimenter une technique différente et que je considère comme plus élégante, car elle ne se base que sur l’utilisation standard de deux modules natifs, sans faire appel au moindre bout de script.

Je ne reviendrai pas ici sur la nécessité de déclarer une classe unhosted, non singleton et avec un attribut clé pour le groupe : tout cela est détaillé dans l’article précédent.

Entrons donc directement dans le vif du sujet !

Comme je viens de le rappeler, un groupe est l’instance d’une classe. Les objets membres de ce groupe sont eux aussi des instances de différentes classe et ils sont rattachés au groupe à l’aide d’une relation de containment.

Notre objectif est donc de créer des instances de la classe du groupe ainsi que des relations de containment entre ces instances et les objets qui vont venir peupler les groupes.

Et pour ce faire, sans utiliser aucun script, il existe un module parfaitement adapté : System.Discovery.FilteredClassAndRelationshipSnapshotDataMapper.

clip_image002

Ce module va tout simplement créer une instance de la classe que vous lui indiquerez, avec les propriétés que vous lui indiquerez ; ainsi qu’une instance de la relation de votre choix, entre les instances indiquées.

Bien entendu, il n’est pas question ici de remplir les champs de manière statique : ce module sera intégré dans votre workflow de découverte et récupérera donc toutes les informations dont il a besoin depuis les modules précédents.

Dans cet exemple, nous souhaitons peupler les groupes à partir d’une clé de registre : nous devrions donc créer notre datasourcemoduletype avec le très classique module Microsoft.Windows.Discovery.RegistryProvider comme datasource, puisque son rôle est justement d’aller lire dans la base de registre.

Mais il existe une solution encore plus simple : un module natif combinant RegistryProvider et SnapshotDataMapper existe déjà ! Il s’agit du module Microsoft.Windows.FilteredRegistryClassAndRelationshipDiscoveryProvider.

Une fois ces éléments mis bout à bout, on arrive au fragment suivant qu’il suffit de modifier en y indiquant la clé de registre qui vous intéresse :

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="Test.My.Computers.Group" Accessibility="Public" Abstract="false" Base="MSIL!Microsoft.SystemCenter.InstanceGroup" Hosted="false" Singleton="false" Extension="false" >
          <Property ID="RegistryValue" Type="string" AutoIncrement="false" Key="true" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
        </ClassType>
        
      </ClassTypes>
    </EntityTypes>
  </TypeDefinitions>
  <Monitoring>
    <Discoveries>
      <Discovery ID="Test.My.Computers.Group.Discovery" Enabled="true" Target="Windows!Microsoft.Windows.OperatingSystem" ConfirmDelivery="false" Remotable="true" Priority="Normal">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="Test.My.Computers.Group">
            <Property TypeID="Test.My.Computers.Group" PropertyID="RegistryValue" />
          </DiscoveryClass>
        </DiscoveryTypes>
        <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.FilteredRegistryClassAndRelationshipDiscoveryProvider">
          <ComputerName>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</ComputerName>
          <RegistryAttributeDefinitions>
            <RegistryAttributeDefinition>
              <AttributeName>RegistryValueExists</AttributeName>
              <Path>SOFTWARE\Test\RegistryValue</Path>
              <PathType>1</PathType>
              <!-- 0=regKey 1=regValue -->
              <AttributeType>0</AttributeType>
              <!-- 0=CheckIfExists (Boolean) 1=treat data as (String) 2=treat data as (Integer) -->
            </RegistryAttributeDefinition>
            <RegistryAttributeDefinition>
              <AttributeName>RegistryValue</AttributeName>
              <Path>SOFTWARE\Test\RegistryValue</Path>
              <PathType>1</PathType>
              <!-- 0=regKey 1=regValue -->
              <AttributeType>1</AttributeType>
              <!-- 0=CheckIfExists (Boolean) 1=treat data as (String) 2=treat data as (Integer) -->
            </RegistryAttributeDefinition>
          </RegistryAttributeDefinitions>
          <Frequency>14400</Frequency>
          <ClassId>$MPElement[Name="Test.My.Computers.Group"]$</ClassId>
          <ClassInstanceSettings>
            <Settings>
              <Setting>
                <Name>$MPElement[Name='System!System.Entity']/DisplayName$</Name>
                <Value>Test My Group - $Data/Values/RegistryValue$</Value>
              </Setting>
              <Setting>
                <Name>$MPElement[Name='Test.My.Computers.Group']/RegistryValue$</Name>
                <Value>$Data/Values/RegistryValue$</Value>
              </Setting>
            </Settings>
          </ClassInstanceSettings>
          <RelationshipId>$MPElement[Name="MSIL!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipId>
          <SourceTypeId>$MPElement[Name="Test.My.Computers.Group"]$</SourceTypeId>
          <SourceRoleSettings>
            <Settings>
              <Setting>
                <Name>$MPElement[Name='Test.My.Computers.Group']/RegistryValue$</Name>
                <Value>$Data/Values/RegistryValue$</Value>
              </Setting>
            </Settings>
          </SourceRoleSettings>
          <TargetTypeId>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</TargetTypeId>
          <TargetRoleSettings>
            <Settings>
              <Setting>
                <Name>$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$</Name>
                <Value>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
              </Setting>
            </Settings>
          </TargetRoleSettings>
          <Expression>
            <SimpleExpression>
              <ValueExpression>
                <XPathQuery Type="Boolean">Values/RegistryValueExists</XPathQuery>  
              </ValueExpression>
              <Operator>Equal</Operator> 
              <ValueExpression>
                <Value Type="Boolean">true</Value> 
              </ValueExpression>
            </SimpleExpression>
          </Expression>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="Test.My.Computers.Group">
          <Name>Test Computers Group</Name>
        </DisplayString>
        <DisplayString ElementID="Test.My.Computers.Group.Discovery">
          <Name>Test Computers Group Discovery</Name>
          <Description>This discovery rule creates and populates groups of Windows Computer Objects that contain a registry key, based on this key's value</Description>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

 

SquaredUp - Récupérer un dashboard bloqué

Lorsque l’on s’aventure dans la modification manuelle du code JSON d’un dashboard, une mauvaise manipulation est vite arrivée et peut résulter dans le blocage complet du dashboard : il n’est plus possible de le modifier, de le sauvegarder ou de revenir en arrière ; tous les boutons sont inopérants.

Heureusement, il est possible de se sortir de ce mauvais pas sans devoir totalement supprimer le dashboard !

En effet, lorsqu’ils sont en cours de modification, les dashboards sont stockés temporairement sur le serveur qui héberge SquaredUp dans le dossier C:\inetpub\wwwroot\SquaredUpv4\User\Packages\VotreLogin\dashboards sous forme de fichier JSON que vous pouvez ouvrir et modifier avec n’importe quel éditeur de texte.

Il vous suffit donc de corriger votre erreur, enregistrer le fichier, rafraichir le dashboard dans la console SquaredUp et le tour est joué, vous avez récupéré la main !

 

 

SquaredUp - Matrix Tiles et valeur absente

 Suite à mon précédent article d’introduction à SquaredUp, il est maintenant temps de s’intéresser à quelques-uns des problèmes rencontrés lors de la construction de quelques dashboards ; en particulier ici lors de l’utilisation de tuiles de type « Matrix ».

Les tuiles « Matrix » sont très intéressantes car elles permettent d’afficher plusieurs informations sous différents formats (état de l’objet, état d’un moniteur, sparkline de performance, SLA propriété de l’objet…) sur une même ligne :

Certaines subtilités ne sont cependant pas documentées, et on peut rapidement se casser les dents sur une opération qui semblait pourtant simple au premier abord.

Propriété de l’objet

 

Il est possible d’afficher une colonne contenant simplement une propriété de l’objet au format texte. Dans la capture ci-dessus, on affiche par exemple la propriété DomainDnsName de la classe Microsoft.Windows.Computer.

La syntaxe est très simple (exemple fourni dans la documentation de SquaredUp) :

{
    "title": "Domain DNS",
    "_type": "celltile/text",
    "config": {
        "display": {
            "contentTemplate": "{{properties.domainDnsName}}"
        }
    }
}


Ce que n’indique pas clairement la documentation, c’est l’obligation de respecter strictement la casse du nom de la propriété… à l’exception de son premier caractère, qui doit toujours être écrit en minuscule.

L’exemple le montre bien, mais sans explication écrite c’est loin d‘être évident !

 

Valeur calculée

SquaredUp utilise la syntaxe Mustache et supporte donc presque toutes les fonctions de transformation issues de Javascript.

Il est donc en théorie possible de ne garder que deux décimales après la virgule lors de l’affichage d’un compteur de performance avec la fonction ToFixed :

"labelTemplate": "{{ (value).ToFixed(2) }}"

 

Malheureusement, cela résulte en une colonne vide.

Et la raison est identique au point précédent : il est impératif d’utiliser la casse exacte de la syntaxe Javascript… sauf le premier caractère qui doit obligatoirement être en minuscule.

La syntaxte suivante fonctionne donc :

"labelTemplate": "{{ (value).toFixed(2) }}"

 

Simple à corriger… mais rageant lorsque l’on bute sur le problème !

 

Valeur d’un objet hébergée dans une colonne « bar »

L’exemple donné par la documentation semble encore une fois d’une simplicité enfantine :

 

{
    "title": "Memory Usage Bar",
    "_type": "celltile/bar",
    "config": {
        "source": {
            "objectname": "Memory",
            "countername": "PercentMemoryUsed"
        },
        "display": {
            "valueTemplate": "{{(value ? Math.floor(value) : 0)}}"
        }
    }
}

Le nom du compteur, éventuellement un peu de formatage de la valeur via une fonction Javascript et voilà.

Mais lorsque la Matrix est ciblée sur une classe hébergée par une autre, cela ne fonctionne pas : la colonne est vide.

Un peu plus loin dans la documentation, dans la section concernant les sparklines, on trouve l’information suivante : lorsque  la tuile est scopée sur un objet hébergé tel qu’un disque, une base de données ou un site web, il faut modifier la configuration du groupement :

"transforms": [
    {
        "operator": "group",
        "parameters": {
            "keys": [
                "managedEntityId"
            ]
        }
    },
    {
        "operator": "merge",
        "parameters": {
            "sourceKey": "id",
            "targetKey": "key.managedEntityId"
        }
    }
]

 

Il semble alors naturel de tenter la même modification pour la sparkline. Malheureusement sans succès…

La solution, qui m’a été apportée par le support de SquaredUp, est la suivante :

"transforms": [
    {
        "operator": "merge",
        "parameters": {
            "sourceKey": "id",
            "targetKey": "managedEntityId"
        }
    }
]



Il s’agit quasiment de la même syntaxe, mais en n’utilisant que le merge.

C’est tout pour aujourd’hui !

SCOM - SquaredUp, le compagnon idéal ?

 

Le problème

Si vous demandez aujourd’hui à un utilisateur de SCOM quelles sont les plus grandes faiblesses du produit, grandes sont les chances qu’il vous cite au minimum la difficulté de créer des Management Packs avancés ainsi que la lourdeur et le manque d’ergonomie de l’interface utilisateur.

Cette dernière est en effet universellement connue pour être lente (même en ayant réalisé toutes les optimisations possibles) et pour offrir une navigation peu intuitive et assez datée, y compris dans les versions les plus récentes du produit.

En découlent donc une frustration et un manque d’efficacité pour les opérateurs de supervision et la quasi impossibilité de faire adopter le produit aux utilisateurs indirects qui pourraient pourtant en bénéficier : les autres membres des services informatiques préfèrent demander à l’opérateur de chercher les données dont ils ont besoin, et les responsables des applications métier n’ont pas de réelle solution pour bénéficier de dashboards synthétiques qui leur permettraient d’obtenir une vision rapide de l’état de santé de leur application.

 

Les solutions

Bien sûr, Microsoft tente depuis déjà de nombreuses versions d’améliorer ces points avec différentes propositions de dashboards censés développer les capacités de présentation du produit (plugin Visio, Widgets dans SCOM 2012, dashboards de la console web HTML 5 depuis la version 1801), mais le résultat de ces efforts est toujours resté très en deca des espérances des utilisateurs.

C’est pourquoi différents éditeurs tiers ont tenté d’apporter une meilleure réponse à ce besoin : historiquement, Savision LiveMaps puis plus récemment SquaredUp. C’est à ce dernier arrivé que nous nous intéressons aujourd’hui, car j’ai eu l’opportunité de le redéployer récemment pour un de mes clients.

 

A quoi ca ressemble ?

Trêve de bavardage, attaquons le sujet par son aspect le plus important : son apparence.

L’interface du produit est des plus moderne, en HTML5 entièrement responsif aussi efficace sur une TV que sur un smartphone et très épurée et efficace.

Chaque dashboard est constitué d’une série de tuiles, chacune contenant un widget parmi les différents types et formats disponibles (alertes, performance, état ;D sous forme de liste, de donut…) et ainsi présenter les informations pertinentes aux différente populations d’utilisateurs :

Décisionnel, avec l’affichage instantané de la disponibilité des SLA de toutes les applications métier :

 

Infrastructure, avec les indicateurs les plus importants pour une technologie donnée (ici pour SQL) :

 

Ou encore métier, avec les indicateurs clés de tous les systèmes qui composent un applicatif :

 

 

Comment ca marche ?

Très simplement ! L’installation ne prend que quelques minutes et est quasi entièrement automatisée. Le seul composant nécessaire est IIS, l’installeur se charge de son déploiement s’il n’est pas présent sur le serveur et il est parfaitement possible d’installer SquaredUp dans un IIS existant (par exemple sur un serveur hébergeant le role Console Web de SCOM).

La configuration ne nécessite ensuite qu’un accès à la base Datawarehouse de SCOM, aucune base additionnelle n’est nécessaire. Toutes les données affichées proviennent de cette base.

Comme quoi il est possible d’avoir un affichage rapide et efficace sans aucune modification des données ou de leur structure… Microsoft devrait en prendre de la graine !

La construction des dashboards se fait ensuite entièrement graphiquement, directement dans la console web. Ils sont en réalités construits en JSON, qu’il est possible de modifier manuellement au besoin.

 

C’est tout ?

Différentes éditions existent, et les plus avancées disposent d’options intéressantes : intégrer des données provenant d’une base SQL externe, d’une requête dans Log Analytics ou du résultat de la requête d’une API REST…

Le produit dispose même d’un module nommé VADA qui permet la construction accélérée d’Applications Distribuées complexes, en détectant automatiquement les connexions TCP entre les différentes couches applicatives (load balancer vers frontal web vers backed vers base de données vers…) à l’aide de netstat exécuté en direct !

Cela ne remplace pas une application distribuée construite à l’aide de découvertes dynamiques par un développeur SCOM chevronné, mais c’est incontestablement plus rapide et pratique que la construction visuelle classique.

 

Ca doit être cher !

Je vous laisserai seul juge de ce point, mais il faut au moins reconnaitre que SquaredUp joue carte sur table en affichant publiquement ses tarifs, ce qui est excessivement rare dans le monde des extensions pour SCOM (plugins et Management Packs d’éditeurs tiers) :

 

A noter également la disponibilité d’une version d’essai au long cours « COVID19 » : 1 mois de fonctionnalités complètes (édition EAM) avec utilisateurs illimités, puis 6 mois d’édition spéciale avec 1 seul utilisateur nommé mais un nombre de dashboards OpenAccess (non cliquables) illimités. L’occasion parfait de tester le produit !

 

Des points négatifs ?

Oui, évidemment, il y en a.

SquaredUp n’a rien d’une solution magique : il ne fait qu’afficher de façon ergonomique et efficace les données collectées par SCOM.

Si votre SCOM est laissé à l’abandon depuis des années, mal ou pas tuné… vous aurez les plus grandes difficultés à obtenir un affichage pertinent. A noter que SquaredUp propose via sa société sœur Cookdown un produit tiers gratuit nommé EasyTune pour aider à résoudre ce point.

La construction des dashboard est parfois trop simple, au point que l’on se prend à regretter l’absence de certaines capacités qui nous semblent pourtant évidentes.

La documentation est claire mais pas toujours suffisamment détaillée.

Le support est par contre toujours très disponible pour vous débloquer et ouvert aux suggestions d’amélioration.

 

Conclusion

Selon moi, SquaredUp devrait être au minimum étudié dans le cadre de tous les déploiements SCOM. Il permet d’apporter à SCOM un point qui lui manque cruellement : la facilité d’accès, en particulier aux utilisateurs non techniques.

Une supervision ne sert à rien si personne ne se sert des données qui en sont issues, et SquaredUp fournit une réponse pertinente à ce manque flagrant dans le produit de base.

Azure Stack : Bug lors de la connexion PEP

Dans Azure Stack, l’établissement d’une connexion au PEP (Privileged EndPoint), appelé également VM ERC (Emergency Recovery Console), est nécessaire pour mener certaines opérations de test de l’environnement (Test-AzureStack préalable à une mise à jour) ou pour déployer certaines ressources (Resource Providers…).

La mise à jour 1910 d’Azure Stack introduit deux nouveaux cmdlet Powershell accessibles depuis le PEP : Get et Set-AzsDnsForwarder.

Malheureusement, il semble que ce module n’apprécie pas beaucoup d’être chargé depuis un ordinateur exécutant un système non-anglais puisque l’ouverture de la sessions PEP ne fonctionne plus non plus depuis cette mise à jour, avec un message d’erreur laissant entendre l’impossibilité d’ouvrir la version francaise du module AzureStack DNS :

clip_image002

Cannot find the Windows Powershell data file « Microsoft.AzureStack.DNS.psd1 » in directory « C:\Program Files\WindowsPowerShell\Modules\Microsoft.AzureStack.DNS\fr-FR », or in any parent culture directories

Les VM ERC sont assez lourdement verrouillées, et il est impossible d’aller corriger l’installation de ce module.

Il est par contre possible de forcer une Culture différente lors de l’ouverture de la Remote-PSSession à l’aide du paramètre SessionOption :

New-PSSession -ComputerName $IP -ConfigurationName PrivilegedEndpoint -Credential $AzureCredential -SessionOption (New-PSSessionOption -UICulture en-US)

Voilà qui devrait suffire à contourner le problème en attendant que Microsoft apporte une correction plus définitive !

Azure Stack : Remplacement des certificats App Service

Le Resource Provider App Service d’Azure Stack nécessite l’utilisation de certificats, qui devront donc être remplacés avant d’arriver à expiration ou en cas de compromission. Notez d’ailleurs qu’aucune alerte n’est présente dans le portail pour prévenir de leur expiration…

Bien que cette opération doive par définition être effectuée assez régulièrement, elle n’est curieusement pas documentée sur Technet.

Heureusement, elle est très simple !

Ouvrez la blade App Service dans le portail d’administration, puis dans le menu Secrets, cliquez sur Rotate dans la partie Certificates :

clip_image002

Sélectionnez ensuite les fichiers .pfx des certificats renouvelés et entrez leurs mots de passe :

clip_image004

Une fois tous les champs remplis, cliquez sur OK.

La procédure de rotation débute alors. Il est possible de suivre sa progression en cliquant sur le bouton Status :

clip_image005 clip_image007

Après environ 20 minutes, la procédure est terminée :

clip_image009

A noter : aucune interruption de service n’a été constatée pendant le déroulement de la rotation du certificat (avec un Resource Provider déployé en haute disponibilité)

Azure Stack : Le Resource Provider App Service ne se charge plus

Quelques temps après avoir déployé le Resource Provider App Service dans notre environnement Azure Stack, j’ai eu la mauvaise surprise de constater qu’il ne se chargeait plus et qu’il n’était plus possible de créer de nouvelles Web App :

clip_image002

Could not validate app name

Ni d'accéder aux propriétés de celles déjà créées :

clip_image004

La console de debug du navigateur (touche F12) permet d’observer un grand nombre d'erreurs 500 pour des requêtes vers https://management.<location>.<domaine> et vers https://api.appservice.<location>.<domaine> :

clip_image005

Dans le portail d’administration, le dashboard app service ne se charge plus non plus, et le message d’erreur donne un premier indice sur la cause du dysfonctionnement :

clip_image007

Failed to load App Service extension. Please check whether App Service resource provider and database is up and running

Allons donc voir du côté des VM « contrôleur » (CN0 et CN1) de l’environnement du Resource Provider. Il y est possible de s’y connecter en RDP à condition d’autoriser le port 3389 dans leur NSG.

Ces VM contiennent une console MMC Web Cloud Management (son raccourci se trouve sur le bureau) qui permet la gestion de tous les composants du RP (Front End, Workers…) et pourrait donc nous en apprendre plus, mais elle plante lors de son ouverture :

clip_image009

The Resource Metering Service has experienced an unhandled exception. Exception: 'Microsoft.Web.Hosting.WebHostingException: Login failed for user 'appservice_metering_Common'. ---> System.Data.SqlClient.SqlException: Login failed for user 'appservice_metering_Common'.)

Ce nouveau message d’erreur sembler confirmer un problème du côté de la base de données.

Cette dernière est hébergée sur un cluster Always On qui fait partie de l’environnement « backend » du Resource Provider : il a été déployé séparément, en utilisant le template « haute disponiblité » de référence Microsoft.

Il est possible de se connecter en RDP à un des nœud du cluster (aps-sql-0 ou 1) à partir d’une des VM « contrôleur » CN-VM sans modifier les règles NSG, à l’aide du compte admin du domaine utilisé pour déployer l’environnement backend.

Une fois connecté, on peut ouvrir SQL Management Studio sur les deux instances :

clip_image010

On constate alors qu'une seule des instance dispose des bases de données nécessaires au fonctionnement du Resource Provider.

La raison en est simple : c'est parce que le template de déploiement du backend fourni par Microsoft ne configure pas automatiquement l'availability group always on. C'est d'ailleurs rappelé dans les release notes du Resource provider, il faut le configurer manuellement… mais il est facile de manquer ce détail, et la procédure n’est pas détaillée.

Commençons donc par sauvegarder les bases, puis identifions le serveur qui est configuré comme « Primary » dans l’Availability Group Always On :

clip_image011

La procédure d’ajout des bases ne peut être réalisée que sur le nœud « primary », mais il doit aussi être celui qui possède les bases; ce n’est pas le cas ici (les bases sont sur le nœud « secondary », il faut donc commencer par réaliser un failover de l'availability group.

Nous pouvons ensuite y rajouter les bases :

Faites un clic-droit sur Availability Databases puis sélectionnez Add Database.

Cochez les bases à ajouter :

clip_image013

Cliquez sur connect pour ouvrir la connexion vers l'autre noeud

clip_image015

Sélectionnez une synchronisation « Full » puis terminez l’assistant.

Les bases sont très petites et la synchronisation est donc quasi-instantanée.

Une fois terminée, la MMC Web Cloud doit maintenant se lancer sans erreur et le Resource Provider est à nouveau fonctionnel!

SCOM : créer une découverte de registre pour la valeur Default

Dans SCOM, les découvertes basées sur une clé de registre sont les plus communes. Il existe des dizaines de guides et tutoriaux qui détaillent la marche à suivre, je ne reviendrai donc pas sur les fondamentaux mais je me contenterai du rappel suivant : comme le montre l’extrait de code suivant, cette découverte nécessite la définition du chemin de la clé de registre à récupérer et il est possible de définir différents PathTypes et AttributeTypes :

<RegistryAttributeDefinition>

<AttributeName>##UniqueID##RegValueData</AttributeName>

<Path>##RegValuePath##</Path>

<PathType>1</PathType> <!-- 0=RegKey 1=RegValue -->

<AttributeType>2</AttributeType> <!-- 0=CheckIfExists (Boolean) 1=treat data as (String) 2=treat data as (Integer) -->

</RegistryAttributeDefinition>

PathType permet de préciser s’il faut récupérer un Clé ou une Valeur, et AttributeType permet de de préciser si le module doit se contenter de vérifier l’existence de l’entrée, de récupérer sa valeur sous forme de String (chaine de caractères) ou de la récupérer sous forme d’Integer (entier numérique).

Jusqu’ici, rien de bien particulier pour qui est habitué à travailler avec ces découvertes…

Un cas particulier s’est cependant présenté il y a quelques semaines : une application dont le numéro de version était stocké dans la valeur « Default Value » :

clip_image001_thumb

S’il s’agit manifestement d’une valeur plus que d’une clé, elle ne possède pas de nom… comment faire alors pour indiquer son chemin ?

Faut-il indiquer le chemin de la clé parente mais préciser PathType = 1 comme s’il s’agissait d’une valeur ?

Ou bien indiquer le chemin SOFTWARE\Appli\(Default) ?

Un très ancien article du blog de Marius Sutra (https://blogs.msdn.microsoft.com/mariussutara/2008/02/28/howto-registry-attribute-definition-explained/) indique qu’un PathType = 2 existerait pour ce cas précis, mais les tests ne sont pas concluants et aucune autre trace de cette possibilité ne semble exister, même pas dans la définition de la probe Registry sur MSDN.

Après quelques essais infructueux, la bonne réponse a finalement été trouvée directement par le client avec qui je travaillais :

Il suffit en réalité d’indiquer le chemin sous la forme SOFTWARE\Appli\\ (avec deux antislash à la fin, donc), et de conserver le PathType = 1

Encore une astuce à conserver dans un coin… Merci Jérémie !

Outils Azure : récupérer le certificat du proxy transparent

L’utilisation d’Azure et d’outils qui s’appuient dessus (tels que Storage Explorer, Azure Migrate, Azure CLI, Visual Studio et VS Code…) est devenue presque banale et quotidienne pour beaucoup d’entre nous.

Cependant, dans un environnement d’entreprise, un problème récurrent se présente : la connexion d’un de ces outils à Azure échoue avec un message indiquant un certificat racine incorrect ou autosigné ; comme par exemple ici « Self-signed certificate in certificate chain » :

clip_image002

Une vérification dans les journal d’événement CAPI2 de la machine où s’exécute l’outil devrait permettre d’identifier quel certificat empêche la connexion et, bien souvent il s’agit du proxy d’entreprise qui fonctionne en mode « transparent », interceptant ainsi tous les flux sortants.

Deux solutions sont alors possibles : demander à l’administrateur du proxy de mettre en liste d’exclusion les flux nécessaires à l’application, ou récupérer le certificat racine du proxy afin de l’intégrer au magasin des autorités de confiance de votre machine ou dans les certificats de confiance de l’application, si elle le supporte (c’est le cas pour Storage Explorer).

C’est bien entendu le second cas qui nous intéresse ici. Pour récupérer le certificat, il est nécessaire de passer par l’outil OpenSSL qui n’est pas présent nativement sous Windows. Fort heureusement, il est possible de télécharger des binaires déjà compilées et de les exécuter directement depuis l’invite de commande : https://sourceforge.net/projects/openssl/

Une fois l’archive téléchargée et décompressée, la commande suivante permet de récupérer la chaine de certificats réellement reçus par le système lors d’une requête HTTPS :

s_client –showcerts –connect urldeconnexionauservice.test.com:443

clip_image004

On retrouve ici l’erreur « self signed certificate in certificate chain » ainsi que les certificats présents, inclus entre les lignes ----BEGIN CERTIFICATE---- et ----END CERTIFICATE---- :

clip_image006

Il ne reste plus qu’à copier cette chaine de caractères dans un fichier texte, à renommer ce fichier avec une extension .cer et à l’importer dans l’outil ou directement dans le magasin Windows des autorités de certification approuvées.