PI Services

Le blog des collaborateurs de PI Services

[Powershell] - Fonction pour obtenir les membres des groupes Active Directory supérieur à 5000 objets.

Problème :

Il existe une limitation à la commande "Get-ADGroupMember" et cette dernière est de 5000, ce qui veut dire que si le groupe détient plus de 5000 membres vous obtiendrez un joli message d'erreur du type :

Get-ADGroupMember : The size limit for this resquest was exceeded

Solution :

Voici une fonction Powershell qui vous permettra de récupérer l'intégralité des membres d'un groupe même s'il y en a 10 000 dedans.

Vous pourrez choisir de retourner :

  • Les utilisateurs
  • Les groupes
  • Le tout
Function Get-AllMembers {
     <#
    .SYNOPSIS
    Return a list of members for a group.

    .DESCRIPTION
    Get-AllMembers is a function that returns a list of members for a specific group.
    
    .PARAMETER Name
    The name of the group you want to get the member list.

    .EXAMPLE
    Get-AllMembers "Domain Admins", "DNS Admins"

    .INPUTS
    String

    .OUTPUTS
        PSCustomObject

    .NOTES
        Author:  ADELAIDE Mathieu
    #>
    PARAM (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [STRING]$Name,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [ValidateSet("UsersOnly","GroupsOnly","All")]
        [STRING]$Return
        )
    Process {
        $Name | Foreach {
            $GroupName = $_
            $ArrayUsers = @()
            $ArrayGroups = @()
            $ArrayAll = @()
            Try {
                $DistinguishedName = Get-ADGroup -Identity $GroupName -ErrorAction Stop | select -ExpandProperty DistinguishedName
                # Searching all Users who's member of current Group
                Try {
                    $AllUsersMembers = Get-ADUser -LDAPFilter "(&(objectCategory=user)(memberOf=$DistinguishedName))" -ErrorAction Stop
                    $AllUsersMembers | foreach {
                        $ArrayUsers += New-Object psobject -Property @{
                            GroupName = $GroupName
                            DistinguishedName = $_.DistinguishedName
                            Enabled = $_.Enabled
                            GivenName = $_.GivenName
                            Name = $_.Name
                            ObjectClass = $_.ObjectClass
                            ObjectGUID = $_.ObjectGUID
                            SamAccountName = $_.SamAccountName
                            SID = $_.SID
                            Surname = $_.Surname
                            UserPrincipalName = $_.UserPrincipalName
                            }

                        # Collect All
                        $ArrayAll += New-Object psobject -Property @{
                            GroupName = $GroupName
                            DistinguishedName = $_.DistinguishedName
                            Enabled = $_.Enabled
                            GivenName = $_.GivenName
                            Name = $_.Name
                            ObjectClass = $_.ObjectClass
                            ObjectGUID = $_.ObjectGUID
                            SamAccountName = $_.SamAccountName
                            SID = $_.SID
                            Surname = $_.Surname
                            UserPrincipalName = $_.UserPrincipalName
                            }
                        }
                    }
                Catch {
                    Write-Warning -Message "Unable to find all users member of $Name"
                    }
                # Searching all Groups who's member of current Group
                Try {
                    $AllGroupsMembers = Get-ADGroup -LDAPFilter "(&(objectCategory=group)(memberOf=$DistinguishedName))" -ErrorAction Stop
                    $AllGroupsMembers | foreach {
                        $ArrayGroups += New-Object psobject -Property @{
                            GroupName = $GroupName
                            DistinguishedName = $_.DistinguishedName
                            GroupCategory = $_.GroupCategory
                            GroupScope = $_.GroupScope
                            Name = $_.Name
                            ObjectClass = $_.ObjectClass
                            ObjectGUID = $_.ObjectGUID
                            SamAccountName = $_.SamAccountName
                            SID = $_.SID
                            }

                        # Collect All
                        $ArrayAll += New-Object psobject -Property @{
                            GroupName = $GroupName
                            DistinguishedName = $_.DistinguishedName
                            Enabled = $_.Enabled
                            GivenName = $_.GivenName
                            Name = $_.Name
                            ObjectClass = $_.ObjectClass
                            ObjectGUID = $_.ObjectGUID
                            SamAccountName = $_.SamAccountName
                            SID = $_.SID
                            Surname = $_.Surname
                            UserPrincipalName = $_.UserPrincipalName
                            }
                        }
                    }
                Catch {
                    # Return an error message if member not found.
                    Write-Warning -Message "Unable to find all groups member of $Name"
                    }
                }
            Catch {
                # Return an error message if Group was not found.
                Write-Warning -Message "Unable to find $Name"
                }
            
            Switch ($Return) {
                "UsersOnly" {Return $ArrayUsers}
                "GroupsOnly" {Return $ArrayGroups}
                "All" {Return $ArrayAll}
                }

            # Release
            $GroupName = $null
            $DistinguishedName = $null
            $AllUsersMembers = $null
            $AllGroupsMembers = $null
            }
        }
    }

