PI Services

Le blog des collaborateurs de PI Services

Power BI Report Server - Script - Export de dashboards en PBIX

Le script ci-dessous prend en paramètre un fichier contenant une liste de dashboard a exporter au format pbix.

 

#####################################################################
###### PwBI_Export_Dashboards.ps1 - EXPORT DE RAPPORTS EN PBIX ######
#####################################################################


<# 

    .SYNOPSIS 
        CONNEXION A L'API SQL POWERBI REPORTING SERVICES ET EXPORT DE UN OU PLUSIEURS DASHBOARD
 

    .PARAMETER  
        webServiceUrl : Url Racine du serveur de rapport
        CatalogUrl : Url du catalogue des items
        DashboardListFile : Fichier des noms de dashboard a exporter
        BackupFolder : Dossier d'export des dashboards
        LogFolder : Chemin du dossier où creer le log du script

 
    .EXAMPLE 
        .\PwBI_Export_Dashboards.ps1 -webServiceUrl "http://MyServer/Reports"  -DashboardListFile ".\PwBI_Export_Dashboards_List.txt" -BackupFolder ".\BACKUP" -LogFolder ".\BACKUP" 
     
#>


[CmdletBinding()]
param(
[Parameter(Mandatory,HelpMessage="Url Racine du serveur de rapport")]
[string]$webServiceUrl,

[Parameter(Mandatory=$false,HelpMessage="Url du catalogue des items")]
[string]$CatalogUrl = "$webServiceUrl/api/v2.0/CatalogItems",


[Parameter(Mandatory,
HelpMessage="Fichier des noms de dashboard a exporter")]
[ValidateScript({
if( -Not ($_ | Test-Path) ){
                throw "File does not exist"
            }
            return $true 
})]
[string]$DashboardListFile,

[Parameter(Mandatory,HelpMessage="Dossier d'export des dashboards")]
[string]$BackupFolder,

[Parameter(Mandatory,HelpMessage="Chemin du dossier où creer le log du script")]
[string]$LogFolder = "D:\Indicateurs_Securité"

)




# GET DASHBOARD LIST CONTENT
[array]$DashboardList = Get-Content -Path $DashboardListFile



# SCRIPT NAME
$ScriptName = "PwBI_Export_Dashboards.ps1"

# LogName = ScriptName without extension
$Log = $ScriptName.Split('.')[0]

# GET CREDENTIALS TO CONNECT TO REPORT SERVER
$cred = $(Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME")



function Write-Log 
{ 
    <# 
    .SYNOPSIS 
        This function creates or appends a line to a log file. 
 
    .PARAMETER  Message 
        The message parameter is the log message you'd like to record to the log file. 
 
    .EXAMPLE 
        PS C:\> Write-Log -Message 'Value1' 
        This example shows how to call the Write-Log function with named parameters. 
    #> 
    [CmdletBinding()] 
    param ( 
        [Parameter(Mandatory)] 
        [string]$Message,
        [Parameter(Mandatory)] 
        [string]$LogPath, 
        [Parameter(Mandatory)] 
        [string]$LogName
        
    ) 
     
    try 
    { 
        $DateTime = Get-Date -Format ‘MM-dd-yy HH:mm:ss’ 
        Add-Content -Value "$DateTime - $Message" -Path "$LogPath\$LogName.log" 
    } 
    catch 
    { 
        Write-Error $_.Exception.Message 
    } 
} 




# TABLEAU DES CATALOG ITEMS
$Message = "Recuperation du catalogue des items..."
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            $CatalogItems = Invoke-RestMethod -Uri $CatalogUrl -ContentType 'application/json' -UseDefaultCredentials -Method get
            }
            catch
            {
            $Message = "Error during query of Catalog Items"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }



# COMPARAISON DE $CatalogItems ET $ReportList POUR DETERMINER LA LISTE DES DASHBOARDS A EXPORTER

$FoundReport = $CatalogItems.value | Where-Object {$_.name -in $DashboardList}

