PI Services

Le blog des collaborateurs de PI Services

Sharepoint Online : Script de Restauration d'éléments dans la corbeille.

Restaurer des milliers d'éléments supprimés par une personne sur un site Sharepoint peut être une opération fastidieuse, voici donc un script Powershell pour le faire à votre place et vous bénéficierez aussi d'un rapport d'état ("Success", "Failure") pour chacun des éléments.

1 - Prérequis Powershell

Dans un premier temps nous allons avoir besoin d’installer le module Powershell nécessaire à la gestion de Sharepoint, pour ce faire utilisez la commande suivante :

# Installation du Module Powershell
Install-Module SharePointPnPPowerShellOnline

Une fois le module Powershell installé nous allons pouvoir nous connecter au site et atteindre sa corbeille de là nous pourrons faire le filtrage et l’export des éléments.

2 - Script Powershell

# Script to Restore RecycleBin elements
# By : Mathieu ADELAÏDE
# Date : 06/09/2019
# Version : V0

#######################
# Step 0 : Management #
#######################
#region - Step 0
# Folder Definition
# Get Current Path
$currentPath = (Get-Location).Path


$RootFolder = "C:\Temp\Sharepoint"
$LogFolder = "$RootFolder\Log"

#Folder Creation
$AllFolder = $RootFolder, $LogFolder
$AllFolder | foreach {
    $FolderName = $_
    If (!(Test-Path $FolderName)) {
        New-Item $FolderName -ItemType Directory
        }
    }

# Files Definition
$ResultState = "$LogFolder\RestaurationReport.csv"
Add-Content $ResultState -Value "Title;ItemType;Size;ItemState;DirName;DeletedByName;DeletedDate;ID;State"
$logFilePath = "$logFolder\SP_Restauration.log"

#Define Array
$Array = @()

#Config Variables
$SiteURL = "https://MySite.sharepoint.com/sites/Recrutement"
#endregion - Step 0

#######################
# Step 1 : Connection #
#######################
#region - Step 1
#Connect to Sharepoint Online
Try {
    Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)
    }
Catch {
    $tmpError = $_.Exception.message
	Write-Output "The following error occurred: $tmpError" | Add-Content $logFilePath
	Write-Output "The error occurred while we trying to Connect to Sharepoint Online" | Add-Content $logFilePath
    }
#endregion - Step 1

#######################################
# Step 2 : Collection and Restoration #
#######################################
#region - Step 2
# List all Items
$RecycleBin = Get-PnPRecycleBinItem

# Filter result
$FilteredRecycleBin = $RecycleBin.Where({$_.DeletedByName -eq "DUPONT Jean"})
$FilteredRecycleBin | foreach {
    $Title = $_.Title
    $ItemType = $_.ItemType
    $Size = $_.Size
    $ItemState = $_.ItemState
    $DirName = $_.DirName
    $DeletedByName = $_.DeletedByName
    $DeletedDate = $_.DeletedDate
    $ID = $_.id
    $Guid = $ID.Guid
    
    # Restore
    Try {
        Restore-PnPRecycleBinItem -Identity $Guid -Force -ErrorAction Stop
        Write-Output "Successfully Restored File $Title" | Add-Content $logFilePath
        $State = "Succes"
        }
    Catch {
        $tmpError = $_.Exception.message
	    Write-Output "The following error occurred: $tmpError" | Add-Content $logFilePath
	    Write-Output "The error occurred while we trying to Restore $Title" | Add-Content $logFilePath
        $State = "Failed"
        }
    $Array += New-Object psobject -Property @{
        Title = $Title
        ItemType = $ItemType
        Size = $Size
        ItemState = $ItemState
        DirName = $DirName
        DeletedByName = $DeletedByName
        DeletedDate = $DeletedDate
        State = $State
        Id = $id
        }

    # Release variable
    $Title = $null
    $ItemType = $null
    $Size = $null
    $ItemState = $null
    $DirName = $null
    $DeletedByName = $null
    $DeletedDate = $null
    $ID = $null
    $State = $null
    $Guid = $null
    }
#endregion - Step 2

###################
# Step 3 : Export #
###################
#region - Step 3
Try {
    $Array | Export-Csv $ResultState -Encoding UTF8 -NoTypeInformation -Delimiter ";"
    Write-Output "Successfully Export Data" | Add-Content $logFilePath
    }
Catch {
    $tmpError = $_.Exception.message
	Write-Output "The following error occurred: $tmpError" | Add-Content $logFilePath
	Write-Output "The error occurred while we trying to Export Data" | Add-Content $logFilePath
    }
#endregion - Step 3

 

Sharepoint Online : Comment faire un export des éléments dans la corbeille d’un site.

Récemment j’ai été confronté à un sujet délicat, une synchronisation entre Sharepoint Online et un poste client s’est terminée par la suppression de la majorité des dossiers et fichiers du site en question.

