Tutoriel

Interroger une API XML avec authentification Windows en PowerShell

Récupérer en PowerShell des données XML accessibles depuis Excel avec l'authentification Windows.

Les noms, adresses, identifiants et résultats utilisés ici sont fictifs ou anonymisés. Les commandes doivent être testées dans un environnement de lab avant d'être utilisées ailleurs.

Certaines API internes ne renvoient pas du JSON mais du XML et utilisent l’authentification intégrée Windows.

C’est notamment assez courant avec des applications historiques ou des outils d’administration internes.

Dans ce scénario, on dispose d’une URL qui retourne une liste de serveurs au format XML. Cette URL fonctionne déjà depuis Excel via :

Données → Obtenir des données → Depuis le Web

avec le mode :

Authentification Windows

L’objectif est de récupérer les mêmes informations directement depuis PowerShell.

1. Tester l’accès avec le compte Windows courant

Si Excel fonctionne avec l’authentification Windows, le premier test à réaliser en PowerShell consiste à utiliser les identifiants de la session Windows courante.

$url = "https://serveur/api/servers"

$response = Invoke-WebRequest `
    -Uri $url `
    -UseDefaultCredentials

L’option :

-UseDefaultCredentials

demande à PowerShell d’utiliser le compte Windows actuellement connecté.

Selon la configuration du serveur, l’authentification pourra notamment utiliser Kerberos ou NTLM via le mécanisme d’authentification Windows intégré.

On peut ensuite vérifier que la requête a fonctionné :

$response.StatusCode

Un résultat :

200

indique que la requête HTTP a réussi.

Pour afficher le contenu renvoyé par l’API :

$response.Content

2. Cas particulier de Windows PowerShell 5.1

Sur Windows PowerShell 5.1, on peut ajouter l’option :

-UseBasicParsing

Par exemple :

$response = Invoke-WebRequest `
    -Uri $url `
    -UseDefaultCredentials `
    -UseBasicParsing

Cette option permet d’éviter certaines dépendances historiques d’Invoke-WebRequest vis-à-vis d’Internet Explorer.

Elle n’est plus nécessaire avec PowerShell 7.

3. Convertir la réponse en XML

Invoke-WebRequest retourne principalement une réponse HTTP.

Le document XML est contenu dans :

$response.Content

On peut demander à PowerShell de l’interpréter comme un document XML :

[xml]$xml = $response.Content

On dispose maintenant d’un véritable objet XML exploitable depuis PowerShell :

$xml

4. Naviguer dans le XML

Prenons un exemple simplifié :

<Servers>
    <Server>
        <Name>SRV-EXCH01</Name>
        <Model>ProLiant DL380 Gen10</Model>
        <SerialNumber>CZ123456</SerialNumber>
        <Status>OK</Status>
    </Server>

    <Server>
        <Name>SRV-EXCH02</Name>
        <Model>ProLiant DL380 Gen10</Model>
        <SerialNumber>CZ654321</SerialNumber>
        <Status>Warning</Status>
    </Server>
</Servers>

PowerShell permet de parcourir ce document très simplement.

Pour récupérer tous les serveurs :

$xml.Servers.Server

Pour récupérer uniquement les noms :

$xml.Servers.Server.Name

Ou encore :

foreach ($server in $xml.Servers.Server) {

    Write-Host "Nom    : $($server.Name)"
    Write-Host "Modèle : $($server.Model)"
    Write-Host "S/N    : $($server.SerialNumber)"
    Write-Host "État   : $($server.Status)"
    Write-Host ""
}

5. Transformer le XML en objets PowerShell

Pour travailler confortablement avec les données, il est généralement préférable de transformer les éléments XML en objets PowerShell.

$servers = foreach ($server in $xml.Servers.Server) {

    [PSCustomObject]@{
        Name         = [string]$server.Name
        Model        = [string]$server.Model
        SerialNumber = [string]$server.SerialNumber
        Status       = [string]$server.Status
    }
}

On obtient alors une collection PowerShell classique :

$servers

Par exemple :

Name        Model                  SerialNumber Status
----        -----                  ------------ ------
SRV-EXCH01  ProLiant DL380 Gen10   CZ123456     OK
SRV-EXCH02  ProLiant DL380 Gen10   CZ654321     Warning

L’intérêt est qu’on peut maintenant utiliser toute la puissance du pipeline PowerShell.

Rechercher les serveurs qui ne sont pas dans un état normal :

$servers | Where-Object Status -ne "OK"

Trier les serveurs :

$servers | Sort-Object Name

Rechercher un modèle particulier :

$servers |
    Where-Object Model -like "*DL380*"

Ou sélectionner uniquement certaines propriétés :

$servers |
    Select-Object Name, Model, Status

6. Filtrer directement côté API

Dans les exemples précédents, l’API renvoie tous les serveurs. Le filtrage est ensuite effectué localement avec PowerShell :

$servers | Where-Object Status -ne "OK"

Cette méthode fonctionne même lorsque l’API ne propose aucun mécanisme de filtrage, mais elle oblige à télécharger et à traiter toute la collection.

Si l’API accepte des paramètres de requête, le filtrage peut être effectué directement par le serveur.

Utiliser des paramètres de requête

Une API peut par exemple accepter une URL de ce type :

https://serveur/api/servers?status=Warning&model=ProLiant

Avec Invoke-RestMethod, les paramètres peuvent être transmis sous forme de table de hachage :

$url = "https://serveur/api/servers"

$query = @{
    status = "Warning"
    model  = "ProLiant"
}

$data = Invoke-RestMethod `
    -Uri $url `
    -Method Get `
    -Body $query `
    -UseDefaultCredentials

Pour une requête GET, PowerShell ajoute les éléments de la table de hachage à l’URL sous forme de paramètres de requête. Il prend également en charge l’encodage des valeurs.

La syntaxe dépend entièrement de l’API. Les paramètres pourraient tout aussi bien s’appeler :

?state=Warning
?filter=Warning
?model=DL380

ou utiliser d’autres opérateurs :

?statusNot=OK
?nameContains=EXCH

Il faut donc consulter la documentation de l’API pour connaître les noms des paramètres, les valeurs acceptées et la manière de combiner plusieurs critères.

Cas d’une API compatible OData

Certaines API utilisent la convention OData et son paramètre $filter.

Pour récupérer les serveurs dont l’état n’est pas OK :

$filter = [Uri]::EscapeDataString("Status ne 'OK'")
$url = "https://serveur/api/servers?`$filter=$filter"