if (!($FoundReport))
{
$Message = "The required Dashboards have not been found in the catalog of the report server - END OF SCRIPT"
write-host -f Yellow $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
Exit 1
}



# EXPORT DES DASHBOARDS
try
{

    foreach ($Report in $FoundReport)
    {
    $url = "$CatalogUrl($($Report.Id))/Content/`$value"
    Invoke-WebRequest -UseDefaultCredentials -Uri $url -OutFile "$BackupFolder\$($Report.name).pbix"
    }

}
catch
{
$Message = "KO - The required Dashboards have not been found in the catalog of the report server - END OF SCRIPT"
write-host -f Yellow $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
Exit 1
}


$Message = "OK - The required Dashboards have been Exported to $BackupFolder - END OF SCRIPT"
write-host -f Green $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log
Exit 0



 

Power BI Report Server - Script - Execution d'un plan de refresh d'un rapport

Le script ci-dessous se connecte sur l'api de Reporting Services (Power BI) pour executer le rafraichissement des données d'un rapport spécifique.

 

###### PWBI_REFRESH_PLAN.PS1 ######

<# 

    .SYNOPSIS 
        CONNEXION A L'API SQL POWERBI REPORTING SERVICES ET EXECUTION DU PLAN DE RAFRAICHISSEMENT D'UN RAPPORT SPECIFIQUE
 

    .PARAMETER  
        webServiceUrl : Url Racine du serveur de rapport
        CatalogUrl : Url du catalogue des items
        ReportFolderPath : Chemin du dossier contenant le rapport a rafraichir (NB: apres la racine du serveur de rapport)
        ReportName : Nom du dashboard a rafraichir
        RefreshPlan : nom du plan de Refresh
        LogFolder : Chemin du dossier où creer le log du script

 
    .EXAMPLE 
        .\PwBI_Refresh_Plan.ps1 -webServiceUrl "http://MyServer/Reports" -ReportFolderPath "/MySpecFolder/" -ReportName "MySpecDashboard" -RefreshPlan "Refresh_Every_2H" -LogFolder "C:\Temp"   
     
#>


[CmdletBinding()]
param(
[Parameter(Mandatory,HelpMessage="Url Racine du serveur de rapport")]
[string]$webServiceUrl,

[Parameter(Mandatory=$false,HelpMessage="Url du catalogue des items")]
[string]$CatalogUrl = "$webServiceUrl/api/v2.0/CatalogItems",

[Parameter(Mandatory,HelpMessage="Chemin du dossier contenant le rapport a rafraichir (NB: apres la racine du serveur de rapport)")]
[string]$ReportFolderPath,

[Parameter(Mandatory,HelpMessage="Nom du dashboard a rafraichir")]
[string]$ReportName,

[Parameter(Mandatory,HelpMessage="nom du plan de Refresh")]
[string]$RefreshPlan,

[Parameter(Mandatory,HelpMessage="Chemin du dossier où creer le log du script")]
[string]$LogFolder = "D:\Indicateurs_Securité"

)

# FULL REPORT PATH
$FullPath = "`'$ReportFolderPath$ReportName`'"  # Backtick to escape "'" character.

# REFRESH PLAN URL
$RefreshPlanUrl = "$webServiceUrl/api/v2.0/PowerBIReports(Path=$FullPath)/CacheRefreshPlans"


# SCRIPT NAME
$ScriptName = "PwBI_Refresh_Plan.ps1"

# LogName = ScriptName without extension
$Log = $ScriptName.Split('.')[0]

# GET CREDENTIALS TO CONNECT TO REPORT SERVER
$cred = $(Get-Credential -Credential "$env:USERDOMAIN\$env:USERNAME")



