Obtenez des liens de stratégie de groupe avec PowerShell
Je discutais récemment avec mon amie Gladys Kravitz à propos des rapports sur les stratégies de groupe. et la discussion m'a amené à dépoussiérer un ancien code que j'avais pour obtenir des liens de stratégie de groupe à l'aide de PowerShell. Le module GroupPolicy a une commande Set-GPLink, mais rien qui vous montre facilement quels GPO sont liés à votre site, domaine et unités d'organisation. Même si vous n’avez peut-être pas besoin d’un tel rapport, j’espère qu’il y a quelque chose dans mes techniques de script qui vous sera utile.
Commencez avec une commande principale
Comme pour tout projet PowerShell, vous souhaitez démarrer depuis la console. Pouvez-vous exécuter une commande de manière interactive qui vous donne l’essence de votre objectif ? Dans mon cas, il s'agit de l'applet de commande Get-GPInheritance du module GroupPolicy. Je compte sur vous pour lire de l’aide et des exemples. Voici comment cela me montre ce qui est lié au niveau du domaine.
En regardant le résultat, je vois une propriété qui contient probablement les informations que je recherche.
Cela semble prometteur. Je peux vérifier que cela fonctionne également avec les unités organisationnelles.
(Get-ADOrganizationalUnit -filter * | Get-GPInheritance).GpoLinks |
Select-Object -Property Target,DisplayName,Enabled,Enforced,Order |
Format-TableCela a l'air plutôt bien et est proche de ce que j'espérais accomplir.
Interrogation des liens de sites
Et les sites ? En supposant que je ne connais pas les noms distinctifs de tous mes sites, je souhaite une solution programmatique. Je peux utiliser le module ActiveDirectory pour découvrir des sites.
$getADO = @{
LDAPFilter = "(Objectclass=site)"
properties = "Name"
SearchBase = (Get-ADRootDSE).ConfigurationNamingContext
}
$sites = Get-ADObject @getADOJe n'ai qu'un seul site mais malheureusement Get-GPInheritance ne peut pas utiliser de sites. Il existe des moyens de parcourir Active Directory et d'analyser les liens de sites, mais j'ai décidé d'opter pour une méthode plus simple et de m'appuyer sur l'objet COM hérité, GPMGMT.GPM. Voici comment.
Tout d’abord, j’ai besoin d’une référence d’objet. Je vais également obtenir toutes les constantes dont l'objet a besoin.
$gpm = New-Object -ComObject "GPMGMT.GPM"
$gpmConstants = $gpm.GetConstants()Je vais avoir besoin de références au domaine et à la forêt.
$gpmdomain = $gpm.GetDomain("company.pri", "", $gpmConstants.UseAnyDC)
$SiteContainer = $gpm.GetSitesContainer("company.pri", "company.pri", $null, $gpmConstants.UseAnyDC)Mon domaine et ma forêt s'appellent Company.pri.
L'objet $SiteContainer possède une méthode GetSite(), mais il nécessite le nom d'un site. Mais je l'ai eu plus tôt.
Ce nouvel objet possède une méthode appelée GetGPOLinks().
C'est plutôt bien. Il ne me manque que le nom du GPO.
$site.GetGPOLinks() | Select GPOId,@{Name="DisplayName";Expression = {$gpmdomain.GetGPO($_.gpoid).Displayname}},Enabled,Enforced,SOMLinkOrderPuisque j'ai le GPOID, je peux obtenir le GPO. Ce code utilise l'objet COM. J'aurais pu utiliser l'applet de commande, mais j'aurais besoin d'analyser les caractères {}.
$site.GetGPOLinks() | Select GPOId,@{Name="DisplayName";Expression = {(Get-GPO -id ($_.gpoid.replace("{|",""))).Displayname}},Enabled,Enforced,SOMLinkOrderAvec ces éléments en place, je peux créer un outil PowerShell autour de ces extraits de code.
Obtenir-GPLink
J'ai écrit une fonction appelée Get-GPLink. Étant donné que j'utilise les applets de commande GroupPolicy et ActiveDirectory et qu'elles prennent en charge la spécification d'un serveur et/ou d'un domaine, je souhaitais également prendre en charge cela. Au départ, j'ai essayé d'utiliser le splatting et d'attribuer dynamiquement des valeurs de paramètres. Mais cela est vite devenu compliqué. Au lieu de cela, je définis une version de portée script des valeurs $PSDefaultParameter.
if ($Server) {
$script:PSDefaultParameterValues["Get-AD*:Server"] = $server
$script:PSDefaultParameterValues["Get-GP*:Server"] = $Server
}
if ($domain) {
$script:PSDefaultParameterValues["Get-AD*:Domain"] = $domain
$script:PSDefaultParameterValues["Get-ADDomain:Identity"] = $domain
$script:PSDefaultParameterValues["Get-GP*:Domain"] = $domain
}Désormais, je n'ai plus à m'en soucier. Si j'exécute Get-ADDomain et que le paramètre Server est utilisé à partir de ma fonction, la cmdlet Get-ADDomain l'utilisera automatiquement.
La fonction parcourt et interroge le domaine et les unités organisationnelles à l'aide du module GroupPolicy. Les liens au niveau du site sont récupérés à l'aide de l'objet COM. Techniquement, j'aurais pu utiliser l'objet COM pour tout.
La fonction collecte tous les liens ce qui me permet de proposer un filtrage par nom de GPO ou d'afficher uniquement les liens activés ou désactivés.
Ce n'est pas si mal. Mais j'en veux toujours plus. Par exemple, je ne peux pas diriger la sortie de ma fonction vers Get-GPO. Bien que je puisse le faire, si je fais un ajustement mineur.
Mais je ne veux pas faire ça tout le temps. J'aimerais également une sortie par défaut d'une table. Je sais que je peux le faire avec un fichier format.ps1xml personnalisé et ma fidèle commande New-PSFormatXML. Mais pour ce faire, j'ai besoin d'un nom de type unique et personnalisé.
Dans mon code, je peux insérer un nouveau nom de type puisque je ne crée pas d'objet personnalisé à partir de zéro comme je le fais habituellement.
$results.GetEnumerator().ForEach( { $_.psobject.TypeNames.insert(0, "myGPOLink") })Une fois que j'ai un nouveau nom de type, je peux définir des extensions de type.
Update-TypeData -MemberType AliasProperty -MemberName GUID -Value GPOId -TypeName myGPOLink -Force
Update-TypeData -MemberType AliasProperty -MemberName Name -Value DisplayName -TypeName myGPOLink -Force
Update-TypeData -MemberType AliasProperty -MemberName GPO -Value DisplayName -TypeName myGPOLink -Force
Update-TypeData -MemberType AliasProperty -MemberName Link -Value Target -TypeName myGPOLink -Force
Update-TypeData -MemberType AliasProperty -MemberName Domain -Value GpoDomainName -TypeName myGPOLink -Force
Update-TypeData -MemberType ScriptProperty -MemberName TargetType -Value {
switch -regex ($this.target) {
"^((ou)|(OU)=)" { "OU" }
"^((dc)|(DC)=)" { "Domain" }
"^((cn)|(CN)=)" { "Site" }
Default { "Unknown"}
}
} -TypeName myGPOLink -ForceCela rend désormais beaucoup plus facile le travail avec mes liens de stratégie de groupe.
Format personnalisé
Bien sûr, puisque j'ai un type personnalisé, je peux créer un fichier format.ps1xml et devenir vraiment sophistiqué. Je peux maintenant créer une vue tabulaire par défaut des résultats. Et comme je suis toujours à la recherche de moyens d'ajouter de la couleur à PowerShell, j'ai décidé de mettre en évidence les liens désactivés et ceux qui sont appliqués à l'aide de séquences d'échappement ANSI.
<TableColumnItem>
<ScriptBlock>
<!-- use ANSI formatting if using the console host-->
if ($host.name -eq 'ConsoleHost') {
if ($_.Enabled) {
$_.Enabled
}
else {
"$([char]0x1b)[1;91m$($_.enabled)$([char]0x1b)[0m"
}
}
else {
$_.Enabled
}
</ScriptBlock>
</TableColumnItem>Le bloc de script utilise ANSI uniquement si PowerShell détecte un hôte de console. J'utilise une séquence d'échappement qui fonctionnera à la fois dans Windows PowerShell et PowerShell 7.x
Le fichier de format inclut également une autre vue.
Résumé
J'utilise le flux de travail que je viens de décrire dans une grande partie de mon travail. Je commence par les commandes principales, je construis un wrapper robuste autour des commandes sous la forme d'une fonction, j'écris un objet dans le pipeline et, si nécessaire, je le personnalise avec des extensions de type et de format. Le résultat final devrait être un outil PowerShell facile à utiliser et utile qui exécute le pipeline comme n'importe quelle autre commande PowerShell.
Si vous voulez voir comment tout cela fonctionne, j'ai publié le code en tant qu'essentiel sur GitHub. Si vous souhaitez l'essayer, vous devrez enregistrer les deux fichiers dans le même répertoire. Portez une attention particulière au nom du fichier de format, car il est chargé lorsque vous pointez le fichier de fonction.
Si vous avez des questions sur ce que j'ai fait ou pourquoi, n'hésitez pas à laisser un commentaire.













