Powershell : expiration des secrets et certificats des applications Azure ActiveDirectory

Ce script permet d’afficher la liste des App Registrations Azure ActiveDirectory ayant un ou des secrets ou certificats expirant à 90 jours, 30 jours ou déjà expirés.

Il est bien évidemment possible d’adapter le script pour le faire tourner dans un runbook Azure ou sur un serveur de manière non-interactive en adaptant la partie authentification.

Connect-AzureAD
$AADAppsColl = Get-AzureADApplication -All:$true
foreach($AADApps in $AADAppsColl) {
    $AppID = $AADApps.AppID
    $AADApp = Get-AzureADApplication -Filter "AppID eq '$AppID'"
    $PassCreds = $AADApp.PasswordCredentials
    if ($null -ne $PassCreds) {
        foreach($PassCred in $PassCreds) {
            if($PassCred.EndDate -gt (Get-Date).AddDays(30) -and $PassCred.EndDate -le (Get-Date).AddDays(90)){
                Write-Host "AzureAD Application Name: $($AADApp.DisplayName)"
                Write-Host "KeyID: $($PassCred.KeyID)"
                Write-Host "Expires: $($PassCred.EndDate)" -ForegroundColor Green
                Write-Host `r
            }
            if($PassCred.EndDate -gt (Get-Date) -and $PassCred.EndDate -le (Get-Date).AddDays(30)) {
                Write-Host "AzureAD Application Name: $($AADApp.DisplayName)"
                Write-Host "KeyID: $($PassCred.KeyID)"
                Write-Host "Expires: $($PassCred.EndDate)" -ForegroundColor Orange
                Write-Host `r
            }
            if($PassCred.EndDate -le (Get-Date)) {
                Write-Host "AzureAD Application Name: $($AADApp.DisplayName)"
                Write-Host "KeyID: $($PassCred.KeyID)"
                Write-Host "Expired: $($PassCred.EndDate)" -ForegroundColor Red
                Write-Host `r
            }
        }
    }
    $KeyCreds = $AADApp.KeyCredentials
    if ($null -ne $KeyCreds) {
        foreach($KeyCred in $KeyCreds) {
            if($KeyCred.EndDate -gt (Get-Date).AddDays(30) -and $KeyCred.EndDate -le (Get-Date).AddDays(90)){
                Write-Host "AzureAD Application Name: $($AADApp.DisplayName)"
                Write-Host "Certificate ID: $($KeyCred.KeyID)"
                Write-Host "Expires: $($KeyCred.EndDate)" -ForegroundColor Green
                Write-Host `r
            }
            if($KeyCred.EndDate -gt (Get-Date) -and $KeyCred.EndDate -le (Get-Date).AddDays(30)) {
                Write-Host "AzureAD Application Name: $($AADApp.DisplayName)"
                Write-Host "Certificate ID: $($KeyCred.KeyID)"
                Write-Host "Expires: $($KeyCred.EndDate)" -ForegroundColor Orange
                Write-Host `r
            }
            if($KeyCred.EndDate -le (Get-Date)) {
                Write-Host "AzureAD Application Name: $($AADApp.DisplayName)"
                Write-Host "Certificate ID: $($KeyCred.KeyID)"
                Write-Host "Expired: $($KeyCred.EndDate)" -ForegroundColor Red
                Write-Host `r
            }
        }
    }
}

Une version commentée du script est disponible en téléchargement.

Powershell : rapport d’état d’occupation des disques locaux de serveurs

J’ai développé un script Powershell utilisant WMI pour adresser à tous les ordinateurs d’une sélection d’OU ActiveDirectory une requête pour connaître l’état de leurs disques. Le but de ce script est de pouvoir générer des rapports d’état (au format CSV ou XML) pour prévoir l’occupation de certaines plateformes et tracer une évolution, afin de pouvoir agir en conséquence (libération d’espace disque, augmentation du disque…)

Bien que la requête soit identique pour les deux scripts, le traitement pour un export CSV est différent de celui qui génère un fichier XML.

Le script permettant de réaliser le rapport en CSV :

function dothemaths {
	param($Disk)
	$Size = $Disk.Size
	$FS = $Disk.FreeSpace
	$DiskPerc = [math]::Round(($FS/$Size)*100,0)
	return $DiskPerc
}