Pour pouvoir connaitre l’étendue des dégâts il a fallu faire un audit de ce qui avait été supprimés par erreur.

On pourrait le faire graphiquement via le portail, mais lorsque le nombre d’éléments s’élève à plusieurs milliers ce n’est pas pratique.

Voici une petite astuce pour faire un export au format CSV des éléments dans la corbeille et en option comment les filtrer sur un utilisateur en particulier.

 

1 - Prérequis Powershell

Dans un premier temps nous allons avoir besoin d’installer le module Powershell nécessaire à la gestion de Sharepoint, pour ce faire utilisez la commande suivante :

# Installation du Module Powershell
Install-Module SharePointPnPPowerShellOnline

Une fois le module Powershell installé nous allons pouvoir nous connecter au site et atteindre sa corbeille de là nous pourrons faire le filtrage et l’export des éléments.

2 - Script Powershell

2.1 Version avec tous les éléments de la corbeille.

#Config Variables
$SiteURL = "https://Monsite.sharepoint.com/sites/Recrutement"
$Items = "C:\temp\RecycleBinItems.csv"
Add-Content $Items -Value "Title;ItemType;Size;ItemState;DirName;DeletedByName;DeletedDate"
$Array = @()
 

#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)

# List and export all Items in Recycle bin
$RecycleBin = Get-PnPRecycleBinItem
$RecycleBin | foreach {
    $Title = $_.Title
    $ItemType = $_.ItemType
    $Size = $_.Size
    $ItemState = $_.ItemState
    $DirName = $_.DirName
    $DeletedByName = $_.DeletedByName
    $DeletedDate = $_.DeletedDate

    $Array += New-Object psobject -Property @{
        Title = $Title
        ItemType = $ItemType
        Size = $Size
        ItemState = $ItemState
        DirName = $DirName
        DeletedByName = $DeletedByName
        DeletedDate = $DeletedDate
        }
    }

$Array | Export-Csv $Items -Encoding UTF8 -NoTypeInformation 

2.2 Version avec option de filtrage.

Dans cette version nous faisons la même requête, en ajoutant un filtrage sur un utilisateur bien précis.

#Config Variables
$SiteURL = "https://Monsite.sharepoint.com/sites/Recrutement"
$Items = "C:\temp\RecycleBinItems.csv"
Add-Content $Items -Value "Title;ItemType;Size;ItemState;DirName;DeletedByName;DeletedDate"
$Array = @()
 

#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)

# List and export all Items in Recycle bin
$RecycleBin = Get-PnPRecycleBinItem
$FilteredRecycleBin = $RecycleBin.Where({$_.DeletedByName -eq "DUPONT Jean"})
$FilteredRecycleBin | foreach {
    $Title = $_.Title
    $ItemType = $_.ItemType
    $Size = $_.Size
    $ItemState = $_.ItemState
    $DirName = $_.DirName
    $DeletedByName = $_.DeletedByName
    $DeletedDate = $_.DeletedDate

    $Array += New-Object psobject -Property @{
        Title = $Title
        ItemType = $ItemType
        Size = $Size
        ItemState = $ItemState
        DirName = $DirName
        DeletedByName = $DeletedByName
        DeletedDate = $DeletedDate
        }
    }

$Array | Export-Csv $Items -Encoding UTF8 -NoTypeInformation 

 

Powershell : Credentials Test Function

 

Une petite fonction Powershell pour vous permettre de vérifier des identifiants.

Function Test-Credential {
    PARAM (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)]
        [STRING]$User,
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [STRING]$Pass
        )
    Process {
        # Test Credentials
        Add-Type -AssemblyName system.DirectoryServices.AccountManagement
        $DirectoryService = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
        $Credentials = $DirectoryService.ValidateCredentials($User,$Pass)

        # Return
        If ($Credentials -eq $true) {
            Write-Host "Credentials are correct" -ForegroundColor Green
            }
        Else {
            Write-Host "Credentials are no good" -ForegroundColor Red
            }
        }
    }

 

WSUS Server : Connection Error

Vous venez de déployer un serveur WSUS, finir sa configuration et rattacher les premières machine à se dernier; mais lorsque vous vous rexonnectez sur le serveur afin d'exécuter la console "Windows Server Update Services" vous avez le droit au message d'erreur suivant :

La cause :

Bien souvent cette erreur est lié à IIS, je vous invite donc à exécuter "Internet Information Services (IIS) Manager" puis déroulez le serveur et sélectionnez "Application Pool", ici vous pourrez constater que le "WsusPool" est arrêté.

La solution :

Sélectionnez le et faite "Advanced Settings..." dans le volet de droite, sélectionnez le "Start Mode" pour le passer de "OnDemand" à "AlwaysRunning" puis validez en faisant "OK".

