Table des matières

Invoke-DailySave - Sauvegarde journalière des dossiers utilisateurs

Il y a des utilisateurs qui prennent l'habitude de garder des documents sur leur poste au lieu de les déposer dans un partage du serveur de fichiers. Cette manière de fonctionner est à proscrire car la sécurité des documents n'est pas garantie. Les administrateurs en charge de l'infrastructure n'ont aucune vision sur leur intégrité ni leur sauvegarde. Les habitudes ayant la vie dure, on est souvent amené à s'adapter à ce genre de comportement.

C'est dans cet optique que j'ai écris ce sript.

Son fonctionnement est assez simple :

Les informations sont affichés en temps réel dans la console avec une coloration syntaxique, information en vert, avertissement en jaune et critique en rouge. Il inclut aussi la possibilité de création d'un rapport succin qui pourra être traité par un autre script renvoyant les informations dans Nagios.

Script principal

# Ligne ajoutée pour activer la coloration syntaxique. A supprimer.
<#
.SYNOPSIS
 
 
    ███████╗██╗  ██╗██╗   ██╗██████╗ ██╗  ██╗ █████╗                 
    ██╔════╝██║  ██║╚██╗ ██╔╝██╔══██╗██║ ██╔╝██╔══██╗                
    ███████╗███████║ ╚████╔╝ ██████╔╝█████╔╝ ███████║                
    ╚════██║██╔══██║  ╚██╔╝  ██╔══██╗██╔═██╗ ██╔══██║                
    ███████║██║  ██║   ██║   ██║  ██║██║  ██╗██║  ██║                
    ╚══════╝╚═╝  ╚═╝   ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝                
                                                                 
                ███████╗██╗   ██╗███████╗████████╗███████╗███╗   ███╗
                ██╔════╝╚██╗ ██╔╝██╔════╝╚══██╔══╝██╔════╝████╗ ████║
                ███████╗ ╚████╔╝ ███████╗   ██║   █████╗  ██╔████╔██║
                ╚════██║  ╚██╔╝  ╚════██║   ██║   ██╔══╝  ██║╚██╔╝██║
                ███████║   ██║   ███████║   ██║   ███████╗██║ ╚═╝ ██║
                ╚══════╝   ╚═╝   ╚══════╝   ╚═╝   ╚══════╝╚═╝     ╚═╝
                                                                 
 
#############################################################################################################
#                                                                                                           #
# Script de sauvegarde du sous-dossier Sauvegarde_Journalière du dossier Documents de chaque utilisateur    #
#                                                                                                           #
# Par Nicolas THOREZ                                                                                        #
#                                                                                                           #
#############################################################################################################

.DESCRIPTION

Lance la sauvegarde du dossier "Documents" de chaque utilisateur.
Un fichier de log journalier est créé et contient toutes les traces de la sauvegarde. (C:\Scripts\DailySave\DailySave-AAAA-MM-JJ.log)

.PARAMETER PC

Indique le ou les noms (séparé par des virgules (,)) des PC à sauvegarder. Si le paramètre n'est pas renseigner, la sauvegarde sera lancée sur tous les PC contenus dans les OU PC Fixes, PC Portables et Direction.

.PARAMETER Verbose

Si le paramètre -Verbose est indiqué, les détails de la sauvegarde seront affichés dans la console.

.PARAMETER Nagios

Si le paramètre -Nagios est indiqué, un fichier de réponse pour un traitement par Nagios sera créé (C:\Scripts\DailySave\SaveReport.dat).

.PARAMETER ExtraInfo

Si le paramètre -ExtraInfo est indiqué, un rapport de copie détaillé sera affiché pour chaque PC traité. 

.EXAMPLE

.\DailySave.ps1
Lance la sauvegarde sur tous les postes.

.EXAMPLE

.\DailySave.ps1 -Verbose
Lance la sauvegarde sur tous les postes en affichant les détails.

.EXAMPLE

.\DailySave.ps1 -PC PC-49,PC-7
Lance la sauvegarde sur les postes PC-49 et PC-7

.NOTES

NOM :      Invoke-DailySave.ps1
AUTEUR :   Nicolas THOREZ
VERSION :  1.5

HISTORIQUE :

0.0     2019-01-30      Début du projet

