PI Services

Le blog des collaborateurs de PI Services

Powershell : Lister les membres externes des sites Sharepoint

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 sites Sharepoint 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 et identifiants (L2, L21, L74 et L88).

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

# Define variables
$ArraySP = @()
$i = 0
$x = 0

# Get the list of All 
$AllSPSite = Get-SPOSite -Limit All
$AllSPSite = $AllSPSite | sort Url

$AllSPSite | foreach {
    $I++
    $Url = $_.Url
    $Owner = $_.Owner
    $Title = $_.title

    # If you are not in the Admin Group you will need to Grant your account as Admin
    Try {
        Set-SPOUser -Site $Url -LoginName "mathieu@contoso.com" -IsSiteCollectionAdmin:$true -ErrorAction Stop
        $Result = $true
        }
    Catch {
        Write-Warning $($_)
        Write-Output $Url | Add-Content C:\temp\SPError.txt
        $Result = $false
        }

    # If you're Admin you can check
    If ($Result -eq $true) {
        # Check Guest Access
        Try {
            # Get Members
            $Members = Get-SPOUser -Site $Url -Limit All -ErrorAction stop
            $External = $Members.where({$_.Usertype -eq "Guest"})

            If ($External.count -ne 0) {
                Write-Host "External found on $Title" -ForegroundColor Cyan
                $External.count
                $External | ForEach {
                    $DisplayName = $_.DisplayName
                    $LoginName = $_.LoginName
                    $Groups = $_.Groups

                    # Store Data
                    $ArraySP += New-Object psobject -Property @{
                        Url = $Url
                        Owner = $Owner
                        Title = $title
                        DisplayName = $DisplayName
                        LoginName = $LoginName
                        Groups = $Groups
                        }
                    # Release
                    $DisplayName = $null
                    $LoginName = $null
                    $Groups = $null
                    }
                }
            # Release
            $Members = $null
            $External = $null
            }
        Catch {
            Write-Warning $($_)
            Write-Host "$Title not Accessible" -ForegroundColor Yellow
            Write-Host "$Url not Accessible" -ForegroundColor Magenta
            Write-Output $Url | Add-Content C:\temp\SPError.txt
            }
        
        # Remove Admin Rights
        Try {
            Set-SPOUser -Site $Url -LoginName "mathieu@contoso.com" -IsSiteCollectionAdmin:$false
            }
        Catch {
            Write-Warning $($_)
            Write-Output $Url | Add-Content C:\temp\SPError.txt
            }
        }
    
    # Release
    $Url = $null
    $Owner = $null
   
    # After 200 connection, reconnect to Sharepoint
    If ($x -eq 200) {
        Connect-SPOService -Url https://contoso-admin.sharepoint.com # (Please change the connection URL)
        $x = 0
        }
    $x++
    }

$ArraySP | Export-Csv c:\temp\ExternalSharepointMembers.csv -Encoding UTF8 -Delimiter ";" -NoTypeInformation

 

[Powershell] - Durcissement Windows Server 2016

Ce script permet de désactiver les éléments non essentiels au serveur lors de son installation.

##################################
# Step 1 : Network configuration #
##################################
Write-Host 'Network configuration' -ForegroundColor Green
Write-Host 'WINS configuration' -ForegroundColor Gray
$Arguments = New-Object System.Collections.Hashtable
$Arguments.Add('DNSEnabledForWINSResolution', $false)
$Arguments.Add('WINSEnableLMHostsLookup', $false)

$ErrorCode = (Invoke-CimMethod -ClassName Win32_NetworkAdapterConfiguration -MethodName EnableWINS -Arguments $Arguments).ReturnValue
If ($ErrorCode -gt 0) {
	Write-Host 'Error when disabling WINS' -ForegroundColor Red
    }

############################
# Step 2 : Remove Features #
############################

Write-Host 'Features configuration' -ForegroundColor Green
$Features = @(
    'FS-SMB1'
    )