function Write-Log 
{ 
    <# 
    .SYNOPSIS 
        This function creates or appends a line to a log file. 
 
    .PARAMETER  Message 
        The message parameter is the log message you'd like to record to the log file. 
 
    .EXAMPLE 
        PS C:\> Write-Log -Message 'Value1' 
        This example shows how to call the Write-Log function with named parameters. 
    #> 
    [CmdletBinding()] 
    param ( 
        [Parameter(Mandatory)] 
        [string]$Message,
        [Parameter(Mandatory)] 
        [string]$LogPath, 
        [Parameter(Mandatory)] 
        [string]$LogName
        
    ) 
     
    try 
    { 
        $DateTime = Get-Date -Format ‘MM-dd-yy HH:mm:ss’ 
        Add-Content -Value "$DateTime - $Message" -Path "$LogPath\$LogName.log" 
    } 
    catch 
    { 
        Write-Error $_.Exception.Message 
    } 
} 



            




# TABLEAU DES CATALOG ITEMS
$Message = "Recuperation du catalogue des items..."
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            $CatalogItems = Invoke-RestMethod -Uri $CatalogUrl -ContentType 'application/json' -UseDefaultCredentials -Method get
            }
            catch
            {
            $Message = "Error during query of Catalog Items"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }




# RECUPERATION DU RAPPORT SPECIFIQUE
$Message = "Recuperation du rapport $ReportName"
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            $CatalogItem = $CatalogItems.value | Where-Object Name -eq $ReportName
            if (!($CatalogItem))
            {
            $Message = "Unable to find $ReportName"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }
 


# RECUPERATION DU PLAN DE RAFRAICHISSEMENT
$Message = "Recuperation du plan de rafraichissement"
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            $refreshplans = 
            
            Invoke-RestMethod -Uri $RefreshPlanUrl -Method get -UseDefaultCredentials
            
            }
            catch
            {
            $Message = "Error during query of Refresh Plan"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }



# RECUPERATION DE L'ID DU PLAN DE RAFRAICHISSEMENT
$refreshplanId = $($refreshplans | Where-Object {$_.value.Description -eq $refreshplan}).value.Id




# EXECUTION DU PLAN DE RAFRAICHISSEMENT
$Message = "Execution du plan de rafraichissement"
write-host $Message
Write-Log -Message $Message -LogPath $LogFolder -LogName $Log


            try
            {
            
            Invoke-RestMethod -Uri "$webServiceUrl/api/v2.0/CacheRefreshPlans($refreshplanId)/Model.Execute" -Method post -UseDefaultCredentials
            
            }
            catch
            {
            $Message = "Error during execution of Refresh Plan"
            Write-Log -Message "$Message - $($Error[0].Exception)" -LogPath $LogFolder -LogName $Log
            Write-Host -F Yellow $Message
            exit 1
            }

 

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

 

Exchange Onprem_Déterminer le nombre d'utilisateurs utilisant réellement POP et IMAP

 

Contexte

Il existe de nombreuses boîtes aux lettres dans Exchange onpremise pour lesquelles IMAP et POP sont activés, mais est ce que réellement ces protocoles sont utilisés pour se connecter à Exchange ou pas ?

Démarche

Récupérons tout d'abord les utilisateurs pour lesquels IMAP et POP sont activés dans l'environnement Exchange Onpremise, puis comparons-les aux journaux IMAP et POP.

  1. Télécharger l’outil Log Parser Download Log Parser 2.2 from Official Microsoft Download Center puis installer-le au niveau du poste depuis lequel l’analyse sera effectuée.
  2. Filtrer les utilisateurs avec IMAP et POP activés en exéxutant tout d'abord les commandes suivantes au niveau de l'interface Exchange Powershell :

    $ImapEnabled = Get-CASMailbox -Filter {Imapenabled -eq $true} -ResultSize unlimited

    $PopEnabled = Get-CASMailbox -Filter {Popenabled -eq $true} -ResultSize unlimited

  3. Analyser les journaux IMAP et POP pour voir quels comptes se connectent réellement à ces protocoles.

  4. Examiner et comparer les deux ensembles de données :

Utiliser Excel ou des commandes similaires à celles ci-dessous :

Compare-Object $ImapLogs $($ImapEnabled.name) | Where sideindicator -eq “<=”