0.1     2019-01-30      Etude du script existant
                        Création du script de remplacement

0.2     2019-01-31      Ajout du paramètre PC pour ne sauvegarder qu'un PC (utile pour relancer une sauvegarde partiellement ratée)
                        Ajout du paramètre v pour avoir des détails supplémentaires (utile en cas d'analyse d'erreur de sauvegardes)

0.3     2019-01-31      Correction des erreurs lié au profil utilisateur ne correspondant pas au nom d'utilisateur
                        Correction des erreurs lié au blocage du pare-feu sur les postes distants

0.4     2019-01-31      Version de test

1.0     2019-01-31      Version mise en production

1.1     2019-01-31      Ajout du paramètre n pour créer un fichier de réponse pour Nagios (utile pour afficher une alerte dans Nagios en cas d'erreur)

1.2     2019-02-04      Ajout et traitements des codes d'erreurs 9 à 15
                        Ajout d'une suppression des anciens logs (la variable $Old indique l'âge maximum)

1.3     2019-02-05      Ajout de la gestion d'une liste d'ordinateurs pour l'argument -PC

1.3.1   2019-02-06      Modification de la gestion des erreurs en cas d'erreur inconnue

1.4     2019-03-26      Réécriture du mode verbeux

1.5     2019-04-08      Optimisation du code
#>

# Paramètres d'entrée

Param(
    [String[]]$PC='All',
    [Switch]$Verbose,
    [Switch]$Nagios,
    [Switch]$ExtraInfo
)

# Déclaration des variables

If ($PC -eq 'All')
    {
        $ListPC = (Get-ADComputer -SearchBase "OU=PC Fixe,OU=Ordinateurs,DC=TEST,DC=LOCAL" -Filter *).Name
        $ListPC += (Get-ADComputer -SearchBase "OU=PC Portable,OU=Ordinateurs,DC=TEST,DC=LOCAL" -Filter *).Name
        $ListPC += (Get-ADComputer -SearchBase "OU=Ordinateurs,OU=Direction,DC=TEST,DC=LOCAL" -Filter *).Name
    }
Else
    {
        $ListPC = $PC
    }
$CheckDate = Get-Date -UFormat %Y-%m-%d
$Path = "C:\Scripts\DailySave"
$LogFile = "$Path\DailySave-$CheckDate.log"
$ScriptName = "Invoke-DailySave.ps1"
$UserProfilePath = ''
$PCSaved = 0
$PCOffline = 0
$PCWithError = 0
$PCWithWarn = 0
$TotalPC = 0
$Old = 7
$Domaine = "TEST"
$FQDN = "test.local"
$Partage = "\\SERVEUR\Partage_utilisateur"
$SavedPath = "Documents"
$Report = "$Path\SaveReport.dat"


# Déclaration des fonctions
Function Add-Log()
    {
        Param
            (
                [string]$Type,
                [string]$Message,
                [switch]$Line
            )

        If ($Line)
            {
                Write-Host "----------------------------------------------------------------------"
                Add-Content -Path $LogFile -Value "----------------------------------------------------------------------"
            }
        Else
            {
                $CheckTime = Get-Date -Format G
                If ($Verbose)
                    {
                        Switch($Type)
                            {
                                "INFO"
                                    {
                                        $Color = "green"
                                    }
                                "WARN"
                                    {
                                        $Color = "yellow"
                                    }
                                "CRIT"
                                    {
                                        $Color = "red"
                                    }
                                "UNKN"
                                    {
                                        $Color = "magenta"
                                    }
                                default
                                    {
                                        $Color = "cyan"
                                    }
                            }
                        Write-Host "$CheckTime     " -NoNewline
                        Write-Host "$type     " -NoNewline -ForegroundColor $Color
                        Write-Host $Message
                    }
                Add-Content -Path $LogFile -Value "$CheckTime     $Type     $Message"
            }
    }


Function Get-ConnectedUser($PC)
    {
        $Name = (Get-WmiObject -ComputerName $PC -Class Win32_ComputerSystem -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -InformationAction SilentlyContinue).UserName
        If ($Name -notlike "$Domaine*")
            {
                
                $Name = "n/a"
                Add-Log -Type "WARN" -Message "Aucun utilisateur connecté."
            }
        Else
            {
                $Name = $Name.Replace("$Domaine",'')
                Add-Log -Type "INFO" -Message "Utilisateur $Name."
            }
        Return $Name
    }

