PI Services

Le blog des collaborateurs de PI Services

Quest RUM : Diagnostic de processings "erratiques"

Bonjour à tous !

Nous allons nous pencher aujourd'hui plus profondément dans les entrailles du produit Quest Migration Manager for Active Directory, notamment avec l'étude d'un cas problématique et de sa résolution.

Le cas que nous allons étudier ci-dessous est susceptible d'être peu rencontré, mais il n'en est pas moins "vicieux". J'espère donc que ce billet n'en sera que d'autant plus utile.

Les symptômes

  • Vous avez migré un ensemble d'utilisateurs à l'aide de QMM.
  • Vous avez lancé des processings avec RUM sur un ensemble de postes.
  • Sur certains postes, le processing se passe correctement, sur d'autres non.
  • Aucune erreur particulière n'est rapportée dans la console RUM, le seul signe visible est qu'aucun élément n'est mis à jour lors du processing (Aucun fichier, aucune clé registre, ...).
  • Si vous remigrez les utilisateurs à l'aide d'un autre serveur Quest, et si vous effectuez de nouveau des processings sur les précédents postes avec ce nouveau serveur, vous pourrez constater que certains postes sont correctement processés cette fois-ci, et que d'autres ne le sont plus.

Analyse du problème

La console RUM de Quest ne nous indiquant aucune erreur particulière lors des processings sur les postes en question, c'est donc vers les fichiers de logs que nous allons devoir nous tourner.

Le premier fichier de log nous intéressant va être celui qui remonte les détails du processing d'un poste.

Vous pourrez trouver, pour une instance RUM, tous les fichiers de processing de l'ensemble des postes dans le dossier suivant :

C:\Program Files (x86)\Common Files\Aelita Shared\Migration Tools\Resource Updating\Logs\

Allez dans le dossier correspondant au poste voulu et ouvrez le fichier de log correspondant au processing "en échec".

A première vue, il n'y a rien d'inhabituel dans le fichier de log, à part que l'on remarque bien qu'il n'y a aucun élément mis à jour lors de processing. Cependant, il faut bien regarder le Warning présent à la deuxième ligne.

Nous y trouvons l'avertissement suivant, à savoir que Le fichier INI contient une ligne invalide :

INI file contains invalid line '9;User_XXX;106572;;User;XXX@sourcedomain.local;User;XXX@targetdomain.local;1'. Escape to next domain pair

Avant de pouvoir analyser la ligne en question, il nous faut donc parcourir le fichier INI en question. Ce fichier correspond en fait au fichier de mappage des utilisateurs que RUM utilise lors de ces processings. A chaque processing, un nouveau fichier de mappage est généré selon les utilisateurs migrés par l'instance QMM. Tous les fichiers de mappage générés se trouvent à l'intérieur du dossier suivant :

C:\Program Files (x86)\Common Files\Aelita Shared\Migration Tools\Resource Updating\Configs\

Si l'on prend le fichier INI cité précédemment, voici ce que l'on trouve à l'intérieur de celui-ci :

La 1ère ligne représente une ligne correctement formée, à savoir que tous les champs du fichier de mappage sont séparés par un point-virgule.

La 2ème ligne surlignée correspond à la ligne citée en erreur. Quand on effectue une analyse de celle-ci, on remarque qu'elle contient plus de point-virgule que pour une ligne correctement formée. Je vous remets le message d'erreur avec le surlignage pour bien mettre en évidence les anomalies :

INI file contains invalid line '9;User_XXX;106572;;User;XXX@sourcedomain.local;User;XXX@targetdomain.local;1'. Escape to next domain pair

En l'occurrence, après une vérification dans les propriétés de l'utilisateur dans l'AD Source, il s'avère que le SamAccountName de l'utilisateur en question est bien "User_XXX" mais que l'UPN de celui-ci est "User;XXX" !

Le fichier de mappage va donc inclure d'office ces points virgules supplémentaires ce qui va causer l'erreur citée précédemment, qui n'est donc pour le moment pas explicitement gérée par Quest dans le sens ou, précédemment, la migration de cette utilisateur via QMM n'a donné lieu à aucun avertissement.

La conséquence de cette erreur nous est donnée dans le message d'avertissement, à savoir Escape to next domain pair.

Une domain pair dans le jargon de Quest correspond à une paire domaine source - domaine cible déclarée dans Quest. Si vous avez plusieurs domaines distincts à migrer avec QMM, vous devez déclarer une domain pair pour chacun d'entre eux. Ce qu'indique le message d'erreur, c"est que toute la partie du fichier de mappage qui est après la ligne en erreur est ignorée, jusqu'à la prochaine domain pair rencontrée comme schématisé dans l'image ci-dessous :

