Le blog technique

Toutes les astuces #tech des collaborateurs de PI Services.

#openblogPI

Retrouvez les articles à la une

PowerShell – Vérifier si une mise à jour Windows (KB) est installée sur les contrôleurs de domaine d’une forêt Active Directory

<pre class="wp-block-syntaxhighlighter-code">#Informations récupérées : Nom du contrôleur de domaine (DC), domaine Active Directory (AD) d'appartenance, OS, présence ou non des KB recherchées, date d'installation des KB

#Prérequis : l'utilisateur qui lance le script doit pouvoir requêter en remote PowerShell Administrator l'ensemble des DC de la forêt

#Paramètre obligatoire qui doit contenir la liste des KB à récupérer
#Par exemple pour appeler le script avec une liste de KB : .\Get-DomainControllerKB.ps1 -DCHotFixIDs KB5022511, KB4589208
Param
( 	
	[Parameter(Mandatory=$true)]
    [array]$DCHotFixIDs
)

#Importe le module Active Directory qui contient des commandes utilisées dans le script
Import-Module ActiveDirectory

#Permet de récupérer les noms de tous les domaines dans la forêt AD
$Domains = Get-ADForest | Select-Object Domains
$Domains = $Domains.Domains

#Créer un tableau qui contiendra l'ensemble des statuts des KB des DC de chaque domaine de la forêt
$Array = @()

#Parcours chaque domaine AD de la forêt et récupére la liste des DC
foreach ($Domain in $Domains)
{
	#Récupére l'ensemble des DC du domaine AD actuellement requêté
	$DCs = Get-ADDomainController -Server $Domain -Filter * | Select-Object Name, HostName, OperatingSystem

	#Parcours chaque DC du domaine AD actuellement requêté
    foreach ($DC in $DCs)
    {
		#Vérifie pour chaque KB si elle est présente sur le DC actuellement requêté
		foreach ($DCHotFixID in $DCHotFixIDs)
		{
			#Créer un objet PowerShell qui contiendra les informations sur la KB actuellement requêtée du DC actuellement requêté
			$Line = New-Object PSObject

			#Ajoute à l'objet PowerShell précédemment crée, le nom du DC actuellement requêté
			$Line | Add-Member -MemberType NoteProperty -Name "DomainController" -Value $DC.HostName

			#Ajoute à l'objet PowerShell précédemment crée, le domaine AD d'appartenence du DC actuellement requêté
			$Line | Add-Member -MemberType NoteProperty -Name "Domain" -Value $Domain

			#Ajoute à l'objet PowerShell précédemment crée, le système d'exploitation du DC actuellement requêté
			$Line | Add-Member -MemberType NoteProperty -Name "OperatingSystem" -Value $DC.OperatingSystem

			#Ajoute à l'objet PowerShell précédemment crée, le numéro de la KB actuellement requêté du DC actuellement requêté
			$Line | Add-Member -MemberType NoteProperty -Name "HotFixIDSearched" -Value $DCHotFixID

			#Commande qui va vérifier si la KB actuellement requêtée est présente sur le DC actuellement requêté
			$HotFixObject = Get-HotFix -ComputerName $DC.HostName -ID $DCHotFixID -ErrorAction SilentlyContinue | Select-Object Description, HotFixID, InstalledBy, InstalledOn

			#Si la KB actuellement requêtée est PRESENTE sur le DC actuellement requêté, ajoute les informations relatives à son installation dans l'objet PowerShell précédemment créé
			if ($HotFixObject)
			{
				#Ajoute à l'objet PowerShell précédemment crée, le statut présent ou absent de la KB actuellement requêtée du DC actuellement requêté
				$Line | Add-Member -MemberType NoteProperty -Name "KBIsInstalled" -Value "Success"

				#Ajoute à l'objet PowerShell précédemment crée, le nom de l'utilisateur qui a réalisé l'installation de la KB actuellement requêtée du DC actuellement requêté
				$Line | Add-Member -MemberType NoteProperty -Name "HotFixInstalledBy" -Value $HotFixObject.InstalledBy

				#Ajoute à l'objet PowerShell précédemment crée, la date d'installation, arrondi au jour prêt, de la KB actuellement requêtée du DC actuellement requêté
				$Line | Add-Member -MemberType NoteProperty -Name "HotFixInstalledOn" -Value $HotFixObject.InstalledOn

				#Ajoute à l'objet PowerShell précédemment crée, le type de KB actuellement requêté du DC actuellement requêté
				$Line | Add-Member -MemberType NoteProperty -Name "HotFixDescription" -Value $HotFixObject.Description

				#Supprime les valeurs des variables propres à la KB actuellement requêté du DC actuellement requêté
				Clear-Variable HotFixObject
			}

			#Si la KB actuellement requêtée est ABSENTE sur le DC actuellement requêté, ajoute des valeurs Fail ou NULL pour ses informations dans l'objet PowerShell précédemment créé
			else
			{
				$Line | Add-Member -MemberType NoteProperty -Name "KBIsInstalled" -Value "Fail"
				$Line | Add-Member -MemberType NoteProperty -Name "HotFixInstalledBy" -Value "NULL"
				$Line | Add-Member -MemberType NoteProperty -Name "HotFixInstalledOn" -Value "NULL"
				$Line | Add-Member -MemberType NoteProperty -Name "HotFixDescription" -Value "NULL"
			}

			#Ajoute à la variable globale qui contient l'ensemble des statuts des KB des DC de chaque domaine, l'objet PowerShell précédemment crée lors de l'itération courante
			$Array += $Line

			#Supprime les valeurs des variables propres à la KB actuellement requêtée du DC actuellement requêté
			Clear-Variable DCHotFixID, Line
		}

		#Supprime les valeurs des variables propres au DC actuellement requêté
        Clear-Variable DC
    }

	#Supprime les valeurs des variables propres au domaine actuellement requêté
    Clear-Variable Domain, DCs
}