Démarrez le "WsusPool", puis vous pourrez fermer la console IIS.

NB : Tant que le serveur WSUS n'aura pas redémarrez la configuration du IIS ne sera pas effective (vous devrez relancez le "WsusPool" Manuellement à chaque nouvelle connexion au serveur).

Retournez dans la console "Windows Server Update Services" et faites "Reset Server Node"

Enjoy

 

Sharepoint Online : Problème d'accès

Il m'est arrivé il y a peu de temps un cas un peu spécifique, quelqu'un qui bénéficiait de droit "Full Control" sur Sharepoint online se voyait gratfié d'un "Access Denied" à chaque tentative de connexion sur l'url de son site et ceux même en cliquant sur le lien envoyé par Sharepoint lorsqu'on lui attribuait les droits.

La Cause:

En fait cette personne avait bénéficié de droits divers et variés (en accès direct, via l'appartenance à des groupes, via les demandes, Full Control, Edit, Read Only, Limited Access...) car personne parmis les gestionnaires Shrepoint ne parvenait à lui donner les droits.

Par conséquent nous sommes tombé dans un conflit de droits d'accès qui n'a pu être résolu de manière graphique, mais via Powershell.

 

La Solution: 

Afin de solder le problème il a fallu passer par Powershell et supprimer les dépendances de ce compte via la suppression du compte de la "UserInfo List".

Pour ce faire nous avons utilisé les commandes ci-dessous (Attention le module Sharepoint Online pour Powershell est nécessaire):

# Connexion à Sharepoint Online
Connect-SPOService -Url https://monnorganisation.sharepoint.com -Credential "adm-maade@monorganisation.com"

# Vérification des groupes d'appartenance pour mon Utilisateur
$AllUsers = Get-SPOUser -Site https://monnorganisation.sharepoint.com/sites/Toto
$FilterMonUser = $AllUsers.where({$_.loginname -eq "tartanpion@globule.net"})

# Afficher les groupes de mon Utilisateur
$FilterMonUser.groups

# Suppression de mon utilisateur de la UserInfo List
Remove-SPOUser -Site "https://monnorganisation.sharepoint.com/sites/Toto" -LoginName "tartanpion@globule.net"

 

Une fois la commande terminée, j'ai pu réaffecter les bons droits au travers du bon groupe dans l'interface du site Sharepoint.

Info supplémentaires:

https://docs.microsoft.com/en-us/sharepoint/remove-users

 

SCCM : Failed To Run Task Sequence on Hyper-V VM Generation 2

Les messages d'erreur sont parfois frustrant par exemple celui ci-dessous:

En plus de ne pas être des plus explicit, une multitude de cas sont exposé sur internet avec des code erreur divers et variés.

Sans l'accès au log tout devient plus dificile, dans le cas de figure ici présent j'ai voulu déployer une VM de génération 2 sous Hyper-V via un média de boot (clés USB), je pensais que j'allais y passer des heures, mais en fait non 10 minutes tout au plus.

1 - Les causes

Les VM de génération 2 sous Hyper-V, ont par défaut le Secure boot activé.

Le secure boot est une fonctionnalité qui vérifie que le "Boot Loader" est signé par une autorité de confiance de la base UEFI pour empêcher l'exécution non authorisé d'un "Firmware", "OS" ou "Driver"  au démarrage.

Sauf que dans notre cas cela nous empêche de créer la partition pour le "Staging" du WinPe.

2 - Les solutions

désactivez le secure boot.

  1. Lancez Hyper-V management.
  2. Sélectionnez votre VM.
  3. Faites un clic droit "Properties".
  4. Dans "Settings for xxxxxx on xxxxxx" sélectionnez "Security".
  5. décochez la case "Enable Secure boot".
  6. Validez.

 

Créez une partition UEFI.

En cherchant dans les logs on pourrait trouver l'erreur suivante "Unable to find the partition that contains the OS boot loaders. Please ensure the hard disks have been properly partitioned".

  1. Après avoir désativer le sécure boot, démarrez votre VM et faites "F8" durant le démarrage.
  2. Dans l'invite de commande exécutez les commandes suivantes:
    1.  Diskpart (lancez l'utilitaire Diskpart)
    2. List Disk (Listez les disque)
    3. Select disk 0 (sélectionnez le disque 0 qui représente le disque système de la VM)
    4. Clean (permet d'effacer les données)
    5. Convert gpt (permet de convertir le disque)
    6. Create partition efi size=200 (création d'une partition Efi)
    7. Assign letter=s (Assignez un lettre disponible à la partition)
    8. Format quick fs=FAT32 (Formatez la partition)
    9. Create partition msr size=128 (création d'une partition msr)
    10. Create partition primary (création d'une partition Windows)
    11. Assign letter=c (Assignez la lettre C)
    12. Format quick fs=NTFS (Formatez en NTFS)
    13. Exit (quittez Diskpart)
  3. Normalement vous devriez maintenant pouvoir lancer la séquencce de tâche sans encombre.

 

Sécurité : Local Admin Password Solution (LAPS) - Partie 4

Réinitialisation du mot de passe :

Avec un compte faisant partie de "Read_Laps"

Normalement lorsqu'un compte ne fait pas partie du groupe "Reset_Laps", il ne bénéficie pas du droit de réinitialiser le mot de passe, vérifions cela.

Avec le client lourd  :

En effet nous pouvons constater que lorsque l'on essaie de réinitialiser le mot de passe le client affiche au bas de la fenêtre un message "Failed to request password reset"

Avec Powershell :

Avec Powershell le résultat est le même (voir plus explicite), l'utilisateur ne possède pas les droits.

Avec un compte faisant partie de "Reset_Laps"

 

Maintenant nous allons utiliser un compte qui se trouve dans le groupe "Reset_Laps" mais pas dans le groupe "Read_Laps", par conséquent il n'est pas possible de lire le mot de passe mais je devrais pouvoir le réinitialiser. 

Avec le client lourd  :

Cette fois-ci nous pouvons constater que le mot de passe n'est pas lisible, et au bas du client lourd le message "Password reset request was successful".

Avec Powershell :

Conclusion :

Grâce à cette solution gratuite de Microsoft vous pouvez améliorer la sécurité de votre parc informatique en réduisant la portée d'attaque d'une intrusion à l'aide du compte administrateur local.

 

https://www.microsoft.com/en-us/download/details.aspx?id=46899&wt.mc_id=fy16techdays

https://technet.microsoft.com/en-us/library/security/3062591?wt.mc_id=fy16techdays

https://blogs.msdn.microsoft.com/laps/

https://support.microsoft.com/en-us/kb/3062591

[Powershell] Héritage des GPO bloqué

 

Voici quelques commandes Powershell pour trouver les OU dont l'héritage est bloqué:

$GPOBlocked = Get-ADOrganizationalUnit -SearchBase "DC=LAB,DC=Org" -Filter * | Get-GPInheritance | Where-Object {$_.GpoInheritanceBlocked}
$GPOBlocked.count
$GPOBlocked | Select Name,Path

Le DistinguishedName (Path) n'est pas toujours agréable à lire je fais donc une conversion en CanonicalName, ensuite vous pouvez afficher les données à l'écran ou les écrire dans un fichier:

$GPOBlocked | ForEach-Object {
        $Name = $_."Name"
        $Path = $_."Path"
        $CanonicalName = Get-ADOrganizationalUnit -Filter {DistinguishedName -like $Path} -Properties CanonicalName | Select-Object -ExpandProperty CanonicalName
        Write-Output "$Name; $CanonicalName" | Add-Content C:\Temp\GpoInheritanceBlocked.txt
    }

 

 

[Powershell] Excel ne s'enregistre pas via le Planificateur de tâches

Lancer un script Powershell via le planificateur de tâches ne pose aucun problème, en revanche si ce dernier souhaite utiliser Excel (par l'intermédiaire de la commande "new-object -comobject excel.application") on peut rencontrer un problème.

Je dis "on peut" car cela fonctionne si l'on a pas coché la case "Run Whether user is logged on or not", en revanche si cette dernière est cochée, Excel ne sera pas en mesure d'enregistrer le fichier.

Pourquoi ?

Par défaut l'application Excel s'exécute avec le compte "Utilisateur exécutant (The Launching User)", mais si j'utilise une tâche planifiée qui ne nécessite pas que je sois connecté Excel ne parvient pas a déterminer qui fait appel à lui.

Que Faire ?

Simplement en déclarant qui  exécute l'application Excel via la MMC -> Services des composants.

Je vous renvoie au point 2 (Gestion de l'Excel distant) de mon précédent article : [Powershell] Ecrire dans un Excel distant.

Sécurité : Local Admin Password Solution (LAPS) - Partie 3

Vérifications Serveur / Clients

Depuis le serveur

Vérifions l'état du mot de passe du compte Administrateur Local d'une machine cible.

Avec le client lourd  :

Avec Powershell :

Depuis l'Active Directory dans l'onglet éditeur d'attribut 

Depuis le poste Client

Avec un compte faisant partie de "Read_Laps"

Réalisons donc une mise à jour de la stratégie de groupe 

Nous allons pouvoir vérifier l'état du mot de passe du compte admin local.

 

Avec le client lourd  :

Avec Powershell :

Avec un compte faisant partie de "Reset_Laps"

Avec le client lourd  :

Avec Powershell :

 

Test de connexion

Nous testerons donc de nous connecter à la machine avec le nouveau mot de passe

C'est fonctionnel.