C'est ce fait qui explique pourquoi certains postes n"étaient pas procéssés, tandis que d'autres l'étaient correctement. C'est également ce fait qui explique les disparités observées dans les processings effectués à l'aide d'une autre instance de Quest RUM. En effet, comme le fichier de mappage des comptes est re-généré à chaque processing, l'ordre des comptes à l'interieur de celui-ci diffère suivant les instances, c'est donc pourquoi le processing était de nouveau fonctionnel pour certains postes, et pour d'autres non avec une nouvelle instance.

Solution

La résolution du problème se présente sous la forme suivante :

  • Modifiez l'utilisateur en erreur dans l'AD source et dans l'AD cible afin de retirer le caractère point-virgule du nom d'utilisateur
  • Effectuez une nouvelle migration de l'utilisateur (un merge dans le cas présent car l'utilisateur est déjà existant dans l'AD cible) avec QMM car RUM se base sur les utilisateurs migrés avec QMM pour construire son fichier de mappage des utilisateurs (voir la section Addendum pour les exceptions)
  • Effectuez de nouveau un processing des postes en erreurs, celui-ci devrait être désormais fonctionnel, avec notamment le message suivant dans le fichier de log des postes procéssés :

Addendum

Il nous faut ajouter une petite précision, car nous avons précisé au paragraphe précédent que RUM se basait sur la liste des utilisateurs migrés avec QMM pour construire son fichier de mappage des utilisateurs.