Function Get-UserLocalPath
    {
        Param(
            [String]$PC,
            [String]$User
        )

        # On récupère le SID de l'utilisateur
        $SID = ([wmi]"win32_userAccount.Domain='$Domaine',Name='$User'").SID

        # ON interroge le PC distant pour connaître le chemin du profil utilisateur
        $LocalPath = (Get-WmiObject -ComputerName $PC -Class Win32_userprofile | Where-Object {$_.SID -eq $SID}).LocalPath

        # On renvoie la valeur
        Return $LocalPath
    }

Function Get-PCOnlineStatus($PC)
    {
        # On interroge le PC via Get-WmiObject plutôt que par ping pour éviter les erreurs de pare-feu
        $OnlineTest = (Get-WmiObject -ComputerName $PC -Class Win32_ComputerSystem -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -InformationAction SilentlyContinue).Domain
        If ($OnlineTest -eq $FQDN)
            {
                Add-Log -Type "INFO" -Message "PC Online."
                Return $true
            }
        Else
            {
                Add-Log -Type "WARN" -Message "PC Offline."
                Return $false
            }
    }

Function Run-Save
    {
        Param
            (
                [string]$PC,
                [String]$User
            )

        # Récupération du chemin du profil utilisateur et formatage du chemin pour accès réseau
        $UserProfilePath = Get-UserLocalPath -PC $PC -User $User
        $UserProfilePath = $UserProfilePath.Replace(":","$")
        
        # Définitions des dossiers pour la copie
        $Source = "\\$PC\$UserProfilePath\$SavedPath"
        $Destination = "$Partage\$User\Sauvegardes_Journalieres"

        # Copie des fichiers
        If ($ExtraInfo)
            {
                ROBOCOPY $Source $Destination /COPYALL /MIR /ZB /R:0 /W:0
            }
        Else
            {
                ROBOCOPY $Source $Destination /COPYALL /MIR /ZB /R:0 /W:0 > $Null
            }
    }

# Création du fichier log et suppression des anciens logs
If (Test-Path $LogFile)
    {
        Add-Log -Line
        Add-Log -Type "INFO" -Message "Fichier log existant, inscription des nouvelles informations à la suite."
        Add-Log -Type "INFO" -Message "Début de la sauvegarde."
        Add-Log -Line
    }
Else
    {
        New-Item -Path $LogFile > $null
        Add-Log -Type "INFO" -Message "Création du fichier log."
        Get-ChildItem –Path $Path –Recurse | Where-Object CreationTime –lt (Get-Date).AddDays(-$Old) | Where-Object Name -ne $ScriptName | Remove-Item -Force > $null
        Add-Log -Type "INFO" -Message "Suppression des logs vieux de $Old jours."
        Add-Log -Type "INFO" -Message "Début de la sauvegarde."
        Add-Log -Line
    }