Compare-Object $POPLogs $($POPEnabled.name) | Where sideindicator -eq “<=”

 

Les utilisateurs qui utilisent activement IMAP et POP sont stockés respectivement dans les $IMAPLogs et $POPLogs. Tous les autres trouvés dans $IMAPEnabled et $POPEnabled et qui ne sont pas dans $IMAPLogs et $POPLogs pourraient avoir les protocoles potentiellement désactivés. 

 

Exchange_Erreur de suppression de l'abonnement Edge 2010 dans une infrastructure Exchange 2013

Description du problème

Lorsqu’il s’agit de supprimer l’abonnement Edge 2010 dans une organisation contenant des serveurs Exchange 2013, le message d'erreur ci-dessous apparaît suite à l'exécution de la commande Remove-EdgeSubscription servername, 

Remove-EdgeSubscription : Impossible d’effectuer cette modification car la propriété ExchangeVersion de l’objet est 0.20 (15.0.0.0), ce qui n’est pas pris en charge par la version actuelle 0.1 (8.0.535.0). Vous aurez besoin d’une version plus récente d’Exchange pour effectuer cette modification. Nom de la propriété : ExchangeVersion
At line:1 char:24 + Remove-EdgeSubscription <<<< edge2 -Verbose
+ CategoryInfo : NotSpecified: (0:Int32) [Remove-EdgeSubscription], DataValidationException
+ FullyQualifiedErrorId : D8A49A14,Microsoft.Exchange.Management.SystemConfigurationTasks.RemoveEdgeSubscription

 

Explication et Résolution

La commande Remove-EdgeSubscription supprime à la fois l’abonnement et tous les objets abonnés de la base de données ADAM locale sur le serveur Edge. Mais si l’un de ces objets abonnés a été créé sur Exchange 2013 après son installation, ils auront une version ExchangeVersion égale à 0.20 (15.0.0.0) et Exchange 2010 SP3 ne peut pas traiter cet objet d'ou le message d'erreur dessus.

 

Pour résoudre ce problème, via ADSIEDIT, recherchez et supprimez manuellement l’objet avec une version Exchange 0.20 (15.0.0.0) de la base de données AD LDS, puis réexécutez la cmdlet Remove-EdgeSubscription, cela devrait maintenant fonctionner (sauf si vous avez deux objets ou plus avec le numéro de version le plus élevé à chercher et à supprimer).

  1. Pour rechercher des objets avec une version ExchangeVersion supérieure à "0.1 (8.0.535.0)", correspondante à la version qu’Exchange 2010, ouvrez ADSIEdit sur le serveur Edge.

2. Cliquez avec le bouton droit sur le nœud ADSI Edit en haut de la fenêtre et choisissez Connect to...

3. Dans la boîte de dialogue Paramètres de connexion (illustrée ci-dessus), définissez le champ Sélectionner un contexte d’appellation connu sur Configuration . Tapez le nom du serveur local et le port AD LDS dans le champ Sélectionner ou taper un domaine ou un serveur. La valeur "server:port" doit être EDGESERVERNAME:50389

5. Développez l’arborescence jusqu’à ce que vous atteigniez CN=First organisation,CN=Microsoft Exchange,CN=Services,CN=Configuration,CN={...................}

6. Vous devez maintenant examiner chaque objet dont la valeur msExchVersion n’est pas 4535486012416. Par exemple, si vous aviez créé un domaine accepté en raison de la fédération dans Exchange 2013, il s’agirait d’un nouvel objet depuis 2010. Cet objet (sous CN=Accepted Domains,CN=Transport Setting,...) a la valeur 88218628259840. 

  • Pour vérifier qu’il s’agit de l’objet correct à supprimer manuellement, entrez Get-Object |FT name,ExchangeVersion à partir de l’environnement de ligne de commande Exchange Management Shell sur le serveur Edge où Object est la cmdlet que vous cherchez à interroger - dans le présent cas, ce serait Get-AcceptedDomain. Cet objet a une version ExchangeVersion plus récente et c’est donc (au moins) cet objet empêche la suppression de l'abonnement Exchange.
  • Supprimez manuellement l’objet dans ADSI Edit (vous pouvez le faire en toute sécurité, car il sera resynchronisé à partir de l’organisation Exchange si vous recréez l’abonnement Edge ultérieurement). Ne le supprimez pas d’Active Directory avec ADSI Edit, mais uniquement d’AD LDS. Veillez à ne supprimer que cet objet et non l’objet parent.