O365 : Réaliser un hard match (Update)

Dans un précédent article j'expliquais comment réaliser un hard match (https://blog.piservices.fr/post/2021/04/01/o365-realiser-un-hard-match).

J'ai récemment eu à me servir de nouveau des cmdlets et ces dernières ne fonctionnaient pas correctement, il est possible de le faire via API Graph, mais les commande Azure AD permettent encore de le faire si besoin.

 

# Get GUID for User
$User = Get-ADUser jdupont | select ObjectGUID,UserPrincipalName
$Upn = $User.UserPrincipalName
$Guid = $User.ObjectGUID.Guid
 
# Convert GUID to ImmutableID
$ImmutableId = [System.Convert]::ToBase64String(([GUID]($User.ObjectGUID)).tobytearray())
 
# Connect Azure AD
Connect-AzureAD

# Retrieve my user
$User = Get-AzureADUser -SearchString "jdupont @mydomain.com"

# Set ImmutableID to user
Set-AzureADUser -ObjectId $User.ObjectID -ImmutableId $ImmutableId

 

Active Directory : Lister tous les comptes dont le mot ne passe n'expire jamais

Dans toute annuaire Active Directory il existe une mauvaise pratique, celle dont je voudrais vous parler aujourd'hui est la non expiration des mots de passe pour un / des comptes du domaine. 

Pourquoi ?

Il existe plusieurs arguments au fait qu'un mot de passe qui n'expire jamais est une mauvaise pratique, je mettrais en avant ici les deux qui me posent le plus problème:

  • Tout d'abord un mot de passe qui n'expire jamais a plus de chance d'être "découvert" dans des attaques de type brute force; si on se concentre sur ces comptes en particulier, le fait qu'il n'y ai pas de rotation de mot de passe, laisse une plus grande période de temps à l'attaquant pour le découvrir.
  • Si le compte est compromis, l'attaquant a par définition un accès "constant" au SI, puisque tant que la rotation du mot de passe n'aura pas lieu, ce dernier conservera son accès.

Que faire ?

Bien qu'il ne soit pas une bonne pratique d'autoriser la non expiration des mots de passe, je les croise tous le jours dans tout Active Directory, avec toujours "une bonne raison" de l'avoir fait.