# Traitement
Foreach ($PC in $listPC)
    {
        $TotalPC += 1
        Add-Log -Type "INFO" -Message "Début de traitement de $PC."
        
        # On teste si la machine est en ligne
        $OnlineStatus = Get-PCOnlineStatus($PC)

        # Si oui on continue le traitement
        If ($OnlineStatus -eq $true)
            {
                # On teste si un utilisateur est connecté
                $User = Get-ConnectedUser($PC)

                # Si oui, on lance la sauvegarde
                If ($User -notlike "n/a")
                    {
                        $CheckTime = Get-Date -UFormat "%Y-%m-%d %H:%M:%S"
                        If ($Verbose)
                            {
                                Write-Host "$CheckTime     " -NoNewline
                                Write-Host "INFO     " -NoNewline -ForegroundColor Green
                                Write-Host "Début de la sauvegarde de $PC."
                            }
                        Add-Content -Path $LogFile -Value "$CheckTime     INFO     Début de la sauvegarde de $PC."
                        Add-log -Type "INFO" -Message "Début de la sauvegarde de $PC."
                        Run-Save -PC $PC -User $User
                        Switch ($LASTEXITCODE)
                            {
                                0
                                    {
                                        Add-Log -Type "INFO" -Message "Source et destination identique, sauvegarde non nécessaire."
                                        $PCSaved += 1
                                    }
                                1
                                    {
                                        Add-Log -Type "INFO" -Message "Tous les fichiers ont été copiés avec succès."
                                        $PCSaved += 1
                                    }
                                2
                                    {
                                        Add-Log -Type "WARN" -Message "Des fichiers étrangers ont été trouvés sur la destination. Sauvegarde abandonnée."
                                        $PCWithWarn += 1
                                    }
                                3
                                    {
                                        Add-Log -Type "INFO" -Message "Certains fichiers ont été copiés. Des fichiers supplémentaires étaient présents. Aucun échec n'a été rencontré."
                                        $PCSaved += 1
                                    }
                                4
                                    {
                                        Add-Log -Type "WARN" -Message "Certains fichiers ou répertoires incompatibles ont été détectés. Le ménage peut être nécessaire."
                                        $PCWithWarn += 1
                                    }
                                5
                                    {
                                        Add-Log -Type "WARN" -Message "Certains fichiers ont été copiés. Certains fichiers étaient incompatibles. Aucun échec n'a été rencontré."
                                        $PCWithWarn += 1
                                   }
                                6
                                    {
                                        Add-Log -Type "WARN" -Message "Il existe des fichiers supplémentaires et des fichiers incompatibles. Aucun fichier n'a été copié et aucun échec n'a été rencontré. Cela signifie que les fichiers existent déjà dans le répertoire de destination."
                                        $PCWithWarn += 1
                                    }
                                7
                                    {
                                        Add-Log -Type "WARN" -Message "Les fichiers ont été copiés, une incompatibilité de fichier était présente et des fichiers supplémentaires étaient présents."
                                        $PCWithWarn += 1
                                    }
                                8
                                    {
                                        Add-Log -Type "CRIT" -Message "Plusieurs fichiers n'ont pas été copiés."
                                        $PCWithError += 1
                                    }
                                9
                                    {
                                        Add-Log -Type "WARN" -Message "Un ou plusieurs fichiers ont été copiés avec succès, mais certains fichiers ou répertoires ne peuvent pas être copiés."
                                        $PCWithWarn += 1
                                    }
                                10
                                    {
                                        Add-Log -Type "CRIT" -Message "Plusieurs fichiers n'ont pas été copiés et des fichiers étrangers ont été trouvés sur la destination."
                                        $PCWithError += 1
                                    }
                                11
                                    {
                                        Add-Log -Type "CRIT" -Message "Quelques fichiers ont été copiés mais plusieurs fichiers n'ont pas été copiés et des fichiers étrangers ont été trouvés sur la destination."
                                        $PCWithError += 1
                                    }
                                12
                                    {
                                        Add-Log -Type "CRIT" -Message "Plusieurs fichiers n'ont pas été copiés et certains fichiers ou répertoires incompatibles ont été détectés."
                                        $PCWithError += 1
                                    }
                                13
                                    {
                                        Add-Log -Type "CRIT" -Message "Quelques fichiers ont été copiés mais plusieurs fichiers n'ont pas été copiés et certains fichiers ou répertoires incompatibles ont été détectés."
                                        $PCWithError += 1
                                    }
                                14
                                    {
                                        Add-Log -Type "CRIT" -Message "Plusieurs fichiers n'ont pas été copiés, des fichiers étrangers ont été trouvés sur la destination et certains fichiers ou répertoires incompatibles ont été détectés."
                                        $PCWithError += 1
                                    }
                                15
                                    {
                                        Add-Log -Type "CRIT"  -Message "Quelques fichiers ont été copiés mais plusieurs fichiers n'ont pas été copiés, des fichiers étrangers ont été trouvés sur la destination et certains fichiers ou répertoires incompatibles ont été détectés."
                                        $PCWithError += 1
                                    }
                                16
                                    {
                                        Add-Log -Type "CRIT" -Message "Erreur grave. Aucun fichier n'a été copié. Soit une erreur d'utilisation, soit une erreur due à des privilèges d'accès insuffisants sur les répertoires source ou de destination."
                                        $PCWithError += 1
                                    }
                                Default
                                    {
                                        If ($LASTEXITCODE)
                                            {
                                                $ExitCode = $LASTEXITCODE
                                            }
                                        Else
                                            {
                                                $ExitCode = "inconnu"
                                            }
                                        Add-Log -Type "UNKN" -Message "Erreur inconnue. Code d'erreur $ExitCode."
                                        $PCWithError += 1
                                    }

                            }
                        Add-Log -Type "INFO" -Message "Fin de la sauvegarde de $PC."
                    }
            }
        Else
            {
                $PCOffline += 1
            }
    }