7. Une fois cet objet est supprimé, réessayez Remove-EdgeSubscription servername. S’il s’agit du seul objet, l’abonnement Edge sera supprimé du serveur Edge.

 

 

 

Active Directory - Comment déléguer l'affichage d'un mot de passe LAPS d'un ordinateur supprimé ?

LAPS ou Local Admin Password Solution est une solution développée par Microsoft qui permet de gérer un mot de passe local de chaque ordinateur d'un domaine Active Directory de façon individuel et autonome. Cet article ne couvrira pas la mise en place de LAPS ni son modèle de délégation, mais se focalisera sur la délégation de la récupération d'un mot de passe LAPS d'un objet ordinateur supprimé.

 

Important : la corbeille Active Directory doit être activée pour récupérer le mot de passe LAPS d'un ordinateur supprimé.

 

Les méthodes par défaut pour lire le mot de passe LAPS d'un ordinateur supprimé

Par ordre de simplicité, les méthodes classiques sont :

  1. Utiliser la console Active Directory Administative Center
  2. Utiliser la cmdlet PowerShell Get-ADObject avec le paramètre -IncludeDeleteObjects
  3. Utiliser l'utilitaire LDP.exe

Pourquoi ne pas utiliser une de ces méthodes pour lire les mots de passe LAPS des ordinateurs supprimés ? Parce que les droits nécessaires sont Domain Admin. Seul un nombre restreint d'administrateurs doit être membre de ce groupe à haut privilège.

 

 

Déléguer la lecture du mot de passe LAPS d'un ordinateur supprimé

Les tâches suivantes doivent être réalisées avec un utilisateur membre du groupe Domain Admin

 

1. Vérifier que l'utilisateur à la possibilité de voir et modifier les objets dans la corbeille AD

Dans une invite de commande exécuter en tant qu'administrateur, saisir la commande suivante :

dsacls "CN=Deleted Objects,DC=contoso,DC=intern" #Les mots contoso et intern sont à remplacer par le domaine à requêter

#Si le message suivant apparaît, les droits actuels de l'utilisateur sont insuffisants

Insufficient access rights to perform the operation.

The command failed to complete successfully.

 

 

Si le message précédent s'affiche et donc que les permissions ne s'affichent pas, il faut s'approprier la propriété du container Deleted Obects

dsacls "CN=Deleted Objects,DC=contoso,DC=intern" /takeownership #commande qui permet de devenir le propriétaire du container Deleted Objects

#Les permissions s'affichent alors, voici un exemple de permission

Owner: CONTOSO\Domain Admins
Group: NT AUTHORITY\SYSTEM

Access list:
{This object is protected from inheriting permissions from the parent}

Allow BUILTIN\Administrators          SPECIAL ACCESS
                                      LIST CONTENTS
                                      READ PROPERTY
Allow NT AUTHORITY\SYSTEM             SPECIAL ACCESS
                                      DELETE
                                      READ PERMISSONS
                                      WRITE PERMISSIONS
                                      CHANGE OWNERSHIP
                                      CREATE CHILD
                                      DELETE CHILD
                                      LIST CONTENTS
                                      WRITE SELF
                                      WRITE PROPERTY
                                      READ PROPERTY

 

 

 

2. Déléguer la lecture des mots de passe LAPS qui se trouvent dans la corbeille AD

Dans une invite de commande exécuter en tant qu'administrateur, saisir la commande suivante :