#Affiche sous forme de liste l'ensemble des KB requêtées des DC de chaque domaine avec leurs status
$Array

<#Exemple d'affichage
DomainController  : DC01.customer.intern
Domain            : customer.intern
OperatingSystem   : Windows Server 2022 Standard
HotFixIDSearched  : KB5022511
KBIsInstalled     : Fail
HotFixInstalledBy : NULL
HotFixInstalledOn : NULL
HotFixDescription : NULL

DomainController  : DC01.customer.intern
Domain            : staff.nsi.dir
OperatingSystem   : Windows Server 2022 Standard
HotFixIDSearched  : KB4589208
KBIsInstalled     : Fail
HotFixInstalledBy : NULL
HotFixInstalledOn : NULL
HotFixDescription : NULL

DomainController  : DC02.technical.intern
Domain            : technical.intern
OperatingSystem   : Windows Server 2019 Standard
HotFixIDSearched  : KB5022511
KBIsInstalled     : Success
HotFixInstalledBy : NT AUTHORITY\SYSTEM
HotFixInstalledOn : 15/02/2023 00:00:00
HotFixDescription : Update

DomainController  : DC02.technical.intern
Domain            : technical.intern
OperatingSystem   : Windows Server 2019 Standard
HotFixIDSearched  : KB4589208
KBIsInstalled     : Success
HotFixInstalledBy : NT AUTHORITY\SYSTEM
HotFixInstalledOn : 28/01/2021 00:00:00
HotFixDescription : Update
#></pre>

 

PowerShell – Récupérer la configuration système des contrôleurs de domaine d’une forêt Active Directory

<pre class="wp-block-syntaxhighlighter-code">#Informations récupérées : Nom du contrôleur de domaine, domaine AD, adresse IP, OS, CPU, taille du disque C, taille restante du disque C, RAM
#Evolution du script : certains attributs non utilisés sont récupérés pour pouvoir éventuellement augmenter le nombre d'informations récupéré 