# Fin de la sauvegarde
Add-Log -Line
Add-Log -Type "INFO" -Message "Fin de la sauvegarde."
Add-Log -Line
Add-Log -Type "INFO" -Message "Total des PC traités : $TotalPC."
Add-Log -Type "INFO" -Message "Total des PC sauvegardés : $PCSaved."
If ($PCOffline -gt 0)
    {
        Add-Log -Type "WARN" -Message "Total des PC hors-ligne : $PCOffline."
    }
Else
    {
        Add-Log -Type "INFO" -Message "Total des PC hors-ligne : $PCOffline."
    }
If ($PCWithWarn -gt 0)
    {
        Add-Log -Type "WARN" -Message "Total des sauvegardes avec des erreurs : $PCWithWarn."
    }
Else
    {
        Add-Log -Type "INFO" -Message "Total des sauvegardes avec des erreurs : $PCWithWarn."
    }
If ($PCWithError -gt 0)
    {
        Add-Log -Type "CRIT" -Message "Total des sauvegardes en échec : $PCWithError."
    }
Else
    {
        Add-Log -Type "INFO" -Message "Total des sauvegardes en échec : $PCWithError."
    }

# Création d'un fichier de réponse pour un traitement par Nagios
If ($Nagios)
    {
        # On vérifie la présence d'un ancien rapport et on le détruit
        If (Test-Path -Path $Report)
            {
                Remove-Item -Path $Report -Force
            }

        # On crée un rapport vierge
        $TempVar = @{}
        $TempVar | Export-Csv -Path $Report -Delimiter ';'

        # On ajoute les valeurs pour Nagios
        $ReportData = Import-Csv -Path $Report -Delimiter ';'
        $ReportData | Add-Member -MemberType NoteProperty -Name "Total" -Value $TotalPC
        $ReportData | Add-Member -MemberType NoteProperty -Name "Saved" -Value $PCSaved
        $ReportData | Add-Member -MemberType NoteProperty -Name "Offline" -Value $PCOffline
        $ReportData | Add-Member -MemberType NoteProperty -Name "Warning" -Value $PCWithWarn
        $ReportData | Add-Member -MemberType NoteProperty -Name "Critical" -Value $PCWithError
        $ReportData | Export-Csv -Path $Report -Delimiter ';'

        # Affichage des logs
        Add-Log -Line
        Add-Log -Type "INFO" -Message "Création du fichier de réponse pour Nagios."
    }

Script d'interprétation pour Nagios

##############################################################################
#                                                                            #
# Vérification du fichier de réponse (SaveReport.dat) de DailySave.ps1       #
#                                                                            #
# Par Nicolas THOREZ                                                         #
#                                                                            #
##############################################################################

$Report = Import-Csv -Path 'C:\Scripts\DailySave\SaveReport.dat' -Delimiter ';'

[int]$Total = $Report.Total
[int]$Saved = $Report.Saved
[int]$Offline = $Report.Offline
[int]$Warning = $Report.Warning
[int]$Critical = $Report.Critical

$Output = ''
$ExitCode = 3

If ($Critical -gt 0)
    {
        $Output = "Critical"
        $ExitCode = 2
    }
Elseif ($Warning -gt 0)
    {
        $Output = "Warning"
        $ExitCode = 1
    }
Elseif (($Warning -eq 0) -and ($Critical -eq 0))
    {
        $Output = "OK"
        $ExitCode = 0
    }
Else
    {
        echo "Unknown - Error in SaveReport.dat, please check"
        exit $ExitCode
    }

echo "$Output - Total : $Total, Saved : $Saved, Offline : $Offline, Warning : $Warning, Error : $Critical"
exit $ExitCode

Nicolas THOREZ 2019/02/19 21:43