dsacls "CN=Deleted Objects,DC=contoso,DC=intern" /G CONTOSO\AD-GROUP-LAPS-DELETED-OBJECTS-READ:LCRP # commande qui permet de donner au groupe AD-GROUP-LAPS-DELETED-OBJECTS-READ les autorisations nécessaires pour lire les mots de passe LAPS des objets supprimés LC = list content et RP = read property

# ces permissions sont alors ajoutés à l'access list qui s'affiche

Allow CONTOSO\AD-GROUP-LAPS-DELETED-OBJECTS-READ
                                      SPECIAL ACCESS
                                      LIST CONTENTS
                                      READ PROPERTY

 

Remarique : il est nécessaire de créer le groupe AD-GROUP-LAPS-DELETED-OBJECTS-READ avant de taper la commande ci-avant, le nom du groupe peut être différent. 



3. Lire le mot de passe LAPS d'un objet ordinateur dans la corbeille AD

Les méthodes habituelles pour lire le mot de passe LAPS tel que LAPS UI ou la console Active Directory Users and Computers ne fonctionnent que sur les ordinateurs présents dans l'Active Directory, il est ainsi recommandé d'utiliser la cmdlet PowerShell Get-ADObject, par exemple :

Get-ADObject -Filter 'msds-LastKnownRDN -like "NameOfTheDeletedComputer"' -IncludeDeletedObjects -Properties ms-mcs-admpwd | Select ms-mcs-admpwd # il faut remplacer NameOfTheDeletedComputer par le nom de l'ordinateur dont le mot de passe LAPS doit être récupéré

# le mot de passe LAPS s'affiche en clair

 

Remarque : l'utilisateur qui doit récupérer le mot de passe de l'objet ordinateur supprimé doit être membre du groupe auquel la délégation a été fournie, dans cet exemple CONTOSO\AD-GROUP-LAPS-DELETED-OBJECTS-READ

Remarque 2 : l'utilisateur qui doit récupérer le mot de passe de l'objet ordinateur supprimé devait déjà avoir la possibilité de lire le mot de passe LAPS de l'ordinateur avant sa suppression

Navigateur : GPO pour configurer le mode IE dans Edge

Microsoft Edge intègre un mode IE (mode Internet Explorer) qui permet de s'appuyer sur les composants d'IE pour ouvrir certains anciens sites qui ne fonctionnent pas sur les nouveaux navigateurs.

Ci-dessous les étapes pour activer le mode IE dans Microsoft Edge via GPO :

1. Récupérer les fichiers ADMX

Si ce n'est pas déjà le cas, il faut récupérer les fichiers ADMX d'Edge et les intégrer dans votre contrôleur de domaine.

Les fichiers ADMX peuvent être téléchargés ici : Fichiers ADMX d'Edge

2. Configurer IE Mode en GPO

Dans l'éditeur de stratégie de groupe, créez une nouvelle GPO

Sur le dossier "Microsoft Edge" sous "Configuration ordinateur" > "Stratégies" > "Modèles d'administration", recherchez le paramètre "Configurer l'intégration d'Internet Explorer". Activez ce paramètre et choisissez le mode d'intégration "Mode Internet Explorer"

Si vous activez uniquement ce paramètre, le mode Internet Explorer sera automatiquement utilisé pour les sites de votre zone Intranet. Si vous désirez l'utiliser sur des sites externes et gérer une liste de sites, il faut activer le paramètre "Configurer la liste des sites en Mode Entreprise".

Il faut également spécifier le chemin vers le fichier XML qui contient la liste des sites

Afin de vérifier que la GPO s'applique bien, dans Edge, vous pouvez accéder à ce lien dans le navigateur Edge: edge://policy

3. Générer le fichier XML des sites pour IE Mode

Pour créer le fichier XML avec la liste des sites, vous pouvez utiliser ce code de base :