$Features | ForEach-Object { 
    $CurrentFeature = $_
    Try {
        Uninstall-WindowsFeature -Name $CurrentFeature -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    # Release
    $CurrentFeature = $null
    }

######################################
# Step 3 : Disabling Scheduled Tasks #
######################################

Write-Host 'Scheduled Tasks configuration' -ForegroundColor Green
$ScheduledTasks = @(
    '*XblGameSaveTask*'
    '*XblGameSaveTaskLogon*'
    )
$ScheduledTasks | ForEach-Object {
    $CurrentScheduledTask = $_
    Try {
        Get-ScheduledTask -TaskName $CurrentScheduledTask | Disable-ScheduledTask -ErrorAction Stop | Out-Null
        }
    Catch {
        Write-Warning $($_)
        }
    $CurrentScheduledTask = $null
    }

################################
# Step 4: Windows Applications #
################################

Write-Host 'Windows Applications configuration' -ForegroundColor Green
$Apps = Get-AppxPackage -AllUsers | Where-Object {$_.NonRemovable -eq $false}
$Packages = Get-AppxProvisionedPackage -Online:$true

# Verifying if some Apps could be remove
If ($Apps.count -gt 0) {
    $Apps | ForEach-Object {
        $AppName = $_.Name
        Try {
            $Package = Get-AppxPackage -Name $AppName -ErrorAction Stop
            If ($Package) {
                Try {
                    # Remove App
                    Write-Host "Removing $AppName" -ForegroundColor Cyan
                    #Remove-AppPackage -Package $Package -ErrorAction Stop
                    $Package = $null
                    }
                Catch {
                    Write-Warning $($_)
                    }
                }
            Else {
                If ($Packages.Count -gt 0) {
                    # Remove the app with Online Package
                    $Package = $Packages.Where({$_.DisplayName -eq $AppName})
                    If ($Package) {
                        Try {
                            Write-Host "Removing $AppName" -ForegroundColor DarkCyan
                            Remove-AppxProvisionedPackage -PackageName $Package.PackageName -Online -ErrorAction Stop
                            }
                        Catch {
                            Write-Warning $($_)
                            }
                        }
                    }
                }
            }
        Catch {
            Write-Warning $($_)
            }
        # Release
        $AppName = $null
        }
    }

###################################
# Step 5 : OneDrive configuration #
###################################

Write-Host 'OneDrive configuration' -ForegroundColor Green
If (Get-Process -Name "*onedrive*") {
    Try {
        Get-Process -Name "*onedrive*" | Stop-Process -Force -ErrorAction Stop
        If (Test-Path -Path "$env:SystemRoot\SysWOW64\OneDriveSetup.exe") {
            Try {
                Start-Process -FilePath "$($env:SystemRoot)\SysWOW64\OneDriveSetup.exe" -ArgumentList '/uninstall' -ErrorAction Stop | out-Null
                }
            Catch {
                Write-Warning $($_)
                }
            }
        Elseif (Test-Path -Path "$env:SystemRoot\System32\OneDriveSetup.exe") {
            Try {
                Start-Process -FilePath "$($env:SystemRoot)\System32\OneDriveSetup.exe" -ArgumentList '/uninstall' -ErrorAction Stop | out-Null
                }
            Catch {
                Write-Warning $($_)
                }
            }
        }
    Catch {
        Write-Warning $($_)
        }
    }
Else {
    Write-Host "No Process OneDrive here" -ForegroundColor Cyan
    }

###################################
# Step 6 : Services configuration #
###################################

$services = @(
    'AppVClient';
    'AudioEndpointBuilder';
    'Audiosrv';
    'AxInstSV';
    'Browser';
    'bthserv';
    'CDPUserSvc';
    'CscService';
    'dmwappushservice';
    'FrameServer';
    'icssvc';
    'lfsvc';
    'lltdsvc';
    'MapsBroker';
    'NcbService';
    'NetTcpPortSharing';
    'OneSyncSvc';
    'PcaSvc';
    'PhoneSvc';
    'PrintNotify';
    'QWAVE';
    'RemoteAccess';
    'RmSvc';
    'SCardSvr';
    'ScDeviceEnum';
    'SensorDataService';
    'SensorService';
    'SensrSvc';
    'SharedAccess';
    'ShellHWDetection';
    'Spooler';
    'SSDPSRV';
    'stisvc';
    'TabletInputService';
    'tzautoupdate';
    'UevAgentService';
    'upnphost';
    'WalletService';
    'WiaRpc';
    'wisvc';
    'wlidsvc';
    'WpnService';
    'WSearch';
    'XblAuthManager';
    'XblGameSave'
    )

$services | Get-Service | Stop-Service -Force

$services | Get-Service | Set-Service -StartupType Disabled -Status Stopped

Try {
    Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NgcCtnrSvc" -Name "Start" -Value 0x4 -Force -ErrorAction Stop
    }
Catch {
    Write-Warning $($_)
    }
Try {
    Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NgcSvc" -Name "Start" -Value 0x4 -Force -ErrorAction Stop
    }
Catch {
    Write-Warning $($_)
    }
Try {
    Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\PimIndexMaintenanceSvc" -Name "Start" -Value 0x4 -Force -ErrorAction Stop
    }
Catch {
    Write-Warning $($_)
    }
Try {
    Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\UnistoreSvc" -Name "Start" -Value 0x4 -Force -ErrorAction Stop
    }
Catch {
    Write-Warning $($_)
    }
Try {
    Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\UserDataSvc" -Name "Start" -Value 0x4 -Force -ErrorAction Stop
    }
Catch {
    Write-Warning $($_)
    }
Try {
    Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\WpnUserService" -Name "Start" -Value 0x4 -Force -ErrorAction Stop
    }
Catch {
    Write-Warning $($_)
    }

$services2 = @(
    'NgcCtnrSvc';
    'NgcSvc';
    'PimIndexMaintenanceSvc';
    'UnistoreSvc';
    'UserDataSvc';
    'WpnUserService';
    )

$services2 | Get-Service | ft Name,StartType,Status

If (Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NgcCtnrSvc_*") {
    Try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NgcCtnrSvc_*' -Name 'Start' -Value 0x4 -Force -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    }


If (Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NgcSvc_*") {
    Try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NgcSvc_*' -Name 'Start' -Value 0x4 -Force -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    }


If (Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Services\PimIndexMaintenanceSvc_*") {
    Try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\PimIndexMaintenanceSvc_*' -Name 'Start' -Value 0x4 -Force -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    }


If (Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Services\UnistoreSvc_*") {
    Try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\UnistoreSvc_*' -Name 'Start' -Value 0x4 -Force -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    }


If (Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Services\UserDataSvc_*") {
    Try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\UserDataSvc_*' -Name 'Start' -Value 0x4 -Force -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    }


If (Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Services\WpnUserService_*") {
    Try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\WpnUserService_*' -Name 'Start' -Value 0x4 -Force -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    }

If (Test-Path -Path "HKLM:\SYSTEM\CurrentControlSet\Services\xbgm") {
    Try {
        Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\xbgm' -Name 'Start' -Value 0x4 -Force -ErrorAction Stop
        }
    Catch {
        Write-Warning $($_)
        }
    }

 

[Office 365] - Forcer la création de OneDrive

Par défaut lorsque vous créez un utilisateur sur Azure AD et que vous lui attribuez une licence Office (F3, E3, E5) le OneDrive n'est pas créé directement; en effet ce dernier n'est provisionné que lorsque l'utilisateur cliquera sur Onedrive lors de sa connexion.

Lorsque vous souhaitez réaliser une migration O365 ce sujet peut être une problématique, car si les Onedrive ne sont pas créés vous pourrez difficilement lancer une migration en amont.

Il est possible de contourner ce sujet en utilisant les commandes Powershell suivantes (avec les droits minimum de "SharePoint Online administrator") :

# Conect To Sharepoint Online
Connect-SPOService -Credential $Credential -Url https://XXXXX-admin.sharepoint.com

Prenez soin de remplacer l'url de connexion par la votre.

Ensuite vous pouvez soit fournir une liste d'utilisateur, soit récupérer l'ensemble des utilisateurs possédant une licence (ici nous importons une liste) et, lancer les requêtes de création.

# Import Users
$users = Import-Csv C:\Temp\Users.csv 

# Start Request
foreach ($u in $users) {
    $upn = $u.userprincipalname
    $list += $upn

    if ($i -eq 199) {
        #We reached the limit
        Request-SPOPersonalSite -UserEmails $list -NoWait
        Start-Sleep -Milliseconds 655
        $list = @()
        $i = 0
    }
}

 

[Powershell] - Etat des lieux des licences O365

Besoin d'avoir un état des lieux de vos licences O365 (voir de l'automatiser) ?

Voici quelques lignes Powershell qui vous remontrons les informations.

Prérequis:

Vous aurez besoin de:

  • Powershell 5.1
  • Le module Azure AD

Code :

Connect-AzureAD
$Array = @()
$AllSKU = Get-AzureADSubscribedSku

$AllSKU | sort SkuPartNumber | foreach {
    $SkuPartNumber = $_.SkuPartNumber
    $Bought = $_.PrepaidUnits.enabled
    $Suspended = $_.PrepaidUnits.Suspended
    $Warning = $_.PrepaidUnits.Warning
    $Assigned = $_.ConsumedUnits
    $Rest = $Bought - $Assigned

    $Array += New-Object psobject -Property @{
        License = $SkuPartNumber
        Bought = $Bought
        Assigned = $Assigned
        Will_Expired = $Warning
        Suspended = $Suspended
        Rest = $Rest
        }
    $SkuPartNumber = $null
    $Bought = $null
    $Assigned = $null
    $Warning = $null
    $Suspended = $null
    $Rest = $null
        
    }

$Array | FT License,Bought,Assigned,Will_Expired,Suspended,Rest
$Array | Export-Csv C:\temp\Licenses.csv -Delimiter ";" -Encoding UTF8 -NoTypeInformation

 

Bien entendu, pour l'automatiser il faudra modifier l'authentification et passer par de la moderne authentification (via un SPN).

[O365] - Extraire la liste des OneDrive qui sont partagés avec des externes.

Si un jour vous souhaitez faire un état des lieux de ce qui est déjà en place en terme de partage, voici quelques liges de Powershell qui permettent de sortir la liste des OneDrive ayant un partage avec un invité.

Prenez soin de remplacer l'url de connexion au portail d'admin Sharepoint.

# Connect to Sharepoint Online
Connect-SPOService -Url https://MonTenant-admin.sharepoint.com/

# Collect all Sharepoint Sites and filter on OneDrive
$AllOneDrive = Get-SPOSite -IncludePersonalSite $true -Limit all -Filter "Url -like '-my.sharepoint.com/personal/'"

# Define variabes 
$ArrayOneDriveMembers = @()
$SortOneDrive = $AllOneDrive | sort Url

$SortOneDrive | foreach {
    $OneDriveUrl = $_.Url
    $OneDriveOwner = $_.Owner
    $OneDriveStorageQuota = $_."Storage Quota"
    $OneDriveTitle = $_.Title
    # Get the list of members for the current OneDrive
    Try {
        $OneDriveMembers = Get-SPOUser -Site $OneDriveUrl -ErrorAction stop
        }
    Catch {
        Write-Output "$OneDriveUrl;$OneDriveTitle" | Add-Content C:\temp\Onedriveerrors.txt
        }
    
    # Define a new variable
    $OneDriveExternal = $OneDriveMembers.where({$_.Usertype -eq "Guest"})

    # Check if the new variable is empty, if not collect logs
    If ($OneDriveExternal.count -ne 0) {
        
        # Display some informations
        Write-Host "External Users are present in $OneDriveTitle" -ForegroundColor Green
        $OneDriveExternal.count

        # Store Data
        $OneDriveExternal | foreach {
            $DisplayName = $_.DisplayName
            $LoginName = $_.LoginName
            
            $ArrayOneDriveMembers += New-Object psobject -Property @{
                OneDriveUrl = $OneDriveUrl
                OneDriveOwner = $OneDriveOwner
                OneDriveStorageQuota = $OneDriveStorageQuota
                OneDriveTitle = $OneDriveTitle
                DisplayName = $DisplayName
                LoginName = $LoginName
                }
            # Release Variables
            $DisplayName = $null
            $LoginName = $null
            }
        }
    
    # Release variables
    $OneDriveUrl = $null
    $OneDriveOwner = $null
    $OneDriveStorageQuota = $null
    $OneDriveTitle = $null
    $OneDriveMembers = $null
    }

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

 

[Azure Active Directory] - Définir le lieu d'utilisation

Lorsque vous synchronisez vos utilisateurs via "Azure AD Connect", que vos utilisateurs sont dans différents pays et que vous utilisez l'audioconférence dans Teams, il semblerait logique que vos utilisateurs se voient attribuer un numéro "locale", plutôt qu'un numéro "Français".

 

Pour ce faire il est important de définir le lieu d'utilisation (Usage Location) dans Azure Active Directory, il est donc important de remplir l'attribut "Country" dans votre Active Directory OnPremise.

La construction est basé sur deux lettres, vous pourrez trouver la correspondance des pays et des lettres dans le lien ci-dessous:

Attention : Vous devrez utiliser les deux lettres en majuscule de la colonne "Language Culture Name"

https://docs.microsoft.com/en-us/previous-versions/commerce-server/ee825488(v=cs.20)?redirectedfrom=MSDN

 

Vous pouvez fixer cet attribut à l'aide de Powershell via la commande ci-dessous (en prenant soin de remplacer "Mathieu" par le nom de votre utilisateur) :

Get-ADUser Mathieu | Set-ADUser -Replace @{‘Country’=”US”}

Ou alors en important un fichier csv pour une modification en masse, exemple de code ci-dessous.

# Import Csv and Set PreferredLanguage Attribute for each user
Import-csv C:\Temp\ToFixed.csv | foreach {
    $SamAccountName = $_.SamAccountName
    $Attribute = $_.Attribute
    Try {
        # Modifying PreferredLanguage Attribute
        Set-ADUser -Identity $SamAccountName -Replace @{‘Country’=$Attribute} -ErrorAction Stop 
        }
    Catch {
        $SamAccountName | Add-Content C:\temp\ErrorUsageLocation.txt
        }
    # Release
    $SamAccountName = $null
    $Attribute = $null
    }

 

[Office 365] - Fixer la langue par défaut pour OneDrive

Lorsque vous synchronisez vos utilisateurs via "Azure Ad Connect" et, que vous avez des utilisateurs dans différents pays, la langue par défaut pour chacun des utilisateurs dépend d'un attribut de votre Active Directory OnPremise, cet attribut est le "preferredLanguage".

Il devra être construit en suivant la nomenclature "Language Culture Name" du lien ci-dessous :

https://docs.microsoft.com/en-us/previous-versions/commerce-server/ee825488(v=cs.20)?redirectedfrom=MSDN

Vous pouvez fixer cet attribut à l'aide de Powershell via la commande ci-dessous (en prenant soin de remplacer "Mathieu" par le nom de votre utilisateur) :

Get-ADUser Mathieu | Set-ADUser -Replace @{‘preferredLanguage’=”en-US”}

Ou alors en important un fichier csv pour une modification en masse, exemple de code ci-dessous.

# Import Csv and Set PreferredLanguage Attribute for each user
Import-csv C:\Temp\ToFixed.csv | foreach {
    $SamAccountName = $_.SamAccountName
    $Attribute = $_.Attribute
    Try {
        # Modifying PreferredLanguage Attribute
        Set-ADUser -Identity $SamAccountName -Replace @{‘preferredLanguage’=$Attribute} -ErrorAction Stop 
        }
    Catch {
        $SamAccountName | Add-Content C:\temp\ErrorPreferredLanguage.txt
        }
    # Release
    $SamAccountName = $null
    $Attribute = $null
    }

 

Powershell : Trouver la date de dernière connexion d'un utilisateur.

Comme vous le savez l'un des attributs qui nous est le plus important pour déterminer l'inactivité d'un compte est l'attribut "LastLogon", ce dernier permet de connaitre la dernière authentification valide d'un compte Active Directory.

Le problème c'est que ce dernier n'est pas répliqué entre les DCs, attention je parle bien du "LastLogon" et non le "LastLogonTimeStamp" qui lui l'est mais, ne détient pas la valeur réelle de la dernière authentification réussie.

Le "LastLogon" n'est pas répliqué entre les DCs pour une raison simple, éviter les "tempêtes" de réplication entre les DCs, imaginez, à chaque ouverture / déverrouillage de session cette valeur change, si vous pensez au nombre de fois que sont déverrouillées des sessions à la journée, cela vous donne une idée des réplications qui seraient nécessaires, par conséquent cet attribut n'est pas répliqué.

Ce qui pose donc problème c'est qu'il n'est pas possible d'avoir la réelle valeur de "LastLogon" sans devoir interroger l'intégralité des DCs et faire une comparaison de cette dernière.

Voici un script qui fera les requêtes et comparaisons pour vous, toutefois gardez à l'esprit qu'il va pour chaque utilisateur demander la valeur de l'ttribut "LastLogonà chaque DC, par conséquent ce script peut prendre plus ou moins de temps à s'exécuter en fonction du nombre d'utilisateur et du nombre de DCs.

$Array = @()
$AllDC = Get-ADDomainController -Filter * | sort Name | select Name
$AllEnabledUsers = Get-ADUser -Filter {Enabled -eq $true} | select Samaccountname

$AllEnabledUsers | select -First 5 | foreach {
    $CurrentUser = $_.Samaccountname
    $RealLastLogon = $null

    $AllDC | foreach {
        $CurrentDC = $_.Name

        Try {
            $Logon = Get-ADUser $CurrentUser -Properties LastLogon -Server $CurrentDC
            $LastLogon = [Datetime]::FromFileTime($Logon.LastLogon)
            If ($LastLogon -gt $RealLastLogon) {
                $RealLastLogon = $LastLogon
                }

            # Release variable
            $LastLogon = $null
            }
        Catch {
            Write-Warning $($_)
            }

        # Release variable
        $CurrentDC = $null
        }
    
    # Store Data
    $Array += New-Object psobject -Property @{
        DistinguishedName = $Logon.DistinguishedName
        Enabled = $Logon.Enabled
        GivenName = $Logon.GivenName
        Name = $Logon.Name
        ObjectClass = $Logon.ObjectClass
        ObjectGUID = $Logon.ObjectGUID
        SamAccountName = $Logon.SamAccountName
        SID = $Logon.SID
        Surname = $Logon.Surname
        UserPrincipalName = $Logon.UserPrincipalName
        LastLogon = $RealLastLogon
        }
        
    # Release Variables
    $CurrentUser = $null
    $RealLastLogon = $null
    }

Il ne vous restera qu'a examiner la sortie en rappelant la variable "$Array" ou à l'exporter dans un CSV comme ci-dessous par exemple.

$Array | Export-Csv C:\Temp\AllUsersWithLastLogon.csv -Delimiter ";" -Encoding UTF8 -NoTypeInformation

 

Si la valeur "LastLogon" est égale à "01/01/1601 01:00:00" cela veut dire que le compte ne s'est jamais connecté

Powershell : Supprimer les KB "Declined"

Quand on veut faire du ménage sur son WSUS et également dans la base de ce dernier, il peut s'avérer utile de supprimer les KB dont le statut est "IsDeclied".

Pour réaliser cela rien de plus simple que quelques lignes de Powershell et de la patience (beaucoup de patience si ça fait longtemps que cela n'a pas été fait).

Connexion

# Define Server Name
$WsusServer = "Mon-Server-WSUS"
# Define connection port
$WsusPort = "8530"

# Initiate Connection
[void][reflection.assembly]::loadwithpartialname("microsoft.updateservices.administration")
$Wsus = [microsoft.updateservices.administration.adminproxy]::getupdateserver($WsusServer,$false,$WsusPort)

# Verifying connection (Should be return the name of the wsus server)
$Wsus.name

 

Définition des Updates à supprimer

# List All Kb in WSUS database
$AllKb = $Wsus.GetUpdates()

# Filter on "IsDeclined" status
$IsDeclined = $AllKb.Where({$_.IsDeclined -eq $True})

 

Suppression des KB

# Delete KB
$IsDeclined | foreach {
    # Define Current Id
    $Id = $_.Id
    Try {
        $Wsus.DeleteUpdate($Id.UpdateId.ToString())
        }
    Catch {
        Write-Warning $($_)
        }
    # Release variable
    $Id = $null
    }

Cette commande est un peu longue mais si elle est exécutée régulièrement, la purge dure moins longtemps.

Powershell : Change the owner of my AD objects

Comme évoqué dans l'article  "Powershell : Who's the owner of my AD objects", il est possible que votre AD contienne des objets dont le propriétaire ne soit aucun des suivants :

  • "Enterprise Admins"
  • "Domain Admins"
  • "Administrators"

Afin de remettre "Domain Admins" comme propriétaire des objets, nous utiliserons Powershell pour chacun des types d'objets.

Attention : La variable $NoGood est issue des scripts du précédent article, pensez à vérifier ce qu'elle retourne au préalable.

Unités d'organisation

# Organizational Units
$NoGood | foreach {
    # Current OU
    $DistinguishedName = $_.DistinguishedName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADOrganizationalUnit -Identity $DistinguishedName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
         Write-Warning $($_)
         $DistinguishedName
        }

    # Release variable
    $DistinguishedName = $null
    }

Les groupes

# Group
$NoGood | foreach {
    # Current Group
    $SamAccountName = $_.SamAccountName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADGroup $SamAccountName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
        Write-Warning $($_)
        $SamAccountName
        }
    
    # Release variable
    $SamAccountName = $null
    }

Les utilisateurs

# Users
$NoGood | foreach {
    # Current User
    $SamAccountName = $_.SamAccountName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADUser $SamAccountName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
        Write-Warning $($_)
        $SamAccountName
        }
    
    # Release variable
    $SamAccountName = $null
    }

 

Les ordinateurs

# Computers
$NoGood | foreach {
    # Current  Computer
    $SamAccountName = $_.SamAccountName

    # Change Owner
    Try {
        # Define Target
        $TargetObject = Get-ADComputer $SamAccountName
        $AdsiTarget = [adsi]"LDAP://$($TargetObject.DistinguishedName)"

        # Set new Owner
        $NewOwner = New-Object System.Security.Principal.NTAccount("DOMAINXXX", "Domain Admins")
        $AdsiTarget.PSBase.ObjectSecurity.SetOwner($NewOwner)
        $AdsiTarget.PSBase.CommitChanges()
        }
    Catch {
        Write-Warning $($_)
        $SamAccountName
        }

    # Release variable
    $SamAccountName = $null
    }