#Prérequis 1 : l'utilisateur qui lance ce script doit pouvoir requêter les bases WMI de tout les contrôleurs de domaine (DC) de la forêt
#Prérequis 2 : le script suppose qu'un seul adapteur réseau de chaque DC posséde une ou plusieurs adresses IP
#Prérequis 3 : le script suppose que le système d'exploitation de chaque DC est installé sur C:

#Importe le module Active Directory qui contient des commandes utilisées dans le script
Import-Module ActiveDirectory

#Permet de récupérer le nom des domaines de la forêt de façon dynamique
$Domains = Get-ADForest | Select-Object Domains
$Domains = $Domains.Domains

#Créer un tableau qui contiendra l'ensemble des DC de tout les domaines de la forêt
$DCToGetConfiguration = @()

#Parcours un par un chaque domaine de la forêt
foreach ($Domain in $Domains)
{
	#Pour chaque domaine de la forêt, récupére l'ensemble des DC du domaine
	$DomainControllersFromCurrentDomain = (Get-ADDomainController -Server $Domain -Filter * | Select-Object HostName, Domain, IPv4Address).HostName

	#Pour chaque domaine de la forêt, ajoute l'ensemble des DC du domaine à l'ensemble des DC de la forêt
	$DCToGetConfiguration += $DomainControllersFromCurrentDomain
}

#Créer un tableau qui contiendra chaque DC de la forêt avec sa configuration
$Array = @()