$Output = @()
$SearchBases = @('OU=APP','OU=FILERS','OU=PRINT','OU=ADMIN')
foreach($SearchBase in $SearchBases){
	$Comps = Get-AdComputer -filter * -SearchBase "$SearchBase,OU=SERVERS,DC=dundermifflin,DC=inc" | select name
	foreach($Comp in $Comps){
		$DiskList = Get-WmiObject Win32_LogicalDisk -ComputerName $Comp.name
		foreach ($Disk in $DiskList){
			if($Disk.DriveType -ne 3){ continue }
			$Temp = New-Object PSCustomObject
			Add-Member -InputObject $Temp -Type NoteProperty -Name 'ComputerName' -Value $Comp.name
			Add-Member -InputObject $Temp -Type NoteProperty -Name 'DiskLetter' -Value $Disk.DeviceID
			Add-Member -InputObject $Temp -Type NoteProperty -Name 'DiskName' -Value $Disk.VolumeName
			Add-Member -InputObject $Temp -Type NoteProperty -Name 'Size' -Value $Disk.Size
			Add-Member -InputObject $Temp -Type NoteProperty -Name 'FreeSpace' -Value $Disk.FreeSpace
			Add-Member -InputObject $Temp -Type NoteProperty -Name 'FreeSpacePercentage' -Value (dothemaths($Disk))
			$Output+=$Temp
		}
	}
}
$Output | Export-CSV DiskReport.csv

Le format CSV peut ensuite être ouvert et exploité dans Excel. Afin de convertir les valeurs exprimées en octet en Mo ou en Go par exemple, il suffira de réaliser un collage spécial sur les colonnes pour faire une division par le nombre correspondant (1073741824 par exemple pour un affichage en Go).

Le script permettant de réaliser un rapport au format XML :

function dothemaths {
	param($Disk)
	$Size = $Disk.Size
	$FS = $Disk.FreeSpace
	$DiskPerc = [math]::Round(($FS/$Size)*100,0)
	return $DiskPerc
}

$XML = New-Object System.Xml.XmlDocument
$XMLd = $XML.CreateXmlDeclaration("1.0","UTF-8",$null)
$XML.AppendChild($XMLd) | out-null
$XMLn = $XML.CreateNode("element","Servers",$null)
$SearchBases = @('OU=APP','OU=FILERS','OU=PRINT','OU=ADMIN')
foreach($SearchBase in $SearchBases){
	$Comps = Get-AdComputer -filter * -SearchBase "$SearchBase,OU=SERVERS,DC=dundermifflin,DC=inc" | select name
	foreach($Comp in $Comps){
		$DiskList = Get-WmiObject Win32_LogicalDisk -ComputerName $Comp.name
		$CompNode = $XML.CreateNode("element","Server",$null)
		$CompNode.SetAttribute("Name",$Comp.name)
		foreach ($Disk in $DiskList){
			if($Disk.DriveType -ne 3){ continue }			
			$XMLe = $XML.CreateElement("DiskLetter")
			$XMLe.InnerText = $Disk.DeviceID
			$CompNode.AppendChild($XMLe) | out-null
			$XMLe = $XML.CreateElement("DiskName")
			$XMLe.InnerText = $Disk.VolumeName
			$CompNode.AppendChild($XMLe) | out-null
			$XMLe = $XML.CreateElement("Size")
			$XMLe.InnerText = $Disk.Size
			$CompNode.AppendChild($XMLe) | out-null
			$XMLe = $XML.CreateElement("FreeSpace")
			$XMLe.InnerText = $Disk.FreeSpace
			$CompNode.AppendChild($XMLe) | out-null
			$XMLe = $XML.CreateElement("FreeSpacePercentage")
			$XMLe.InnerText = dothemaths($Disk)
			$CompNode.AppendChild($XMLe) | out-null
		}
		$XMLn.AppendChild($CompNode) | out-null
	}
}
$XML.AppendChild($XMLn) | out-null
$XML.Save("DiskReport.xml")

Ensuite, le fichier XML pourra être importé et exploité dans un objet Powershell, comme je l’ai expliqué dans ce tutoriel.

VMware et Powershell : rapport des versions d’hôtes vSphere

Voici un script permettant de lister les versions des hôtes vSphere présents dans un vCenter. Le premier mode liste tous les hôtes tandis que le deuxième permet de lister une version spécifique, majeure ou mineure. Par exemple, en saisissant « 6 », alors le script cherchera les serveurs en 6.x.x tandis qu’en envoyant « 5.5 », il cherchera les serveurs en 5.5.x. Dans tous les cas, un export CSV exploitable est réalisé. Naturellement, il est nécessaire que les modules Powershell de VMware soient déployés sur la machine exécutant le script (si ils ne le sont pas, il suffit simplement de télécharger PowerCli).

Import-Module VMware.VimAutomation.Core

function allhosts{
Write-Host "Retrieving all hosts on vCenter $vcenter"
$spheres = Get-VMHost
process
}

function spechosts{
Write-Host "Retrieving specific hosts on vCenter $vcenter."
$reqver = Read-Host "Please input the desired version in one of those formats : 6, 6.0 or 6.0.0"
if($reqver.Length -lt 5) { $reqver = "$reqver*" }
$spheres = Get-VMHost | Where-Object {$_.Version -like $reqver}
process
}

function process{
$path = "$env:temp\vmware-hostver-$vcenter.csv"
$header = "Hostname,PowerState,Version"
Add-Content -Value $header -Path $path
foreach($vsphere in $spheres) {
$row = $vsphere.Name+","+$vsphere.PowerState+","+$vsphere.Version
Add-Content -Value $row -Path $path
}
Write-Host "Processing is done. Results are located in $path."
explorer $env:temp
}