Ce n'est plus systématiquement vrai avec la version 8.13 de QMM et RUM, car nous pouvons indiquer à RUM lors des processings de se baser directement sur le SIDHistory de l'utilisateur cible (SID de l'utilisateur source) dans l'AD cible pour effectuer la correspondance des comptes utilisateurs sur les machines migrées.

Vous trouverez l'option lors de l'étape suivante du processing :

Quest RUM : Script de désinstallation des agents

Bonjour à tous !

Lors d'une migration effectuée à l'aide du produit Quest Migration Manager for Active Directory, vous vous retrouvez souvent en fin de migration avec un nombre conséquent d'agents Quest à désinstaller de tous les postes migrés.

Ce que vous pouvez très bien faire à l'aide de Quest RUM (Resource Updating Manager) via l'option Cleanup :

Mais il peut arriver qu'entre temps que certains postes migrés soient devenus indisponibles. Plutôt que de refaire une passe numéro X pour désinstaller les agents, nous allons voir comment automatiser la désinstallation de ceux-ci.

Prérequis

Pour ce faire, il vous faut :

  • Un serveur sur lequel sera placé un partage accessible par toutes les machines ayant un agent Quest
  • Un éditeur de script Powershell
  • Une console d'édition de stratégies de groupe

Le partage de fichiers

Afin que notre script de désinstallation puisse écrire les logs nécessaire, il doit avoir à disposition un partage sur lequel les machines concernées puissent avoir un droit d'écriture de fichiers.

Par défaut, le script va utiliser le nom de partage QuestUninstallLogs$ mais vous pourrez le modifier via les arguments du script. Nous rajoutons un à la fin du nom du partage afin de masquer celui-ci.

Il faut ajouter sur le partage (droit de partage) le droit en modification pour le groupe Everyone.

Il faut ajouter sur le dossier (droit NTFS) le droit en modification et en écriture pour le groupe Authenticated Users

Une fois cela fait, nous pouvons passer à l'édition du script.

Le script

Arguments

Le script s'appelle à l'aide des arguments suivants :

  • -Pole (obligatoire) : Nom du dossier de 1er niveau des logs
  • -Site (obligatoire) : Nom du dossier de 2ème niveau des logs
  • -LogServer (obligatoire) : Nom du serveur devant recevoir les logs
  • -LogShare (facultatif) : Nom du partage devant recevoir les logs

Sortie

Deux types de fichiers de logs vont être générés en sortie dans le chemin indiqué en paramètre du script :

  • XXX_SUMMARY.log : Fichier résumant toutes les désinstallations effectuées par le script
  • COMPUTERS\PC-XXX.log : Fichier détaillant le déroulement de la désinstallation de l'agent Quest pour le poste concerné

Code

#-------------------#
# Script Parameters #
#-------------------#
# Description
# The Uninstall_Quest_Agent script is uninstalling the Quest RUM Agent
# 
# How to use it 
#
# .\Uninstall_Quest_Agent.ps1 -Pole "POLE_IDF" -Site "Site_1" -LogServer "LOG_SERVER_1" -LogShare "QuestUninstallLogs$"
#
# -Pole = Targeted Pole
# -Site = Targeted Site
# -LogServer = Server on which the uninstall logs will be stored
# -LogShare (not mandatory) = Network Share on which the uninstall logs will be stored

########## PARAMETERS ##########

[CmdletBinding(DefaultParametersetName="Common")]
param(
	
	#Attribute field to check
	[Parameter(Mandatory=$true,Position=1)][string] $Pole = $null,
	
	#Attribute field to check
	[Parameter(Mandatory=$true,Position=2)][string] $Site = $null,
	
	#Attributes fields to check
	[Parameter(Mandatory=$false,Position=3)][string] $LogServer = $null,

	#Attributes fields to check
	[Parameter(Mandatory=$false,Position=4)][string] $LogShare = "QuestUninstallLogs$"
	
	)

########## SCRIPT EXECUTION ##########

# Check if script has been executed (log file already exists)

# Define logfile path and name
# Detailed logs
$LogFileDirectoryPath = "\\" + $LogServer + "\" + $LogShare + "\" + $Pole + "\" + $Site + "\COMPUTERS"
$LogFileName = $env:computername + ".log"
$LogFilePath = $LogFileDirectoryPath + "\" + $LogFileName

# Summary logs
$SummaryLogFileDirectoryPath = "\\" + $LogServer + "\" + $LogShare + "\" + $Pole + "\" + $Site
$SummaryLogFileName = $Pole + "_" + $Site + "_" + "SUMMARY.log"
$SummaryLogFilePath = $SummaryLogFileDirectoryPath + "\" + $SummaryLogFileName

# End script if Quest Agent Service is missing
$Service = Get-Service QsRUMAgent -ErrorAction SilentlyContinue

if($Service -eq $null)
{
    Exit
}

########## LOG FILES MANAGMENT ##########

# Checking if directories need to be created

# Check if pole directory exists
$PoleFilePath = "\\" + $LogServer + "\" + $LogShare + "\" + $Pole

# If directory don't exist, we create it
if(!(Test-Path $PoleFilePath))
{
    New-Item $PoleFilePath -ItemType Directory
}

# Check if site directory exists
$SiteFilePath = "\\" + $LogServer + "\" + $LogShare + "\" + $Pole + "\" + $Site

# If directory don't exist, we create it
if(!(Test-Path $SiteFilePath))
{
    New-Item $SiteFilePath -ItemType Directory
}

# Check if COMPUTERS directory exists
$ComputersFilePath = "\\" + $LogServer + "\" + $LogShare + "\" + $Pole + "\" + $Site + "\COMPUTERS"

# If directory don't exist, we create it
if(!(Test-Path $ComputersFilePath))
{
    New-Item $ComputersFilePath -ItemType Directory
}

########## UNINSTALLING AGENT ##########

$Date = (Get-Date).ToString()
$ComputerName = "\\" + $env:computername

# Log
$ToWrite = $Date + " - " + "Begining of Quest agent uninstallation"
Add-Content $LogFilePath $ToWrite

# Stopping and Deleting Quest Migration Manager RUM Agent Service
$Service = Get-Service QsRUMAgent -ErrorAction SilentlyContinue

#If service exists
if($Service)
{
    Stop-Service $Service.Name
    # Log
    $ToWrite = $Date + " - " + "Quest Migration Manager RUM Agent Service - Stopped"
    Add-Content $LogFilePath $ToWrite

    sc.exe $ComputerName delete $Service.Name
    # Log
    $ToWrite = $Date + " - " + "Quest Migration Manager RUM Agent Service - Deleted"
    Add-Content $LogFilePath $ToWrite

    # Log Général
    $ToWrite = $Date + " - " + $env:computername + " - " + "Agent Deleted"
    Add-Content $SummaryLogFilePath $ToWrite
}

else
{
    # Log
    $ToWrite = $Date + " - " + "ERROR : Quest Migration Manager RUM Agent Service not found !"
    Add-Content $LogFilePath $ToWrite
}

# Deleting Quest Migration Manager RUM Agent Directory
$QuestAgentDirectory = "$env:SystemRoot\Quest Resource Updating Agent"

#If directory exists
if(Test-Path $QuestAgentDirectory)
{
    Remove-Item $QuestAgentDirectory -Recurse
    # Log
    $ToWrite = $Date + " - " + "Quest Migration Manager RUM Agent Directory - Deleted"
    Add-Content $LogFilePath $ToWrite
}

else
{
    # Log
    $ToWrite = $Date + " - " + "ERROR : Quest Migration Manager RUM Agent Directory not found !"
    Add-Content $LogFilePath $ToWrite
}

# Log
$ToWrite = $Date + " - " + "End of Quest agent uninstallation"
Add-Content $LogFilePath $ToWrite

Utilisation du script

Vous pouvez utiliser ce script directement sur le poste concerné ou par GPO pour une désinstallation groupée.

Par GPO, le script s'utilise dans la section Powershell dans les Paramètres Ordinateurs de la console de configuration des GPO.

Le chemin pour y accéder est Configuration Ordinateur > Stratégies > Paramètres Windows > Arrêt > Scripts Powershell

A minima, le script doit s'appeller à l'aide des paramètres suivants :

Au fur et à mesure que le script est exécuté, vous devriez voir le dossier des logs se remplir, que ce soit pour le log SUMMARY qui résume toutes les désinstallations ou pour les logs individuels par machine.

Erreur de mises à jour WSUS

Contexte :

Le problème décrit ci-dessous a été rencontré dans le cadre de la mise en place d’un serveur WSUS sous Windows Server 2012 R2.

Problème rencontré :

Suite à la mise en place de WSUS, toutes les installations de mises jours depuis les serveurs étaient en échec ainsi que la remontée des rapports.

image

Un code d’erreur apparait : 80244016.

image

Analyse :

Pour obtenir plus de détails sur ce type de problème, l’outil WireShark peut être utilisé depuis un « client » WSUS afin d’analyser les flux réseau entre le client et le serveur WSUS.

Il faut ensuite filtrer les résultats obtenus sur les protocoles http et https.

La traceWireShark permet de mettre en évidence qu’une erreur de type « http/1.1 400 Bad Request » est déclenchée.

image

En observant en détail la requête http, on observe que le client WSUS tente de récupérer un fichier « .cab » sur le serveur WSUS avec le nom complet du serveur WSUS (FQDN).

La requête essai de communiquer avec l’host : hote.mondomaine.com.

image

Explication :

Le problème est dû à une incohérence entre la configuration du site IIS du serveur WSUS et l’URL positionnée sur les clients WSUS.

Les deux valeurs doivent être identiques (soit le nom NetBIOS du serveur WSUS, soit son FQDN).

Pour trouver le paramètre erroné il suffit de vérifier tous les endroits où l’URL est rentrée.

Le premier point à vérifier est la configuration de la stratégie de groupe positionnant l’URL WSUS sur le client.

Pour vérifier il suffit de lancer la commande « Rsop.msc » sur le client WSUS, puis d’aller dans le dossier « Windows Update » dans la section Configuration Ordinateur / Modèle d’administration / Composant Windows :

image

L’URL est positionnée dans le paramètre « Spécifier l’emplacement intranet du service de mise à jour Microsoft »

image

Il ne reste plus qu’à vérifier la configuration de IIS. Une fois la page de management est ouverte il faut se mettre sur le site « WSUS Administration ».

Puis aller dans le paramètre « Bindings »

image

Et d’éditer l’adresse http.

image

Dans notre exemple, la GPO demande au client d’utiliser le FQDN du serveur alors que le site IIS accepte uniquement les requêtes adressées au nom court.

image

Résolution :

Pour résoudre le problème, il suffit de modifier le « host name » du site Web IIS par le FQDN du serveur.

Suite au redémarrage de IIS, les clients WSUS peuvent appliquer les mises à jour avec succès.

image

On peut vérifier en relançant une capture de trames et on s’aperçoit bien que le serveur WSUS répond avec le code 200, ce qui signifie que la requête a été traitée avec succès.

image

Recupération du code retour lors d'une exécution d'un script PoSh sous Orchestrator

INTRODUCTION

Orchestrator (SCO) est une solution d'automatisation de processus (Run Book Automation – RBA) pour l'orchestration. Un runbook contient plusieurs activités connectées sous forme d'un workflow. Pour bien définir le chemin d'un workflow, il faudra savoir récupérer le code retour d'une activité. Par défaut, le code retour d'une activité (Initialize Data) est définit comme dans l'exemple ci-dessous:

Pour afficher l'écran ci-dessus, double-cliquer sur le lien entre deux activités. On cochant la bonne case, nous pourrons définir le chemin du workflow en cas de "success","warning" ou "failed". Mais, cela devient plus compliqué si nous souhaitons récupérer un code retour d'une application ou d'un script PoSh. Dans l'exemple ci-dessous, nous allons récupérer le code retour d'un script PoSh lancé par Orchestrator. 

Exemple

Nous allons créer un runbook qui lancera un script PoSH avec deux codes retour possible  : 100 et 101. Les deux codes retour doivent emprunter deux chemins diférents. Le runbook resemble à:

L'activité "Run Program" lancera le script. L'activité "Return code" en PoSh récupérera le code retour et le mettra dans une variable qu'on traitera par la suite. Attention: dans l'exemple le script PoSh est lancé par un fichier .bat.

Le script powershell contient les codes suivants: 

$server = "127.0.0.1"
$ping = new-object System.Net.NetworkInformation.Ping
$ReponsePing = $ping.Send($server)


if ($ReponsePing.status –eq “Success”) 
{
     return 100
}
else 
{
     return 101
}

 

Le script .bat contient les codes suivants:

powershell .\ping.ps1

Paramétrage des activités

Dans l'activité "Run Program":

Sélectionner "Command execution" puis entrer le nom du serveur, le chemin du fichier .bat et le répertoire de travail. 

Après l'exécution de l'activité, le code retour (100 ou 101) sera publié dans une variable publiée automatiquement. Le nom de la variable est Pure Output et on constate bien à la fin le code retour 100. 

Nous allons récupérer ce code retour sans une autre variable qu'on nommera $pureOutput grâce à un script PoSh définit dans l'activité "Return code".

Assurez vous que la variable est bien publiée dans "Published Data". Grâce à cette variable nous pourrons définir correctement le chemin à prendre si le code retour est 100 ou 101. Pour le faire, double-cliquer sur le connecteur (link) et ajouter la variable "pureOutput" et son contenu.

Faire la même chose sur le connecteur (link) pour le code retour 101.

 

 

 

 

 

 

 

 

Installation et configuration d'un relais SMTP sous IIS

Introduction 

Les applications qui sont accessibles sur internet sont bien souvent installées dans un environnement de DMZ pour des raisons de sécurité. Des fois, ces applications nécessitent un envoi de mail sans pour autant passer directement par le serveur SMTP mais en faisant un rebond sur un relais SMTP (encore une fois pour des raisons de sécurité). Nous allons installer et configurer ce relais SMTP sous IIS.

Prérequis

  • 2 serveurs Windows (2, pour du load-balancing)
  • 1 VIP F5 avec les deux serveurs configurés dans le pool (Le F5 gère automatiquement le load-balancing)
  • l'ip du serveur SMTP

Installation IIS ( A faire sur les deux serveurs)

Sélectionner « Web Server (IIS) » et cliquer sur « Next ».

Laisser les fontionalités par défaut et faire un "Next" pour démarrer l'installation de IIS.

Configuration du relais SMTP (A faire sur les deux serveurs)

1. Lancer « Internet Information Services 6.0 Manager » et faire un clic droit sur « SMTP Virtual Server #1» puis sélectionner « Properties ».

2. Dans l’onglet « Access », cliquer sur « Authentification » et sélectionner « Anonymous »

3. Dans l’onglet « Delivery » , cliquer sur « Outbound Security».

4. Cliquer sur « Advanced » et entrer le Fully-Qualified domain name (Nom du serveur IIS) et dans Smart host, entrer l’addresse du serveur SMTP.

La configuration du relais SMTP est terminé.

Autoriser une application à envoyer des e-mails sur le relais SMTP (A faire sur les deux serveurs)

Pour autoriser une application à envoyer des e-mail en anonyme, il faudra autoriser l'ip de serveur applicatif sur le relais SMTP IIS.

1. Aller dans l’onglet « Access » et cliquer sur « Relay ». Cliquer sur Add pour entrer l’IP du serveur qui doit envoyer de e-mail. 

PowerShell – Envoi de fichier chiffrés via FTP

Introduction

Afin de réaliser une action régulière et standart, deux solutions sont possible :

  • Réaliser l’action manuellement,
  • Développer un script qui s’en charge automatiquement.

Prérequis

  • Powershell,
  • 7zip,
  • Créer une tâche planifiée

Script

Le script suivant permet d’automatiquement envoyer par FTP dans un fichier chiffré, ce script utilise principalement les variables suivantes :

  • $days_to_send : L’age maximum des fichiers à envoyer
  • $source_path : Repertoire contenant les fichier à envoyer
  • $zipdirectory : Repertoire provisoire où seront compressé les fichiers
  • $log_location : emplacement du fichier de log
  • $sz : emplacement l’executable de 7zip
  • $ftp : FTP cible
  • $user : username utilisé pour se connecter au FTP
  • $pass : password utiliser pour se connecter au FTP et pour chiffré le fichier ZIP
  • $From : Adresse email qui emet les notifications par mail
  • $To : Adresse email recevant les notifications par mail
  • $SMTPServer : serveur SMTP utilisé pour les notifications par mail

 

#MAIN
function main
{
#Date Variables
$full_date = Get-Date
$custom_date = $full_date.ToString("yyyyMMdd")
$days_to_send = 4
$limit = $full_date.AddDays(-$days_to_send)

#Location Variables
$source_path = "E:\BACKUP\DATA\Database"
$zipdirectory = "G:\FTP_Folder\TEMP_ZIP"
$log_location = "G:\FTP_Folder\Logs"
$sz = ("C:\Program Files\7-Zip\7z.exe")

#FTP Variables
$ftp = "ftp://ftpr.domain.com/" 
$user = "FTP_Username" 
$pass = "FTP_Password"

#General variables
$LogFile
$compressed_folder
$folder_to_compress
$environment = "PROD"

#Mail Variables
$From = "Hatem.DJELASSI@piservices.com"
$To = "Hatem.DJELASSI@piservices.com"
$Cc = "Jean.DUPONT@piservices.com"
$Attachment
$Subject 
$Body
$SMTPServer = "smtp.domain.com"
$SMTPPort = "25"

Create-log-file -_log_location $log_location;

#Start Log
Append-log-file -_text "---------------------------------------------------------------"
Append-log-file -_text "START LOG @$full_date"

Create-Directory -_path $zipdirectory;
Copy-backups-older-than -_source_path $source_path -_destination_path $folder_to_compress -_limit $limit;
Compress_folder -_sz $sz -_pass $pass -_zipdirectory $zipdirectory -_folder_to_compress $folder_to_compress;
Delete_Folder -_file_to_delete $folder_to_compress;
Send_FTP -_user $user -_pass $pass -_ftp $ftp -_file_to_send $compressed_folder;
Notify -_from $From -_to $To -_cc $cc -_subject $Subject -_body $Body -_smtp $SMTPServer -_port $SMTPPort;

$compressed_folder = Get-ChildItem -Path $zipdirectory -Recurse -Force | Where-Object {$_.extension -match ".7z"} -ErrorAction stop
try
    {
        Remove-Item $compressed_folder.FullName -Recurse -ErrorAction Stop
        Append-log-file -_text "OK Deletion of the item has been made successfully" -_LogFile $LogFile;
    }
catch
    {
        Append-log-file -_text "ERROR when deleting the item $compressed_folder" -_LogFile $LogFile;
        write-host "An error occurred when deleting the item $compressed_folder" + $_.Exception.Message 
    }


#End log
Append-log-file -_text "END LOG @$full_date"
Append-log-file -_text "---------------------------------------------------------------"

}

#SendMail Function
function Notify
{
    param($_from, $_to, $_cc, $_attachment, $_subject, $_body, $_smtp, $_port)
    try 
            {           
                Send-MailMessage -From $_From -to $_To -Cc $_Cc -Subject $_Subject `
                -Body $_Body -SmtpServer $_SMTP -port $_Port 
                Append-log-file -_text "The email notification has been sent successfully" -_LogFile $LogFile;
            }
        catch
            {
                write-host "An error occurred when sending the notification" + $_.Exception.Message 
                Append-log-file -_text "The email notification has not been sent" -_LogFile $LogFile;
            }
}


#Log file functions
function Create-log-file 
{
    param ([string]$_log_location)        
    if (!(Test-Path "$_log_location\$custom_date - FTP_Script.log"))
    {
        try 
            {           
                $_LogFile = "$_log_location\$custom_date - FTP_Script.log"
                Set-Variable -Name LogFile -Value $_LogFile -Scope 1
            }
        catch
            {
                write-host "An error occurred when creating the log file" + $_.Exception.Message 
            }
        
    }
    else
    {
        try 
            {
                $_LogFile = "$log_location\$custom_date - FTP_Script.log"
                Set-Variable -Name LogFile -Value $_LogFile -Scope 1
            }
        catch
            {
                write-host "An error occurred when reusing the log file" + $_.Exception.Message 
            }
    }
}

function Append-log-file 
{
    param ($_text)
    try 
        {
            $custom_date+" "+$_text | Out-File $LogFile -Append -Force
        }
    catch
        {
            write-host "An error occurred when writing into the log file" + $_.Exception.Message 
        }
}

#Folders & files functions
function Create-Directory
{
    param ($_path)
    try 
        {
            $_folder_to_compress = New-Item -ItemType directory -Path "$_path\$custom_date - $environment Database Export" -force -ErrorAction Stop
            Set-Variable -Name folder_to_compress -Value $_folder_to_compress -Scope 1
            Append-log-file -_text "OK New folder to compress created successfully" -_LogFile $LogFile;

        }
    catch
        {
            Append-log-file -_text "ERROR when creating new folder to compress" -_LogFile $LogFile;
            write-host "An error occurred when creating new folder to compress" + $_.Exception.Message 
        }
}

function Copy-backups-older-than
{
    param ($_source_path, $_destination_path, $_limit)
    try
        {
            Get-ChildItem -Path $_source_path -Recurse -Force | Where-Object {$_.extension -in ".dif",".bak" -and !$_.PSIsContainer -and $_.CreationTime -gt $_limit } | copy-item -Destination  $_destination_path -Force -Container -ErrorAction stop
            Append-log-file -_text "OK The copy of the backups made after $limit has been made successfully" -_LogFile $LogFile;
        }
    catch
        {
            Append-log-file -_text "ERROR when copying the backups made after $limit" -_LogFile $LogFile;
            write-host "An error occurred when copying the backups made after $limit" + $_.Exception.Message 
        }
}

function Compress_folder
{
    param ($_sz, $_pass, $_zipdirectory, $_folder_to_compress)
    try
    {
        & $sz a -p"$_pass" "$_zipdirectory\$custom_date - $environment Database Export.7z" "$_folder_to_compress\*"
        $_compressed_folder = Get-ChildItem -Path $_zipdirectory -Recurse -Force | Where-Object {$_.extension -match ".7z"} -ErrorAction stop
        Set-Variable -Name compressed_folder -Value $_compressed_folder -Scope 1
        Append-log-file -_text "OK Compression/encryption of the folder has been made successfully" -_LogFile $LogFile;
    }
    catch
    {
        Append-log-file -_text "ERROR during compression/encryption of the folder" -_LogFile $LogFile;
        write-host "An error occurred during compression/encryption of the folder" + $_.Exception.Message 
    }
}

function Delete_Folder
{
    param ($_file_to_delete)
    try
    {
        Remove-Item "$_file_to_delete" -Recurse -ErrorAction stop
        Append-log-file -_text "OK Deletion of the item has been made successfully" -_LogFile $LogFile;
    }
    catch
    {
        Append-log-file -_text "ERROR when deleting the item $_file_to_delete" -_LogFile $LogFile;
        write-host "An error occurred when deleting the item $_file_to_delete" + $_.Exception.Message 
    }
}

#FTP Function
function Send_FTP
{
    param ($_user, $_pass, $_ftp, $_file_to_send, $_subject, $_body)
    #Create Web client
    try
    {
        $webclient = New-Object System.Net.WebClient -ErrorAction stop
        $webclient.Credentials = New-Object System.Net.NetworkCredential($_user,$_pass) -ErrorAction Stop 
        $webclient.Proxy = $NULL 
        Append-log-file -_text "OK FTP WebClient created successfully" -_LogFile $LogFile;
    }
    catch
    {
        Append-log-file -_text "ERROR when creating the FTP WebClient" -_LogFile $LogFile;
        write-host "An error occurred when creating the FTP WebClient" + $_.Exception.Message 
    }
    #Create URI
    try
    {
        $_uri = New-Object System.Uri($_ftp+$_file_to_send.Name) -ErrorAction stop 
        Append-log-file -_text "OK FTP URI created successfully" -_LogFile $LogFile;
    }
    catch
    {
        Append-log-file -_text "ERROR when creating the FTP URI" -_LogFile $LogFile;
        write-host "An error occurred when creating the FTP URI" + $_.Exception.Message 
    }
    #Send the file
    try
    {
        $webclient.UploadFile($_uri, $_file_to_send.FullName)
        Append-log-file -_text "OK FTP file sent successfully" -_LogFile $LogFile;
        $global:Subject = "FTP Upload Success"
        $global:Body = "The file $_file_to_send has been sent successfully @$full_date"
    }
    catch
    {
        Append-log-file -_text "ERROR when sending the file through FTP" -_LogFile $LogFile;
        write-host "An error occurred when sending the file through FTP" + $_.Exception.Message 
        $global:subject = "FTP Upload Failure"
        $global:Body = "An error occurred when sending the FTP backups from $environment to FTP @$full_date"
    }
}

main

SQL Server – Executer une procédure stockée en “RUN AS”

Introduction

Afin de fournir à un utilisateur le droit d’executer une procédure stockée, un administrateur SQL peut fournir à un utilisateur le droit “EXECUTE” sur celle-ci.

Cependant, il est possible que l’utilisateur rencontre une erreur lors de l’execution de la procédure stockée de type :

image

Celà est souvent du à la présence de code SQL dynamique, généré lors de l’execution.

Explication

Il est possible de fournir à l’utilisateur le droit “SELECT” sur l’objet de la requête et le script pourras être executé avec succès, néanmoins, celà vas contre le principe d’une procédure stockée qui permet de fournir aux utilisateurs un accès aux données via l’interface restrictive de la procédure stockée.

Afin de résoudre ce problème il est possible (à partir de SQL 2008) d’utiliser la clause “EXECUTE AS” en spécifiant un compte disposant des droits nécessaires à l’execution de la procédure stockée.

Il est important de noter que l’utilisation de “EXECUTE AS” ne fournit pas à l’utilisateur final des droits d’impersonnalisation qui permetteraient à l’utilisateur de “récuperer” les droits du compte d’execution.

Réalisation

L’exemple suivant explique comment utiliser “EXECUTE AS” :

image

La ligne 2 spécifie le compte “SQLUser” à utiliser pour l’execution du script,

La ligne 5 retourneras le compte “SQLUser”,

La ligne 7 “EXECUTE AS CALLER” permet d’utiliser le compte de l’utilisateur executant la procédure stockée,

La ligne 8 retourneras le compte de l’utilisateur executant la procédure stockée,

La ligne 10 “REVERT” retablis l’utilisation du compte “SQLUser” spécifié au début du script.

Source

https://msdn.microsoft.com/fr-fr/library/ms181362.aspx

http://www.sqlmatters.com/Articles/Using%20Execute%20As%20with%20Stored%20Procedures%20Containing%20Dynamic%20SQL.aspx

SQL Server – Envoyer par email le résultat d’une requête

Introduction

L’une des requêtes qui peut être demandé à un adminsitrateur SQL est l’execution régulière d’une requête et le transmission de son résultat par email.

Prérequis

Afin de répondre à ce type de demande, les outils suivants seront utiles :

  • La procédure “sp_send_dbmail”,
  • L’agent SQL Server,
  • La configuration d’un profile Database Mail.

Réalisation

Le script suivant permet d’envoyer par mail le résultat de la procédure stockée “usp_StoredProcedure” :

image

Depuis la base MSDB, executer la procedure “sp_send_dbmail” (ligne 1), cette procédure prends les paramètres suivants :

@profile_name : profile mail configuré sur SQL Server sous le dossier “Management”

@recipients : adresse mail du destinataire

@subject : sujet qui figureras dans le mail envoyé

@query : requête à executer

@query_result_width : largeur en nombre de caractère du fichier qui seras envoyé en pièce jointe, par défaut la valeur est de 256 caractères, ce paramètre est important si l’on souhaite réaliser par la suite un import vers excel

@attach_query_result_as_file : utiliser la valeur “1” afin de fournir le resultat de cette requête dans un fichier en pièce jointe

@query_attachment_filename : nom du fichier en pièce jointe

@query_result_separator : caractère de séparation qui seras utilisé

L’article suivant explique plus en détails les paramètres qu’il est possible d’utiliser : https://msdn.microsoft.com/fr-fr/library/ms190307.aspx

Identifier le certificat correspondant à l’attribut AD UserCertificate ou cACertificate

Dans le cadre de la gestion des certificats dans Active Directory, il arrive que l’on retrouve des valeurs de ce type pour les attributs UserCertificate ou CaCertificate:

 image

Ce type de champ est difficilement lisible et cela peut donc complexifier la tâche lorsque l’on veut supprimer un certificat déployé en doublon, ou tout simplement un certificat avec des informations erronées.

Pour rapidement différencier les certificats il suffit de suivre les étapes suivantes:

Copier le contenu Hexadécimal dans un fichier texte :

image

image

image

Exécuter la commande suivante :

Certutil –decodehex C:\Temp\HexValue.txt C:\Temp\OutputCert.cer

image

Le résultat de la commande est un fichier .cer que l’on peut ouvrir et qui contiendra les attributs du certificat de manière lisible, ces opérations doivent être répétées sur les différentes entrées pour chaque certificat à identifier.

image

image

Poste de Travail : Fixer le Navigateur par Défaut

Voici comment fixer le navigateur par défaut de vos utilisateurs.

Via PowerShell:

Exécutez les commandes suivantes à l'ouverture de session utilisateur sous forme de script powershell en fonction de votre choix.

Pour Internet Explorer:

Set-Location HKCU:\
    Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\ftp\UserChoice' -name ProgId IE.FTP
    Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice' -name ProgId IE.HTTP
    Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice' -name ProgId IE.HTTPS

 

Pour Mozilla Firefox:

Set-Location HKCU:\
    Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\ftp\UserChoice' -name ProgId FirefoxURL
    Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice' -name ProgId FirefoxURL
    Set-ItemProperty 'HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice' -name ProgId FirefoxURL

Vous pouvez aussi le faire par GPO dans "Configuration Utilisateur > Paramètres Windows > Registre"