#Parcours un par un chaque DC de chaque domaine de la forêt, récupéré précédemment
foreach ($ServerToGetConfiguration in $DCToGetConfiguration)
{
	#Créer un objet PowerShell qui contiendra la configuration du DC actuellement requêté
    $Line = New-Object PSObject

	#Ajoute à l'objet PowerShell précédemment crée, le nom du DC actuellement requêté
    $Line | Add-Member -MemberType NoteProperty -Name "HostName" -Value $ServerToGetConfiguration

	#Récupére le domaine AD d'appartenance du DC actuellement requêté
	$WMIComputerSystemObject = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $ServerToGetConfiguration | Select-Object Name, Domain

	#Ajoute à l'objet PowerShell précédemment crée, le domaine AD d'appartenance du DC actuellement requêté
	$Line | Add-Member -MemberType NoteProperty -Name "Domain" -Value $WMIComputerSystemObject.Domain

	#Récupére l'adresse IP du DC actuellement requêté
	$WMINetworkAdapterConfigurationObjects = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $ServerToGetConfiguration | Select-Object PSComputerName, IPAddress, IPSubnet, DefaultIPGateway, DNSServerSearchOrder

	#La commande Get-WmiObject -Class Win32_NetworkAdapterConfiguration récupére les adaptateurs réseaux, certains d'entre eux ne comportent pas de carte réseau
	#La boucle suivante ne permet de récupérer que la carte réseau qui contient une adresse IP
	#Cette portion du script ne marchera pas si plusieurs cartes réseaux contiennent chacune une IP
	foreach ($NetworkAdapterConfigurationObject in $WMINetworkAdapterConfigurationObjects)
	{
		if ($NetworkAdapterConfigurationObject.IPAddress)
		{
			$Line | Add-Member -MemberType NoteProperty -Name "IPAddress" -Value ([string]($NetworkAdapterConfigurationObject.IPAddress))
		}
	}

	#Récupére le système d'exploitation du DC actuellement requêté
	$WMIOperatingSystemObject = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ServerToGetConfiguration | Select-Object PSComputerName, Caption, BuildNumber

	#Ajoute à l'objet PowerShell précédemment crée, système d'exploitation du DC actuellement requêté
	$Line | Add-Member -MemberType NoteProperty -Name "OperatingSystem" -Value $WMIOperatingSystemObject.Caption
	
	#Récupére les CPU du DC actuellement requêté
    $WMIProcessorObjects = Get-WmiObject -Class Win32_Processor -ComputerName $ServerToGetConfiguration | Select-Object PSComputerName, Name, MaxClockSpeed, Manufacturer, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors

	#Boucle qui va additioner l'ensemble des CPU du DC requêté pour obtenir le nombre total de CPU
	$CPUNumbers = 0
	foreach ($WMIProcessorObject in  $WMIProcessorObjects)
	{
		$CPUNumbers += 1
	}

	#Ajoute à l'objet PowerShell précédemment crée, le nombre total de CPU du DC actuellement requêté
	$Line | Add-Member -MemberType NoteProperty -Name "CPU_total" -Value $CPUNumbers

	#Récupére l'espace disque du DC actuellement requêté
	$WMILogicalDiskObjects = Get-WMIObject -Class Win32_LogicalDisk -ComputerName $ServerToGetConfiguration | Select-Object PSComputerName, DeviceID, FileSystem, MaximumComponentLength, Size, FreeSpace

	#Boucle qui va parcourir chaque disque du DC requêté et ajoute à l'objet PowerShell précédemment crée, l'espace disque total et restant de C: et l'affiche en GB
	foreach ($WMILogicalDiskObject in $WMILogicalDiskObjects)
	{
		if ($WMILogicalDiskObject.DeviceID -eq "C:")
		{
			$Line | Add-Member -MemberType NoteProperty -Name "Disk_size(GB)" -Value ([math]::truncate(($WMILogicalDiskObject.Size)/1GB))
			$Line | Add-Member -MemberType NoteProperty -Name "Disk_free(GB)" -Value ([math]::truncate(($WMILogicalDiskObject.FreeSpace)/1GB))
		}
	}

	#Récupére les RAM du DC actuellement requêté
	$WMIPhysicalMemoryObjects = Get-WmiObject -Class Win32_PhysicalMemory -ComputerName $ServerToGetConfiguration | Select-Object PSComputerName, BankLabel, Capacity, Manufacturer

	#Boucle qui va additionner l'ensemble des RAM du DC requêté pour obtenir le nombre total de RAM
	$RAMnumbers = 0 
	foreach ($WMIPhysicalMemoryObject in $WMIPhysicalMemoryObjects)
	{
		$RAMnumbers += $WMIPhysicalMemoryObject.Capacity
	}
	
	#Ajoute à l'objet PowerShell précédemment crée, le nombre total de RAM du DC actuellement requêté
	$Line | Add-Member -MemberType NoteProperty -Name "Memory(MB)" -Value ([math]::truncate(($RAMnumbers)/1MB))

	#Ajoute à la variable globale qui contient l'ensemble des configuration des DC, l'objet PowerShell précédemment crée de l'itération courante
	$Array += $Line

	#Supprime les valeurs des variables propres au DC actuellement requêté
	Clear-Variable Line, WMIComputerSystemObject, WMINetworkAdapterConfigurationObjects, WMIOperatingSystemObject, CPUNumbers, WMILogicalDiskObjects, WMIPhysicalMemoryObject
}

#Affiche sous forme de liste l'ensemble des DC avec leurs configurations
$Array

<#Exemple d'affichage
HostName        : DC01.customer.intern
Domain          : customer.intern
IPAddress       : 192.168.1.50
OperatingSystem : Microsoft Windows Server 2019 Standard
CPU_total       : 4
Disk_size(GB)   : 50
Disk_free(GB)   : 30
Memory(MB)      : 16384

HostName        : DC02.technical.intern
Domain          : technical.intern
IPAddress       : 192.168.1.51
OperatingSystem : Microsoft Windows Server 2022 Standard
CPU_total       : 4
Disk_size(GB)   : 100
Disk_free(GB)   : 60
Memory(MB)      : 16384
#></pre>

 