$data = Invoke-RestMethod `
    -Uri $url `
    -UseDefaultCredentials

L’accent grave placé devant $filter empêche PowerShell de l’interpréter comme une variable dans l’URL.

Plusieurs conditions peuvent être combinées :

$filter = [Uri]::EscapeDataString(
    "Status ne 'OK' and contains(Model,'DL380')"
)

$url = "https://serveur/api/servers?`$filter=$filter"

$data = Invoke-RestMethod `
    -Uri $url `
    -UseDefaultCredentials

Cette syntaxe ne fonctionne que si l’API prend réellement en charge OData.

Filtrage côté API ou côté PowerShell

Le filtrage côté API présente plusieurs avantages :

  • moins de données transférées ;
  • moins de mémoire consommée par PowerShell ;
  • traitement plus rapide lorsque la collection est importante ;
  • possibilité pour le serveur d’utiliser ses propres index.

Le filtrage avec Where-Object reste toutefois nécessaire lorsque l’API ne propose pas cette fonctionnalité ou lorsqu’un traitement plus complexe doit être réalisé localement.

7. Exporter les données vers CSV

Une fois les données transformées en objets PowerShell, l’export vers Excel ou vers un fichier CSV devient trivial :

$servers |
    Export-Csv `
        -Path ".\servers.csv" `
        -NoTypeInformation `
        -Encoding UTF8

Le fichier pourra ensuite être ouvert directement dans Excel.

8. Utiliser Invoke-RestMethod

Pour interroger une API, Invoke-RestMethod peut parfois être encore plus pratique :

$data = Invoke-RestMethod `
    -Uri $url `
    -UseDefaultCredentials

Selon les en-têtes HTTP renvoyés par le serveur, notamment :

Content-Type: application/xml

PowerShell peut interpréter directement la réponse XML.

On peut vérifier ce qui a été obtenu :

$data.GetType()

et examiner les propriétés disponibles :

$data | Get-Member

Get-Member est particulièrement utile lorsque l’on découvre une API et qu’on ne connaît pas encore précisément la structure des données retournées.

9. Utiliser un autre compte Windows

Si l’API ne doit pas être interrogée avec l’utilisateur courant, on peut demander explicitement des identifiants :

$credential = Get-Credential

Puis :

$response = Invoke-WebRequest `
    -Uri $url `
    -Credential $credential `
    -UseBasicParsing

Cependant, lorsque l’authentification intégrée Windows fonctionne déjà avec le compte connecté, -UseDefaultCredentials reste généralement la solution la plus simple :

$response = Invoke-WebRequest `
    -Uri $url `
    -UseDefaultCredentials `
    -UseBasicParsing

Elle permet notamment de conserver naturellement le contexte d’authentification Windows de la session.

10. Script complet

Voici un exemple complet compatible avec Windows PowerShell 5.1 :

$url = "https://serveur/api/servers"

$response = Invoke-WebRequest `
    -Uri $url `
    -UseDefaultCredentials `
    -UseBasicParsing

[xml]$xml = $response.Content

$servers = foreach ($server in $xml.Servers.Server) {

    [PSCustomObject]@{
        Name         = [string]$server.Name
        Model        = [string]$server.Model
        SerialNumber = [string]$server.SerialNumber
        Status       = [string]$server.Status
    }
}

$servers |
    Sort-Object Name |
    Format-Table -AutoSize

À partir de là, les données peuvent être filtrées, exportées, comparées avec d’autres sources ou utilisées dans des scripts d’inventaire.

11. Attention aux namespaces XML

Certains documents XML sont un peu plus complexes et utilisent des namespaces.

Par exemple :

<ns:Servers xmlns:ns="http://example.local/servers">

Dans ce cas, une commande comme :

$xml.Servers.Server

peut ne plus fonctionner directement.

Il faudra alors utiliser les fonctions XML .NET telles que :

SelectNodes()

avec un XmlNamespaceManager.

Ce cas mérite généralement d’être traité séparément, car la syntaxe dépend directement des namespaces déclarés dans le XML.

Conclusion

PowerShell gère nativement les documents XML et l’authentification Windows, ce qui permet assez facilement de remplacer une récupération manuelle réalisée depuis Excel.

Le principe est généralement le suivant :

Invoke-WebRequest
        ↓
UseDefaultCredentials
        ↓
$response.Content
        ↓
[xml]
        ↓
PSCustomObject
        ↓
Pipeline PowerShell

Une fois le XML transformé en objets PowerShell, son origine n’a finalement plus beaucoup d’importance.

On peut alors utiliser les outils habituels :

Where-Object
Select-Object
Sort-Object
Export-Csv
ForEach-Object

Cette approche est particulièrement pratique pour automatiser des inventaires de serveurs, croiser plusieurs sources de données ou alimenter des scripts d’administration.