Dans cet article, je vous propose un double sujet, les profils Powershell, et un script avancé de déverrouillage de comptes utilisateurs.

 Les profils, késako?

Un profil est un fichier .ps1 qui porte un nom bien précis dans un répertoire spécifique, et qui est chargé automatiquement par Powershell et Powershell ISE lorsque vous l'ouvrez. (Chacun de ces outils possédant son fichier de profil bien distinct). Une variable est d'ailleurs crée lorsque vous lancez Powershell, nommée $profile, qui vous indique un fichier qui (probablement) n'existe pas (encore!). En allant donc créer (le cas écheant) puis modifier ce fichier vous pourrez ainsi personnaliser votre console PS à son démarrage, en précisant une couleur de fond, une police et couleur d'écriture, mais également changer l'invité afin de préciser par exemple si vous êtes sur une console avec des droits élevés ou non. Les possibilités sont très grandes, et, pour peu que vous n'ayez pas des milliers de lignes d'un code mal conçu, les répercussions sur le temps de démarrage de Powershell sont insignifiantes.

Si par exemple vous avez créé une fonction bien pratique et que vous voulez vous en servir, vous n'allez pas appeler à chaque fois le script pour intégrer la fonction dans Powershell, puis lancer votre fonction.

Créez donc votre fichier de profil, puis testez à votre guise, ajoutez des alias, des fonctions que vous appréciez... et si vous n'avez pas de fonction que vous appréciez, pourquoi ne pas ajouter celle que je vais vous proposer à la suite de cet article? :)

Petite présentation tout d'abord de ce que fait mon script...

Ce script est capable de :

  • Déverrouiller un ou plusieurs comptes utilisateurs
  • Définir une date de verrouillage du (des) compte(s)
  • Forcer un nouveau mot de passe pour le(s) compte(s) tout en s'assurant qu'il respecte la complexité de votre domaine
  • Obliger le changement de mot de passe à la prochaine ouverture de session

Et normalement, vous devez vous dire "Ha? C'est tout? Mais Nicolas, tout ça est déja possible en passant par Powershell! Pourquoi refaire ce qui existe?" et vous auriez raison de me poser la question!

La réponse est simple : Pour faire ce genre d'actions, il est nécessaire d'utiliser 3 commandes, là ou mon script n'en demande qu'une.

  1. Unlock-Adaccount : Pour le déverrouillage
  2. Set-Aduser : pour la date de verrouillage et l'obligation de changer de mot de passe
  3. Set-AdaccountPassword : Pour le changement de mot de passe.

Et si vous devez déverrouiller plusieurs comptes, les rendre valides pour 1 mois avec un mot de passe générique et une obligation de changement à la prochaine ouverture de session, (comme c'est bien souvent le cas dans le cadre d'embauches intérimaires) ... Vous commencez à voir ou je veux en venir.

En utilisant ce script (nommé Reactivate-ADUSER), vous pourrez très aisément faire ces modifications sans avoir à vous connecter à l'AD, chercher le compte, faire le déverrouillage, choisir un nouveau mot de passe, Modifer la date de ... de... ZzZzzZz...

Allez, un simple exemple pour prouver : en 10 secondes, j'ai réactivé un utilisateur, nommé "mfm01" j'ai mis une date de désactivation dans 5 jours, forcé un mot de passe, que le script ne me signale que "lle" n'était pas accepté par l'AD et de choisir un nouveau mot de passe.

Et comme mon script accepte ce qui vient du pipe, il est tout à fait possible d'effectuer cette manipulation pour un grand nombre de comptes en un claquement de doigts. Si l'on reprend mon exemple d'intérmiaires qui arrivent tous en août et qui sont valides un mois, et en supposant que les comptes à reactiver commencent tous par "Interimaires_Atelier", il suffit de taper :

Get-Aduser -filter 'samaccountname -like "Interimaires_Atelier*" ' | Reactivate-ADUSER -NewExpirationDate 1m -NewPassword "A_Changer" -ChangePasswordAtLogon