Azure AD – Déployer le passwordless avec Microsoft Authenticator

Une présentation du passwordless a été faite dans ce post : Azure AD – Présentation du passwordless

Microsoft Authenticator est un logiciel pratique qui permet à la fois de faire du l’authentification multifacteur, mais également de faire du passwordless. Cet article explique comment préparer son environnement pour pouvoir se connecter en passwordless avec Microsoft Authenticator.

Les configurations ci-après sont réalisées avec un compte Global Administrator (GA).

Désactiver le MFA historique

La méthode moderne de faire du MFA est via l’accès conditionnel Azure, il est nécessaire de désactiver le MFA historique pour éviter d’avoir des conflits.

Se connecter successivement à portal.azure.com > Cliquer sur l’icône des 3 traits horizontaux > Azure Active Directory > Users >  Per-user MFA

Pour chaque utilisateur qui doit pouvoir faire du passwordless, il faut désactiver le MFA

 

Combiner les informations de sécurité SSPR et MFA

Les étapes sont décrites dans l’article : Azure – Combiner les informations de sécurité SSPR et MFA

 

(Optionnel) Activer le MFA via accès conditionnel Azure

Active le MFA n’est pas obligatoire pour l’authentification via Microsoft Authenticator mais, est fortement recommandé.

Pour créer un nouvel accès conditionnel Azure ou conditional access (CA), se connecter successivement à portal.azure.com > Cliquer sur l’icône des 3 traits horizontaux > Azure Active Directory > Security > Policies > New policy

 

Dans Users choisir les utilisateurs qui doivent faire du passwordless, attention de toujours garder quelques comptes de bris de glace qui ne sont pas concernés par la politique

Dans Cloud apps or actions, cocher All cloud apps

Dans Conditions, uniquement, s’il faut exclure certains utilisateurs en se basant sur des critères prédéfinis

Dans Enable policy choisir On

 

Dans Grant, cocher Grant access et Require multifactor authenticator puis cliquer sur Select

Enfin cliquer sur Create

Dorénavant, les utilisateurs configurés dans la CA devront s’authentifier en MFA. Lors de la première connexion, il leur sera demandé d’enregistrer des facteurs d’authentifications, Microsoft Authenticator étant plus que recommandé sans quoi l’authentification passwordless ne pourra pas se faire.

 

Identifier les domaines fédérés et planifier la migration de l’authentification vers Azure

Dans le cas où l’environnement disposerait d’une ferme ADFS, les domaines fédérés ne peuvent pas être utilisés pour faire du passwordless.

En fonction de comment est gérer l’authentification des utilisateurs OnPremises versus Azure, les actions à réaliser peuvent être plus ou moins chronophages.

Se référer à la documentation Microsoft : Migrate from federation to cloud authentication

Il est donc nécessaire de réaliser cette transition de l’authentification vers le cloud Azure pour pouvoir faire du passwordless.

 

Configurer l’authentification passwordless via Microsoft Authenticator

Se connecter successivement à portal.azure.com > Cliquer sur l’icône des 3 traits horizontaux > Azure Active Directory > Security > Authentication Methods > Microsoft Authenticator

Dans Enable and Target, activer Enable

Dans Include, choisir Select groups puis choisir le groupe qui contient les utilisateurs qu’il faut déployer passwordless, ici le groupe s’appelle Passwordless-test
Dans Authentication mode choisir Any, any inclut la possibilité de faire du passwordless

Cliquer sur Save

 

Cela conclut la configuration de l’authentification passwordless via Microsoft Authenticator

 

Configuration par l’utilisateur final la possibilité de faire de l’authentification passwordless avec Microsoft Authenticator

Chaque utilisateur souhaitant s’authentifier en passwordless doit activer l’option dans son application Microsoft Authenticator

Une fois sur le compte d’entreprise, cliquer sur Enable phone sign-in