Write-Host "vSphere host version listing script"
Write-Host "===================================`r`n"
$vcenter = Read-Host "vCenter to connect to ?"
Connect-VIServer $vcenter | Out-Null
Write-Host "1: List all hosts and versions on this vCenter`r`n2: List only hosts running under a specific version"
$mode = Read-Host "Choice"
switch ($mode){
1 { allhosts }
2 { spechosts }
}
Disconnect-VIServer $vcenter -confirm:$false | Out-Null
Write-Host "Bye."

Powershell : listing d’enregistrements DNS

J’ai développé un (plutôt deux, à vrai dire) script(s) Powershell permettant de remplacer la fonction de filtre présente sur la console DNS. En effet, celle-ci est un peu pénible à utiliser car il faut attendre que toute la zone soit chargée dans la console si le filtre n’est pas encore actif, puis il faut rafraîchir… et il est persistant après fermeture, ce qui fait qu’il faut réinitialiser le filtre et actualiser lorsqu’on souhaite revenir à une vision d’ensemble.

A l’exécution, le script demande le nom ou l’IP du serveur DNS à interroger, ainsi que la chaîne de caractères à trouver dans le nom de l’enregistrement. Le joker * est utilisable, ce qui permet à la fois de rechercher une expression ou une chaîne stricte.

La version basique du script renvoie ses résultats dans la console avec uniquement le nom de l’enregistrement :

Write-Host "=========================================="
Write-Host "DNS records listing script - Basic Version"
Write-Host "=========================================="
$dnsnode = Read-Host "DNS Server to query"
$lfseq = Read-Host "Character sequence to look for ('*' tokens accepted)"
Write-Host "Connecting to DNS Server and retrieving DNS zones"
$dnszones = Get-DNSServerZone -ComputerName $dnsnode | Where-Object {$_.IsReverseLookupZone -eq $false}
foreach($dnszone in $dnszones){
Write-Host "Zone currently parsed:" $dnszone.ZoneName
$dnsrecords = Get-DnsServerResourceRecord -ComputerName $dnsnode -ZoneName $dnszone.ZoneName | Where-Object {$_.HostName -like "$lfseq"} | ft HostName
if($dnsrecords.length -gt 0) { $dnsrecords }
else { Write-Host "No matching records found." }
Write-Host "`r"
}
Write-Host "Parsing over."
Write-Host "Bye."

La version améliorée renvoie les résultats dans la console, mais génère un fichier CSV offrant plus d’informations (type d’enregistrement et données de l’enregistrement) et effectue une suppression des doublons : nécessaire car la commande Powershell peut renvoyer plusieurs fois le même enregistrement si il est dans une sous-zone. Par exemple, en considérant la zone infra.local, si un enregistrement s’appelle sql dans la sous-zone prod, il apparaîtra alors deux fois : une fois en tant que sql.prod et une fois en tant que sql.prod.infra.local.

Write-Host "============================================="
Write-Host "DNS records listing script - Enhanced Version"
Write-Host "============================================="
$dnsnode = Read-Host "DNS Server to query"
$lfseq = Read-Host "Character sequence to look for ('*' tokens accepted)"
$today = Get-Date -f yyyyMMdd
$path = "dns-$today.csv"
$csvheader = "Hostname,Zone,RecordType,RecordData"
Add-Content -Value $csvheader -Path $path
Write-Host "Connecting to DNS Server and retrieving DNS zones"
$dnszones = Get-DNSServerZone -ComputerName $dnsnode | Where-Object {$_.IsReverseLookupZone -eq $false}
foreach($dnszone in $dnszones){
Write-Host "Zone currently parsed:" $dnszone.ZoneName
$dnsrecords = Get-DnsServerResourceRecord -ComputerName $dnsnode -ZoneName $dnszone.ZoneName | Where-Object {$_.HostName -like "$lfseq"}
if($dnsrecords.length -gt 0){
foreach($dnsrecord in $dnsrecords){
Write-Host "Record: "$dnsrecord.Hostname
switch($dnsrecord.RecordType){
"A" {$recorddata = $dnsrecord.RecordData.IPv4Address.IpAddressToString}
"CNAME" {$recorddata = $dnsrecord.RecordData.HostNameAlias}
}
if($dnsrecord.HostName.SubString($dnsrecord.HostName.Length-$dnszone.ZoneName.Length,$dnszone.ZoneName.Length) -ne $dnszone.ZoneName) {
$row = $dnsrecord.Hostname+","+$dnszone.ZoneName+","+$dnsrecord.RecordType+","+$recorddata
Add-Content -Value $row -Path $path
}
}
Write-Host "DNS records found:"$dnsrecords.length
}
else { Write-Host "No matching records found." }
Write-Host "`r"
}
Write-Host "Parsing over, cleaned duplicates, hence the difference between the number of lines in the CSV file and the number displayed in console. Please find the output CSV file named $path in $pwd."
Write-Host "Bye."
explorer $pwd