Et vous aurez en fin de script, un récapitulatif de toutes les actions effectuées.

Mieux encore, mon script est compatible avec -WhatIf (pour ne faire aucune action, juste simuler ce qu'il se passe) et également -Verbose (Pour obtenir des informations détaillées de ce qu'il se passe).

Qui plus est, pour éviter tout problème ultérieur, j'ai intégré une aide à cette fonction. Une fois ajoutée dans Powershell, vous n'avez qu'a faire Get-Help Reactivate-AdUser -Full pour avoir une description de ce que vous pouvez faire avec cette commande.

Mais plus de des grands mots, pourquoi ne pas tout simplement avoir le script en question?

Le voici!


Le Script

Vous pouvez télécharger le script sur mon espace git personnel.

Function Reactivate-ADUSER
{

<#

.SYNOPSIS
Script de réactivation de compte d'utilisateur.

.DESCRIPTION
Ce script permet de réactiver des comptes utilisateurs désactivés, de modifier leur mot de passe, leur date de désactivation en une seule ligne de commande.

.EXAMPLE
C:\PS> Reactivate-Aduser -SamAccountName NicolasLang -Unlock
Réactive l'utilisateur NicolasLang

.EXAMPLE
C:\PS> Reactivate-ADUSER -SamAccountName NicolasLang -NewExpirationDate 10d
Met une date d'expiration du compte dans 10 jours

.EXAMPLE
C:\PS> Get-Aduser -filter 'samaccountname -like "Interimaire_*"' | Reactivate-ADUSER -NewExpirationDate 1m -NewPassword "TempPASS" -ChangePasswordAtLogon
Ajoute un mois à la date d'expiration, créé un nouveau mot de passe et force son changement à la prochaine ouverture de session pour tous les comptes commençant par "Interimaire_"

.INPUTS
Des objets du type [Microsoft.ActiveDirectory.Management.ADUser], généralement issus d'une commande Get-Aduser

.OUTPUTS
Un objet Arraylist qu'il est possible de renvoyer vers Export-CSV pour une lecture et un stockage aisé

.PARAMETER SamAccountName
Le nom du compte Windows à réactiver. Peut provenir du pipe.

.PARAMETER Unlock
Réactive le compte (décoche la case "vérrouillé" dans l'AD)

.PARAMETER NewExpirationDate
Permet de définir une date d'expiration par rapport à aujourd'hui. Le format est le suivant : (durée)(valeur)
La valeur est soit : d (pour Days : Jours), m (pour Months : Mois), y (pour Year : Année)
Ainsi, une valeur de 2d correspond à 2 jours. Le compte sera desactivé après demain à 23h59:59.

.PARAMETER NewPassword
Le nouveau mot de passe choisi pour le / les utilisateur(s) à réactiver

.PARAMETER ChangePasswordAtLogon
Précise si le mot de passe est à changer à la prochaine ouverture de session.

.LINK
http://nicolaslang.blogspot.com/2016/08/gerer-aisement-les-deverouillages-de.html

.NOTES
Crée par Nicolas Lang

#>

    [CmdletBinding(DefaultParametersetName="unlock",
        ConfirmImpact="Medium",
        SupportsShouldProcess=$true,
        helpURI="http://nicolaslang.blogspot.com/",
        SupportsPaging=$false,
        PositionalBinding=$false
        )]
param (
       [Parameter (Mandatory=$true, Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
       [Alias("Username","Compte")]
       [ValidateNotNullOrEmpty()]
       [string]$SamAccountName,
       [Parameter (Mandatory=$false)]
       [switch]$Unlock,
       [Parameter (Mandatory=$false)]
       [ValidatePattern('^(?\d+)(?[d,m,y])$')]
       [string]$NewExpirationDate,
       [Parameter (Mandatory=$false)]
       [string]$NewPassword,
       [Parameter (Mandatory=$false)]
       [switch]$ChangePasswordAtLogon
   )

    Begin
    {

        [switch]$whatif = $false
        $whatif = $PSCmdlet.MyInvocation.BoundParameters["whatif"]
        $verbose = $PSCmdlet.MyInvocation.BoundParameters["Verbose"]
        $verbosestatus = $VerbosePreference
        if ($verbose)
        {
            $VerbosePreference = "Continue"
        }

        $array = New-Object System.Collections.ArrayList

    }

    Process
    {
        #On vérifie l'entrée du pipe, et on s'assure que le format de l'objet est correct

        if ($_)
        {
            Write-Verbose "Vérification des informations venant du pipe"
            if ($_ -isnot [Microsoft.ActiveDirectory.Management.ADUser])
            {
                return $(throw("Le type de données est incorrect"))
            }
            if ($verbose)
            {
                Write-Verbose "Informations du pipe valides"
            }
            $compte = $_

        }
        else
        {
            Write-Verbose "Recherche du compte $SamAccountName dans l'AD"

            try
            {
                $compte = get-aduser $SamAccountName -Properties passwordlastset,AccountExpirationDate,Lockedout -ErrorAction Stop
            }
            #Si pas de commande, alors peut etre pas de module?
            catch [System.Management.Automation.CommandNotFoundException]
            {

                Write-Verbose "Erreur sur la commande, recherche du module AD dans la liste des modules disponibles"

                #On vérifie s'il est disponible, et on tente de le charger
                if ((Get-Module -listavailable ActiveDirectory) -eq $null)
                {
                    Return "Module ActiveDirectory manquant. Installez les outils RSAT pour utiliser cette fonction"
                }
                else
                {
                    try
                    {

                        Write-Verbose "Module trouvé, intégration dans PowerShell"

                        Import-Module ActiveDirectory -ErrorAction Stop
                    }
                    catch
                    {
                        return $(throw("Impossible de charger le module ActiveDirectory! Have you tried turn it off and on again, Moss?"))
                    }
                    try
                    {

                        Write-Verbose "Intégration OK : Nouvelle recherche du compte."

                        $compte = Get-ADUser $SamAccountName -ErrorAction Stop
                    }
                    catch
                    {
                        Return $(throw("Une nouvelle erreur s'est produite : $($error[0].Exception)"))
                    }
                }
            }
            catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]
            {

                return $(throw("Le compte $Samaccountname est introuvable"))
            }
            catch
            {
                return "Une erreur inconnue s'est produite : $($error[0])"
            }
        }
        $user = $compte

        $object = [PSCUSTOMOBJECT]@{
        "Compte" = $user.samaccountname
        "Nom" = $user.name
        }

        #region EXPIRATION
            if ($NewExpirationDate)
            {
                switch ($user.accountexpirationdate)
                {
                    $null { $accountexpirationdate = "?"}

                    default { $accountexpirationdate = $user.accountexpirationdate}
                }
                $object | Add-Member -MemberType NoteProperty -Name "Accountexpirationdate" -Value $accountexpirationdate

                Write-Verbose "Calcul de la nouvelle date d'expiration."

                $NewExpirationDate -match '^(?\d+)(?[d,m,y])$' | Out-Null
                switch ($Matches.duree)
                {
                    "d" {$expirationdate = (Get-Date).date.AddDays(1).AddDays($Matches.valeur)}
                    "m" {$expirationdate = (Get-Date).date.AddDays(1).AddMonths($Matches.valeur)}
                    "y" {$expirationdate = (Get-Date).date.AddDays(1).AddYears($Matches.valeur)}
                }

                Write-Verbose "Nouvelle date : $expirationdate"
                Write-Verbose "Application de la nouvelle date au compte $($user.samaccountname)"

                try
                {
                    Set-ADUser $user -AccountExpirationDate $expirationdate -ErrorAction Stop -WhatIf:([bool]$whatif)
                }
                catch
                {
                    return $(throw("Une erreur s'est produite durant la modification de la date d'expiration sur $($user.samaccountname)"))
                }
                try
                {
                    $accountexpirationdate = (Get-aduser $user -Properties Accountexpirationdate -ErrorAction Stop).AccountexpirationDate 
                }
                catch
                {
                    throw("Impossible de récuperer les informations d'expiration du compte $user")
                }
                finally
                {
                    $object | Add-Member -Name "AccountExpirationDate_apres" -MemberType NoteProperty -Value $accountexpirationdate
                }

            }
            #endregion EXPIRATION

        #region CHANGEPASSWORDATLOGON
            if ($ChangePasswordAtLogon.IsPresent)
            {

                switch ($user.passwordexpired)
                {
                    $true { $passwordchangevalue = "Oui"}
                    $false { $passwordchangevalue = "Non"}
                    default { $passwordchangevalue = "?"}
                }

                $object | Add-Member "ChangePasswordAtLogon" -MemberType NoteProperty -Value $passwordchangevalue 

                Write-Verbose "Application du changement de mot de passe forcé"

                try
                {
                    set-aduser $user -ChangePasswordAtLogon $true  -WhatIf:([bool]$whatif)
                }
                catch
                {
                    return $(throw("Erreur lors de l'application du changement de mdp à la prochaine ouverture de session. $($error[0])"))
                }
                finally
                {

                    Write-Verbose "Récupération du nouveau status de changement de mot de passe"

                    $passwordchangevalue = $null
                    switch ((get-aduser $user -Properties passwordexpired).passwordexpired)
                    {
                        $true { $passwordchangevalue = "Oui"}
                        $false { $passwordchangevalue = "Non"}
                        default { $passwordchangevalue = "?"}
                    }
                    $object | Add-Member "ChangePasswordAtLogon_Apres" -MemberType NoteProperty -Value $passwordchangevalue
                }

            }
            #endregion CHANGEPASSWORDATLOGON

        #region PASSWORD
        if ($NewPassword)
        {
            $invalidpassword = $true
            Write-Verbose "Modification du mot de passe pour $($user.samaccountname)"
            While ($invalidpassword)
            {
                try
                {
                    Set-ADAccountPassword $user -Reset -NewPassword (ConvertTo-SecureString $NewPassword -AsPlainText -Force) -ErrorAction Stop -WhatIf:([bool]$whatif)
                    $invalidpassword = $false
                }
                catch [Microsoft.ActiveDirectory.Management.ADPasswordComplexityException]
                {
                    $invalidpassword  = $true

                    $NewPassword = Read-Host "Le mot de passe ne respecte pas  le niveau de complexité, de longueur, ou d'historique.`nEntrez un nouveau mot de passe : "
                }
                catch
                {
                    return $(throw("Erreur lors de la mise à jour du mot de passe sur $($user.samaccountname).`n$($error[0])"))
                }

                finally
                {
                    $object | Add-Member -MemberType NoteProperty -Name "Password_apres" -Value $NewPassword -Force
                }
            }
        }

            #endregion PASSWORD

        #region UNLOCK
        if ($Unlock.IsPresent)
        {

            switch ($user.lockedout)
            {
                $true { $islockedout = "Oui" }
                $false { $islockedout = "Non" }
                default { $islockedout = "???" }
            }
            $object | Add-Member -Name "Verrouillage" -MemberType NoteProperty -Value $islockedout

            Write-Verbose "Déverouillage du compte utilisateur $($user.samaccountname)"
            try
            {
                Unlock-ADAccount -Identity $user -WhatIf:([bool]$whatif)
            }
            catch
            {
                return $(throw("Une erreur est survenue lors du déverouillage du compte $($user.samaccountname)`n$($error[0])"))
            }
            switch ((get-aduser $user -Properties lockedout).lockedout)
            {
                $true {$islockedout = "Oui"}
                $false {$islockedout = "Non"}
                default {$islockedout = "???"}
            }
            $object | Add-Member -Name "Verrouillage_apres" -MemberType NoteProperty -Value $islockedout

        }

        #endregion UNLOCK

    $array.Add($object) | Out-Null
    }
    End
    {

        $VerbosePreference = $verbosestatus
        return $array
    }
}

Ajouter un commentaire

Article précédent Article suivant