<site-list version="4">
   <created-by>
      <tool>EMIESiteListManager</tool>
      <version>12.0.0.0</version>
      <date-created>07/19/2022 14:03:04</date-created>
   </created-by>
   <site url="www.piservice.fr">
      <compat-mode>Default</compat-mode>
      <open-in>IE11</open-in>
   </site>
   <site url="blog.piservices.fr">
      <compat-mode>Default</compat-mode>
      <open-in>IE11</open-in>
   </site>
</site-list>

Il y a un outil pour générer ce code XML qui s'appelle "Enterprise Mode Site List Manager" qui peut être téléchargé ici: EMSLM

Une fois installé, ouvrez l'outil et cliquez sur le bouton "Add", vous pouvez ajouter un site à la liste et indiquer les paramètres qui s'appliquent pour ouvrir ce site. Lorsque la liste est prête, cliquez sur le bouton "File" puis "Save to XML".

Lorsque vous accédez à un site de cette liste dans Edge, une icône IE apparaîtra dans la barre d'adresse pour vous indiquer qu'il s'exécute via le mode IE, tout en étant toujours dans Edge.

Documentation officielle :

[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 $($_)
        }
    }

 

MECM : Paramètres de langue dans SUP pour Windows 11 Servicing (déploiement Feature Updates)

Contrairement aux versions précédentes de Windows, les Feature Updates de Windows 11 dans MECM Servicing nécessitent de définir toutes les langues nécessaires dans le point de mise à jour logicielle. Sinon, ils seront synchronisés mais pas téléchargés.

Configuration classique pour Windows 10

Prenant un exemple où on souhaite télécharger les Feature updates Windows 10 en Anglais, Français, Italien, Allemand et Espagnol :

--> Uniquement deux langues cochées dans la configuration SUP, mais les Feature Updates Windows 10 peuvent être synchronisés et téléchargés en plusieurs langues.

Configuration pour Windows 11

Le Windows Servicing dans MECM ne télécharge les maj d'upgrade Windows 11 que si les langues sont configurées dans SUP.

Prenant le même exemple ci-dessus (Anglais, Français, Italien, Allemand et Espagnol) :

Si on ne coche que Français et Anglais, les Feature Updates Windows 11 seront synchronisés en plusieurs langues mais ne peuvent être téléchargés que en langue française et anglaise.

Active Directory - Comment afficher les attributs LDAP non-visibles dans l'utilitaire de délégation Active Directory ?

Cela fait 1 h que vous cherchez dans l'utilitaire de délégation de permission d'Active Directory votre attribut, mais impossible de le trouver ?

Dans un premier temps, il faut savoir que les noms affichés dans cet utilitaire sont sous le format "friendly name" qui n'est pas le nom technique dit "LDAP name". Vous pouvez vous référer à l'article suivant qui fait la correspondance entre les 2 représentations pour les attributs les plus utilisés : tableau de correspondance attribut LDAP et friendly name

Si votre attribut n'est toujours pas visible, c'est parce que par défaut certains attributs sont volontairement cachés.

 

Afficher un attribut caché dans l'utilitaire de délégation Active Directory

 

Pour afficher un attribut caché dans l'utilitaire de délégation Active Directory, il faut se connecter à un contrôleur de domaine (DC) et ouvrir le fichier dssec.dat.

Par défaut, celui-ci se trouve sous C:\Windows\System32

Le fichier est divisé par type d'objet LDAP : utilisateur, ordinateur, etc.

Lorsqu'un attribut est égale à 7 l'attribut n'est pas visible dans l'utilitaire de délégation, il faut modifier la valeur à 0 pour le rendre visible.

 

Exemple sur l'attribut "st" dont le friendly name est "State/Province"

Avant :

 

Après :

 

Remarque : les modifications dans le fichier dssec.dat ne sont pas répliqués sur les autres DC, il faut donc réutiliser le même DC pour de futures modifications ou refaire les mêmes manipulations sur le nouveau DC. À noter que cela ne concerne que l'affichage dans l'utilitaire de délégation, toute modification de permission via l'utilitaire est répliquée sur l'ensemble des DC.