En revanche, même s'il n'est pas possible de s'en séparer, il est tout de même bon de mettre en oeuvre quelques bonne pratiques lorsque l'on est dans cette situation :

  • Lister les comptes dont le mot de passe n'expire jamais.
  • Documenter la cause de cette configuration.
  • Documenter leur emploi (raison d'utilisation, application dans lesquelles ils sont utilisés, machines sur lesquelles ils sont utilisés).
  • Documenter la date du dernier changement de mot de passe (même si elle peut être retrouvée dans l'AD).
  • Documenter une procédure de changement de mot de passe (documentation applicative, processus de dépendance...).
  • Indiquer une personne / équipe en mesure de pouvoir réaliser le changement de mot de passe (il se peut qu'il y ai des développeurs, prestataires externes, éditeurs qui se servent de ce mot de passe). 
  • Réaliser une rotation du mot de passe manuellement.

Lister les comptes avec Powershell.

# Variables
$RootFolder = "C:\Temp"
$Workefolder = "$RootFolder\NeverExpires"
$LogFolder = "$RootFolder\Logs"
$LogFile = "$LogFolder\NeverExpires.txt"
$AllFolders = $RootFolder, $Workefolder, $LogFolder

# Check and create if needed
foreach ($Folder in $AllFolders) {
    If (!(Test-Path $Folder)) {
        Try {
            New-Item $Folder -ItemType Directory -ErrorAction Stop
            }
        Catch {
            Write-Warning $($_)
            }
        }
    }

# Import module
Try {
    Import-Module ActiveDirectory -ErrorAction Stop
    }
Catch {
    Write-Output "Failed to import module ActiveDirectory" | Add-Content $LogFile
    Exit
    }

# Get Active Directory users that have a password that never expires
Try {
    $AllEnabledUsers = Get-ADUser -Filter {Enabled -eq $true} -Properties PasswordNeverExpires -ErrorAction Stop
    $PasswordNeverExpires = $AllEnabledUsers.Where({$_.PasswordNeverExpires -ne $false})
    }
Catch {
    Write-Output "Failed to collect users" | Add-Content $LogFile
    }

# Export result
$PasswordNeverExpires | Export-Csv "$Workefolder\PasswordNeverExpires.csv" -Delimiter ";" -Encoding UTF8

 

Powershell : Change the owner of my GPO

Pour faire suite au précédent articles Powershell : Who's the owner of my AD objects et Powershell : Change the owner of my AD objects voici comment trouver les GPO dont le propriétaire n'est pas "Domain Admins" et les modifier.

# Get Domain
$Domain = Get-ADDomain | select -ExpandProperty NetBIOSName

# Get all GPO
$AllGPO = Get-GPO -All
$AllADGPO = Get-ADObject -Filter {(ObjectClass -eq "groupPolicyContainer")} -Properties displayName

# Filter GPO that are not owned by Domain Admins
$NoGood = $AllGPO.Where({$_.owner -ne "$Domain\Domain Admins"})

# Change owner of all invalid GPO
$NoGood | select -First 1 | foreach {
    $DisplayName = $_.DisplayName
    $Id = $_.ID
    $Guid = $Id.Guid

    $CurrentGpo = $AllADGPO.Where({$_.DisplayName -eq $DisplayName})
    Write-Host $CurrentGpo
    Write-Host $DisplayName -ForegroundColor Magenta
    
    $adsiTarget = [adsi]"LDAP://$($CurrentGpo.DistinguishedName)"
    $idRef = New-Object System.Security.Principal.NTAccount("$Domain", "Domain Admins")
    $adsiTarget.PSBase.ObjectSecurity.SetOwner($idRef)
    $adsiTarget.PSBase.CommitChanges()
    
    
    $DisplayName = $null
    $Id = $null
    }

 

 

[Powershell] - Gérer la modération des listes de distribution dans un environnement en Split Model Permission

Sur le même principe que l'article précédent "autoriser une liste d'utilisateur à envoyer des email à une liste de distribution" voici la fonction pour gérer la "Modération"

Powershell

Function Allow-ToModeratedBy {
    param (
    [Parameter(Mandatory)][String]$GroupName,
    [Parameter(Mandatory)][String[]]$SamAccountName
    )

    Try {
        Get-ADGroup $GroupName -ErrorAction Stop
        foreach ($Sam in $SamAccountName) {
            Try {
                $CurrentUser = Get-ADUser $Sam -ErrorAction Stop
                Try {
                    Set-ADGroup -Identity $GroupName -Add @{msExchModeratedByLink=@($CurrentUser.DistinguishedName)} -ErrorAction Stop
                }
                Catch {
                    Write-Warning $($_)
                    }
            }
            Catch {
                Write-Warning $($_)
            }
        }
    }
    Catch {
        Write-Host "Group Not found sorry" -ForegroundColor Yellow
    }
}

 

[Powershell] - Autoriser l'envoie sur une liste de distribution dans un environnement en Split Model Permission

Contexte :

Dans le cas ou vous implémentez l'Active Directory Split Model Permission, vous réduisez les permissions de l'Exchange sur L'AD et par conséquent une partie des commandes disponible depuis l'Exchange.

Problème :

La modification doit être faite sur les fiches des objets AD et il faut connaitre l'attribut à modifier.

Par exemple pour autoriser un ou des utilisateur(s) à écrire à une liste de distribution, il faut pour chaque utilisateur ajouter son "DistinguishedName" dans l'attribut "authOrig" de la liste en question.

Solution :

Si vous ne souhaitez pas éditez les fiches AD, aller dans l'onglet éditeur d'attributs et éditer / modifier l'attribut (déjà 4 clics avec la validation) vous pouvez utiliser Powershell avec la ligne de commande suivante :

Set-ADGroup -Identity "SamAccountName_Du_Groupe" -Add @{authOrig=@("DistinguishedName_Du_User")}

Mais si vous devez le faire pour plusieurs utilisateurs et ne pas traiter cela unitairement, il vous est possible d'utiliser la fonction suivante en passant plusieurs utilisateurs pour l'attribut "SamAccountName" séparés par une virgule :

Function Allow-ToSendTo {
    param (
    [Parameter(Mandatory)][String]$GroupName,
    [Parameter(Mandatory)][String[]]$SamAccountName
    )

    Try {
        Get-ADGroup $GroupName -ErrorAction Stop
        foreach ($Sam in $SamAccountName) {
            Try {
                $CurrentUser = Get-ADUser $Sam -ErrorAction Stop
                Try {
                    Set-ADGroup -Identity $GroupName -Add @{authOrig=@($CurrentUser.DistinguishedName)} -ErrorAction Stop
                }
                Catch {
                    Write-Warning $($_)
                    }
            }
            Catch {
                Write-Warning $($_)
            }
        }
    }
    Catch {
        Write-Host "Group Not found sorry" -ForegroundColor Yellow
    }
}

 

Powershell : WSUS Report Servers States

Voici un script qui vous permettra de réaliser un état des lieu sur les mises à jour de sécurité nécessaires pour l'ensemble de vos serveurs rattachés à un WSUS.

Attention : Prenez soins de modifier les variables suivantes :

  1. Sur la ligne 12 vous pouvez remplacer le "-30" par un valeur plus élevé si vous souhaitez un delta plus large (le -30 correspondant à M - 1, et ainsi connaitre le nombre de mise à jours manquante à J-30 jours)
  2. Sur la ligne 14 $Alias (doit correspondra à l'alias renseigné dans votre DNS).
  3. Ligne 44 vous pouvez ajouter un -Credential (si vous n'exécutez pas ce script dans un contexte avec les droits nécessaires).

 

Ce dernier est une version pour un WSUS unique en mode autonome ou déconnecté, ne possédant pas de Downstram Server, je publierais une autre version pour ce cas de figure.

#################################
# Step 1 : Variables Definition #
#################################
#region - Variables
$RootFolder = "C:\Temp\WSUS"
$ResultFolder = "$RootFolder\Result"
$LogFolder = "$RootFolder\Logs"
$Archives = "$RootFolder\Archives"
$AllFolders = $RootFolder, $ResultFolder, $LogFolder, $Archives
$LogFile = "$LogFolder\GeneralLog.log"
$Date = Get-Date -Format M
$TargetDate = ((Get-Date).AddDays(-30)).ToString('MM/dd/yyyy')
$Array = @()
$Alias = "wsus"

# Folders creation
foreach ($Folder in $AllFolders) {
    If (!(Test-Path $Folder)) {
        New-Item $Folder -ItemType Directory
    }
}

# Old Files deletion
Get-ChildItem $LogFolder | Remove-Item -Force
Get-ChildItem $ResultFolder | Remove-Item -Force
#endregion - Variables

###############################
# Step 2 : Locate WSUS Server #
###############################
#region - Locate WSUS Server
Write-Output "Trying to locate WSUS server" | Add-Content $LogFile
Try {
    $WSUSServer = Resolve-DnsName $Alias -ErrorAction Stop | select -ExpandProperty Name -Last 1
    Write-Output "Successfully locate WSUS server" | Add-Content $LogFile
}
Catch {
    Write-Output "Failed to locate WSUS server" | Add-Content $LogFile
}

# Wsus Role Check
Write-Output "Verifying WSUS server role installation" | Add-Content $LogFile
Try {
    $WsusRoleInstalled = Invoke-Command -ComputerName $WSUSServer -ScriptBlock {Get-WindowsFeature | Where-Object {($_.InstallState -eq "Installed") -and ($_.Name -like "UpdateServices")}} -ErrorAction Stop | Select-Object -Property Installed
    Write-Output "Successfully Run the query" | Add-Content $LogFile
    If ($WsusRoleInstalled.Installed -ne $true) {
        # End of Script
        Write-Output "WSUS Server not found" | Add-Content $LogFile
        Exit
    }
}
Catch {
    Write-Output "Failed to verify WSUS server role installation" | Add-Content $LogFile
    Exit
}
#endregion - Locate WSUS Server

############################
# Step 3 : WSUS Connection #
############################
#region - WSUS Connection
# Try on default port 8530
Try {
    $WsusPort = "8530"
    [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
    $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)
    $Connection = $Wsus.Name
}
Catch {
    # Try on port 80
    Try {
        $WsusPort = "80"
        [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
        $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)
        $Connection = $Wsus.Name
    }
    Catch {
        # Try on port 8531
        Try {
            $WsusPort = "8531"
            [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
            $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$true,$WsusPort)
            $Connection = $Wsus.Name
        }
        Catch {
            # Try on port 443
            Try {
                $WsusPort = "443"
                [void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
                $Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$true,$WsusPort)
                $Connection = $Wsus.Name
            }
            Catch {
                # End of Script
                Write-Output "WSUS Server connection failed on all ports" | Add-Content $LogFile
                Exit
            }
        }
    }
}
#endregion - WSUS Connection

###########################
# Step 4 : WSUS Reporting #
###########################
#region - WSUS Reporting
# Variables Definition
$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateClassifications = $Wsus.GetUpdateClassifications() | Where-Object {$_.Title -eq "Security Updates"}
$UpdateScope.Classifications.AddRange($updateClassifications)
$Servers = $wsus.GetComputerTargets($computerscope) | Where-Object {($_.ComputerRole -eq "Server")}
$NBServers = $Servers.Count

Write-Output "WSUS Server has $NBServers to manage" | Add-Content $LogFile
$Servers | Foreach {
    $FullDomainName = $_.FullDomainName
    $WorkstationID = $_.Id
    $LastSyncClient = $_.LastSyncTime
    [STRING]$LastSyncResultClient = $_.LastSyncResult
    $LastReportedStatusTime = $_.LastReportedStatusTime
    $IPAddress = ($_.IPAddress).IPAddressToString
    $Id = $_.Id
    $Make = $_.Make
    $Model = $_.Model
    $OSLanguage = ($_.OSInfo).DefaultUILanguage
    $OSArchitecture = $_.OSArchitecture
    $OSFamily = $_.OSFamily
    $OSDescription = $_.OSDescription
    $ComputerTargetGroupIds = $_.ComputerTargetGroupIds
    $SyncsFromDownstreamServer = $_.SyncsFromDownstreamServer
    $UpdateServer = $_.UpdateServer.Name

    # Get AD Information
    Try {
        Write-Output "Trying to retrieve Active Directory Informations for $FullDomainName" | Add-Content $LogFile
        $CurrentServer = Get-ADComputer -Filter {DNSHostName -eq $FullDomainName} -Properties CanonicalName -ErrorAction Stop | Select-Object Enabled,CanonicalName,OperatingSystem,Name
        Write-Output "Successfully retrieve Active Directory Informations for $FullDomainName" | Add-Content $LogFile
    }
    Catch {
        Write-Output "Failed to retrieve Active Directory Informations for $FullDomainName" | Add-Content $LogFile
    }
    # Define state for Begining and End of year, plus a targeted date (here M-30)
    $updatescope.ToCreationDate=[DATETIME]"01/31/2022"
    $KBToInstallJanuary = $wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | where {($_.ComputerTargetId -eq $WorkstationID)} |Select-Object -ExpandProperty NotInstalledCount
    $updatescope.ToCreationDate=[DATETIME]"12/31/9999"
    $KBToInstallUpToDate = $wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | where {($_.ComputerTargetId -eq $WorkstationID)} |Select-Object -ExpandProperty NotInstalledCount
    $updatescope.ToCreationDate=[DATETIME]"$TargetDate"
    $KBToInstallJ30 = $wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | where {($_.ComputerTargetId -eq $WorkstationID)} |Select-Object -ExpandProperty NotInstalledCount
    $IPAddress = ($_."IPAddress").IPAddressToString
    
    $Array += New-Object psobject -Property @{
        FullDomainName = $FullDomainName
        WorkstationID = $Id
        LastSyncClient = $LastSyncClient
        LastSyncResultClient = $LastSyncResultClient
        LastReportedStatusTime = $LastReportedStatusTime
        IPAddress = $IPAddress
        Enabled = $CurrentServer.Enabled
        CanonicalName =$CurrentServer.CanonicalName
        Name = $CurrentServer.Name
        KBNeededInJanuary = $KBToInstallJanuary
        KBNeededLastMonth = $KBToInstallJ30
        KBNeededToBeUpToDate = $KBToInstallUpToDate
        Id = $Id
        Make = $Make
        Model = $Model
        OSLanguage = $OSLanguage
        OSArchitecture = $OSArchitecture
        OSFamily = $OSFamily
        OSDescription = $OSDescription
        ComputerTargetGroupIds = $ComputerTargetGroupIds
        SyncsFromDownstreamServer = $SyncsFromDownstreamServer
        UpdateServer = $UpdateServer
    }
    # Release
    $FullDomainName = $null
    $WorkstationID = $null
    $LastSyncClient = $null
    $LastSyncResultClient = $null
    $LastReportedStatusTime = $null
    $IPAddress = $null
    $Id = $null
    $Make = $null
    $Model = $null
    $OSLanguage = $null
    $OSArchitecture = $null
    $OSFamily = $null
    $OSDescription = $null
    $ComputerTargetGroupIds = $null
    $SyncsFromDownstreamServer = $null
    $CurrentServer = $null
    $UpdateServer = $null
    $KBToInstallJanuary = $null
    $KBToInstallUpToDate = $null
    $KBToInstallJ30 = $null
}
#endregion - WSUS Reporting

########################
# Step 4 : Export Data #
########################
#region - Export Data
$Array | select WorkstationID,Make,Model,OSFamily,OSDescription,OSArchitecture,OSLanguage,IPAddress,FullDomainName,Name,CanonicalName,Enabled,SyncsFromDownstreamServer,UpdateServer,LastReportedStatusTime,LastSyncClient,LastSyncResultClient,KBNeededInJanuary,KBNeededLastMonth,KBNeededToBeUpToDate | Export-Csv $ResultFolder\Report.csv -Delimiter ";" -Encoding UTF8 -NoTypeInformation

#endregion - Export Data

 

 

[AD Connect] - Event Id 528

Contexte

Après un reboot du serveur AD Connect, le service "ADSync" reste en "Starting" ("démarrage" pour les OS en FR), et après quelque temps une Alerte Azure devrait remonter avec le message suivant :

En allant jeter un oeil dans les journaux d'événements, dans le journal "Application" vous devriez trouver l'erreur suivante :

Event Id 528

"Windows API call WaitForMultipleObjects returned error code: 575. Windows system error message is: {Application Error}

The application was unable to start correctly (0x%lx). Click OK to close the application.

Reported at line: 3714."

 

Bon a ce stade je vais être honnête, ça sent pas bon, la base de données locale d'AD Connect est dans un mauvais état (les fichiers sont corrompus) et nous devrions normalement réinstaller le serveur mais, avant cela on va d'abord essayer un petit "tricks".

 

Solution

1-Arrêter le service "ADSync"

Nous allons dans un premier temps "Désactiver" le service "ADSync" et redémarrer le serveur.

Ouvrez la console des services "Services.msc", sélectionnez le service "ADSync", faites un clic droit "Properties" et changez le "Startup Type" en sélectionnant "Disabled".

 

Enfin redémarrez le serveur.

2-Remplacer les fichiers corrompus

Après le redémarrage du serveur, connectez vous et ouvrez un explorateur jusqu'ici (Attention : Remplacez "XXXXXXXXXXXXX" par le nom du compte que vous utilisez) :

C:\Users\XXXXXXXXXXXXX\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\ADSync2019” (C'est ici que se trouve les  fichiers corrompus).

Ouvrez un second explorateur jusqu'ici :

C:\Program Files\Microsoft SQL Server\150\LocalDB\Binn\Templates

Dans ce second explorateur copiez les fichiers suivants

  • Model.mdf
  • Modellog.ldf

Enfin collez les dans le premier explorateur (“C:\Users\XXXXXXXXXXXXX\AppData\Local\Microsoft\Microsoft SQL Server Local DB\Instances\ADSync2019”) :

 

Confirmez le remplacement des fichiers

 

3-Remmettre le service ADSync en état.

Ouvrez la console des services "Services.msc", sélectionnez le service "ADSync", faites un clic droit "Properties" et changez le "Startup Type" en sélectionnant "Automatic".

4-Redémarrer le service.

Démarrez le service, ouvrez la console des services "Services.msc", sélectionnez le service "ADSync", faites un clic droit et sélectionnez "Start"

 

Si vous êtes dans un jour de chance le service démarre et la synchronisation commence.

 

Conclusion

En conclusion, nous ne parvenons pas à expliquer ce qui a causé la corruption des fichiers lors du dernier redémarrage mais avec ce petit "tricks" on a pu éviter la réinstallation du serveur AD Connect.

Powershell : Lister les membres externes des OneDrives

Attention au côté juridique de cette opération. 

Dans certain cas de figure nous sommes amenés à devoir faire une liste de tout accès externe à différents types de ressources, dans le cas présent nous allons lister l'ensemble des OneDrives avec un partage externe, pour ce faire vous aurez besoin de :

  • Powershell 5.1,
  • Le module Sharepoint Online,
  • un accès Sharepoint Admin.

Vous pouvez maintenant lancer le script en prenant soin de modifier les url de connexion (L2 et L59) 

# Connection to Sharepoint Online
Connect-SPOService -Url https://contoso-admin.sharepoint.com # (Please change the connection URL)

# Get the list of All OneDrives
$AllOneDrive = Get-SPOSite -IncludePersonalSite $true -Limit all -Filter "Url -like '-my.sharepoint.com/personal/'"

# Define variables
$ArrayOneDriveMembers = @()
$SortOneDrive = $AllOneDrive | sort Url
$X = 0

$SortOneDrive | foreach {
    $OneDriveUrl = $_.Url
    $OneDriveOwner = $_.Owner
    $OneDriveStorageQuota = $_."Storage Quota"
    $OneDriveTitle = $_.Title
    # Get Member of current OneDrive
    Try {
        $OneDriveMembers = Get-SPOUser -Site $OneDriveUrl -ErrorAction stop
        }
    Catch {
        Write-Warning $($_)
        Write-Output "$OneDriveUrl;$OneDriveTitle" | Add-Content C:\temp\Onedriveerrors.txt
        }
    
    # Get External members
    $OneDriveExternal = $OneDriveMembers.where({$_.Usertype -eq "Guest"})
    If ($OneDriveExternal.count -ne 0) {
        Write-Host "External Users are present in $OneDriveTitle" -ForegroundColor Green
        $OneDriveExternal.count
        $OneDriveExternal | foreach {
            $DisplayName = $_.DisplayName
            $LoginName = $_.LoginName

            # Store Data
            $ArrayOneDriveMembers += New-Object psobject -Property @{
                OneDriveUrl = $OneDriveUrl
                OneDriveOwner = $OneDriveOwner
                OneDriveStorageQuota = $OneDriveStorageQuota
                OneDriveTitle = $OneDriveTitle
                DisplayName = $DisplayName
                LoginName = $LoginName
                }
            # Release
            $DisplayName = $null
            $LoginName = $null
            }
        }
    # Release
    $OneDriveUrl = $null
    $OneDriveOwner = $null
    $OneDriveStorageQuota = $null
    $OneDriveTitle = $null
    $OneDriveMembers = $null
    
    # After 500 connection, reconnect to Sharepoint
    If ($x -eq 500) {
        # Connect to Sharepoint
        Connect-SPOService -Url https://contoso-admin.sharepoint.com 
        $x =0
        }
    $x++
    }

# Export Data
$ArrayOneDriveMembers | Export-Csv c:\temp\ExternalOneDriveMembers.csv -Encoding UTF8 -Delimiter ";" -NoTypeInformation

 

SCOM : Management Server installation failed.

Contexte

Lors du déploiement d'un Management Server (SCOM) sur un nouveau serveur, je me suis retrouvé confronté à une erreur lors de l'installation, ce dernier détectait que SCOM était déjà installé sur le serveur alors que ce dernier avait fraichement déployé.

Après avoir vérifié que ni SCOM, ni l'agent était installé sur le serveur en question, j'ai parcouru les logs d'installation à la recherche d'une information qui pourrait m'aider, malheureusement rien et, idem dans l'Event Viewer.

Après de multiples tentatives et analyse, nous avons redéployer un serveur tout neuf mais, avec le même résultat.

Finalement en analysant l'ensemble du process, je me suis aperçu que l'agent avait été déployé durant l'installation du serveur, puis désinstallé manuellement, par conséquent l'agent n'était plus présent sur le serveur mais, certaines clés de registre elles l'étaient encore.

 

Solution

Il  a donc fallu vérifier qu'aucune de ces clés n'étaient présentes et, supprimer celles qui l'étaient :

  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft Operations Manager
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\System Center Operations Manager
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\System Center
  • HKEY_LOCAl_MACHINE\System\CurrentControlSet\Services\MOMConnector
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Healthservice

Et également supprimer le dossier faisant référence à SCOM sous l'arborescence ci dessous : 

  • HKEY_CLASSES_ROOT\Installer\Products

Une fois ce nettoyage réalisé, un redémarrage du serveur fut nécessaire et l'installation a pu être réalisé avec succès.