Affichage des articles dont le libellé est powershell. Afficher tous les articles
Affichage des articles dont le libellé est powershell. Afficher tous les articles

9 sept. 2015

[snippet] Comment s'approprier rapidement des profils itinérants ?

Vous voulez vous approprier tous les profils itinérants Windows XP ? Mais si vous faites ça, les utilisateurs n'auront plus accès à leur profils itinérant et obtiendrons une erreur à l'ouverture de session.
Le profil itinérant possède une ACL NTFS qui indique que l'utilisateur du profil est le propriétaire du répertoire et des fichiers. L'administrateur ne peut pas accéder à ces fichiers, car il ne figure pas dans l'ACL par défaut. Une GPO existe pour rajouter le groupe Administrateurs dans l'ACL à la création du répertoire, mais admettons que cette GPO ne s'applique pas ou a été créée après les répertoires.
Le but du jeu est d'ajouter le groupe Administrateurs dans l'ACL de chaque répertoire. Or, pour modifier une ACL, il faut pouvoir y accéder, ce qui n'est pas le cas... La seule façon est de s'approprier le répertoire (ce qui va remplacer l'ACL existante par une ACL vierge, avec pour seule entrée le nouveau propriétaire). En faisant cela, les utilisateurs n'auront donc plus le droit d'accéder à leur propre profil. Il faut rajouter l'utilisateur dans l'ACL en contrôle total, et ce, récursivement pour les fichiers existants.

Attention : pour que Windows autorise de charger un profil itinérant, même si l'utilisateur a un contrôle total sur tous ses fichiers, il faut que le propriétaire du répertoire soit : soit le compte utilisateur soit le groupe Administrateurs. S'il s'agit d'un autre compte, même le compte Administrateur, le chargement échouera.

Voici le script powershell :

# répertoire à scanner
$dir = "D:\profils"

$profils = Get-ChildItem -Path $dir -Force -ea 0

$profils | ForEach-Object {
    $rep = $_
    if($rep.Mode.startsWith('d')) { # on ignore les fichiers
        $user = $_.Fullname.Split('\')[2]
        $owner = $acl = $null
        $acl = $rep.GetAccessControl()
        $owner = $acl.Owner
        if($owner -ne 'BUILTIN\Administrateurs' -and $owner -ne 'BUILTIN\Administrators') { # le propriétaire n'est pas déjà administrateur, on ne fait rien
            echo "$user MAUVAIS OWNER!"
        }
        else {
            $found =  $false
            $acl.Access|Where-Object{$_.IdentityReference.ToString() -ieq $user}|ForEach-Object{$found=$true} # on vérifie si le répertoire possède déjà l'ACL...
            if($found) {
                Write-Host -fore gray "$user have rights ($owner)"
            }
            else {
                write-host -fore blue "Il faut ajouter DOMAINE\$user."
                $NTuser = New-Object System.Security.Principal.NTAccount("DOMAINE\$user")
                $access = [System.Security.AccessControl.FileSystemRights]::FullControl
                $heritage = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
                $heritagefile = [System.Security.AccessControl.InheritanceFlags]::None
                $propa = [System.Security.AccessControl.PropagationFlags]::None
                $allow = [System.Security.AccessControl.AccessControlType]::Allow
                $ace = New-Object System.Security.AccessControl.FileSystemAccessRule("DOMAINE\$user",$access,$heritage,$propa,$allow) # on cré une ACE pour répertoire
                $acefile = New-Object System.Security.AccessControl.FileSystemAccessRule("DOMAINE\$user",$access,$heritagefile,$propa,$allow) # on cré une ACE pour les fichiers
                $acl.AddAccessRule($ace)
                if(!$?) {
                    echo "Impossible de créer l'ACL"
                }
                else { # l'ACL est OK, on l'applique
                    $rep.SetAccessControl($acl)
                    if($?) { # on répète récursivement aux fichiers et répertoires déjà existants
                        echo "Appropriation récursive de $($rep.Fullname)."
                        $aclfile = $null
                        Get-ChildItem -Path $rep.FullName -Force -Recurse | ForEach-Object {
                            if($_.Mode.StartsWith('d')) { # répertoire...
                                $_.SetAccessControl($acl)
                            }
                            else { # fichier...
                                $aclfile = $_.GetAccessControl() # doit recréer l'ACL à chaque fois pour les fichiers
                                $aclfile.AddAccessRule($acefile)
                                $_.SetAccessControl($aclfile)
                            }
                        }
                        Write-Host -fore green "`r`n$user : OK"
                    }
                    else {
                        Write-Host -fore red "`r`n$user : ERR"
                    }
                }
            }
        }
    }
}

Il est utile pour l'administrateur d'avoir accès au contenu des profils pour vérifier les abus de stockage de fichiers (notamment car le contenu du profil est "streamé" à l'ouverture de la session, contrairement au contenu d'un lecteur réseau ou d'une redirection de Mes documents).

10 nov. 2012

[snippet] Script d'alerte de d'indisponibilité de serveur

Un petit script Powershell qui vérifie si les machines spécifiées (dans un fichier texte par exemple) répondent, et si une machine ne répond pas, un mail d'alerte est automatiquement envoyé avec un rapport. Il suffit ensuite de créer une tâche planifiée s'exécutant toutes les 10 minutes...

Voici le script powershell :

# ce script sert à vérifier l'état des tâches sauvegardes
# et d'envoyer un mail rapport à la fin
# ci-dessous la configuration de l'envoie de mail

$expediteur = "Opérateur de sécurité"

$destinataire = "admin1@domaine.tld,admin2@domaine.tld" # séparer entre virgule

$serveur = "srv-exchange.domaine.tld"

$objet = "Message alerte disponibilité quotidienne " + [System.DateTime]::Now

# ci-dessous la configuration du fichier de log

$log = "verifDispo.txt"

############################ NE RIEN MODIFIER CI-DESSOUS ######################

echo "Tâche en cours..."
$dateStartRapport = date
$data = "Nom".PadRight(20)+"État`n"
$data += '-'.PadRight(20-1,'-')+' '+'-'.PadRight(7-1,'-')
$countOK = 0
$count = 0

if($args.length -eq 1 -and $args[0] -ine "-help") {
    $file = $args[0]
}
else {
    $file = "dispo.txt"
}


foreach ($srv in Get-Content $file)
{
    if($srv -ne "") {
        $count++
        $line = "`n"+$srv.PadRight(20)
        if($srv.StartsWith('#')) {
            $line += "Ignoré"
            $countOk++
        }
        else {
            ping -n 1 -w 1000 $srv | Out-Null
            if($?) {
                $line += "Ok"
                $countOk++
            }
            else {
                $line += "Erreur"
            }
        }
        $data += $line
    }
}

$rapport = "Rapport de disponibilité " + $dateStartRapport
$rapport += "`nRésumé: $countOk/$count OK en "+((Get-Date) - $dateStartRapport).minutes.ToString().PadLeft(3)+" minutes, terminé à $(Get-Date)"
$rapport += "`n`n$data"
$rapport += "`n`nTerminé"
$rapport >> $log
if($count -ne $countOk) {
    # envoie du mail
    $message = new-object System.Net.Mail.MailMessage $expediteur, $destinataire, $objet, $rapport
    $message.Priority = "High"
    $client = new-object System.Net.Mail.SmtpClient $serveur
    $client.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
    $client.Send($message)
    if($?) { "Mail envoyé avec succès à $destinataire" >> $log }
    else { "Mail erreur pour $destinataire" >> $log }
}
else { "Mail non envoyé" >> $log }

Le script est très simpliste. Il ne vérifie pas l'état des services. Donc si le firewall du serveur (ou poste client !) est configuré pour ne pas répondre aux pings, cela ne fonctionnera pas. D'ailleurs, si un serveur subit une usurpation d'IP, ça ne sera pas détecté non plus. Evidemment, si c'est le serveur de messagerie qui ne répond plus, vous ne risquez pas de recevoir l'alerte non plus...

14 avr. 2012

[snippet] Compter le nombre de pages imprimées grâce aux journaux d'évènements

Chaque impression faite vers une imprimante en passant par un serveur d'impression sous Windows Server créé un enregistrement dans le journal événement Système.
Il est donc possible, à partir de là, de compter le nombre de pages imprimées par utilisateurs du domaine, sur ce serveur d'impression.
Les impressions directes (qui ne passent pas par le serveur d'impression, comme les photocopies ou les imprimantes reliées directement à l'ordinateur) ne seront évidemment pas comptabilisées.

$start = date
Write-Host -NoNewline "Lecture du journal"
$data = Get-EventLog -ComputerName serveur-impression -LogName System -Source Print -InstanceId 1073741834
Write-Host " ($([int](date).Subtract($start).TotalSeconds)s)"
Write-Host "Calcul du résultat"
$data | ForEach-Object {
    $var = @{}
    $msg = $_.Message
    # on parse le contenu de message pour récupérer le login et le nombre de page
    $var.user = $msg.Substring($msg.IndexOf("possédé par")+12).Split(" ")[0].ToLower()
    $var.nb = $msg.Substring($msg.IndexOf("pages imprimées")+18)
    $res = New-Object PSObject -Property $var
    # on retourne un objet
    Write-Output $res
} | group-object user | foreach { # on regroupe toutes les impressions par utilisateur
    $var2 = @{}
    $var2.user = $_.Name
    # on fait le cumul des pages de l'utilisateur
    $var2.total = ($_.Group | measure-object nb -sum).Sum
    $res2 = New-Object PSObject -Property $var2
    Write-Output $res2
} | sort -Descending total
echo "Terminé en $([int](date).Subtract($start).TotalSeconds) sec"

Le script retourner une liste de couple "user,total" triée à partir du plus gros total de feuilles. Pour pouvez passer le résultat du sort directement vers un export-csv.

Attention : le traitement peut durer une dizaine de minutes selon le nombre d'impressions à traiter.

Note : veuillez à avoir une taille limite pour le journal d’événement assez élevée pour stocker assez d'enregistrements. Pour changer la taille, rendez-vous dans le gestionnaire d’événements, faites un clic-droit > Propriétés sur le journal Système et augmentez la taille. Une taille de 50 Mo suffit à stocker l'historique d'environ 10 à 50 000 impressions.

21 déc. 2011

[snippet] Recevoir par mail l'état du planificateur de tâche en Powershell

Il est souvent utile, dans la gestion d'un parc informatique, de repérer une tâche planifiée qui ne s'est pas bien déroulée. Plutôt que d'aller vérifier l'état du planificateur de tâche de Windows Server tous les jours, un script Powershell peut faire une alerte mail !

L'outil schtasks permettra de récupérer en ligne de commande, l'état complet du planificateur de tâche. Le script Powershell va mettre en forme les résultats, déterminer si une tâche est en erreur, et envoyer un e-mail, qui sera marqué important si une tâche est en erreur.

Voici le script :
schtasks.exe /query /fo:csv /v > tmp.csv
$taches = import-csv tmp.csv -Delimiter ","

$all = ""
$allOK = $true
$taches | foreach-object {
    if($_."Statut de la tâche planifiée" -eq "Activée") {
        if($_."Dernier résultat" -ne "0") {
            $allOK = $false
        }
        $all += $("`r`n0x" + $_."Dernier résultat" + " " + $_."Nom de la tâche" +" (Etat:" + $_."État" + ", " + $_."Heure de la dernière exécution"+ ")")
    } else {
        $all += $("`r`n0x" + $_."Dernier résultat" + " " + $_."Nom de la tâche" +" (Désactivée)")
    }
}

$objet = "Tâches planifiées sur $(Get-Content env:\computername)"
$serveur = "192.168.1.253"
$destinataire = "cedric@domaine.com"
$expediteur = "Opérateur de sauvegardes >sauvegarde@domaine.com>"
$client = new-object System.Net.Mail.SmtpClient $serveur
$client.Credentials = New-Object System.Net.NetworkCredential("sauvegarde@domaine.com", "mdp")
$message = new-object System.Net.Mail.MailMessage $expediteur, $destinataire, $objet, $all
if($allOK -eq $false) { $message.Priority = "High" }
$client.Send($message) 

Puis de créer une tâche planifiée sur le serveur, pour vérifier l'état des tâches.

Ce qui donnera pour résultat :

Exemple de mail reçu

12 déc. 2011

[snippet] Se connecter a MySQL depuis Powershell

Il est possible d'écrire dans une base de données MySQL depuis un simple script Powershell. Le tout en utilisant la capacité a Powershell d'utiliser les assemblies .Net et MySQL Connector/Net (a télécharger http://dev.mysql.com/downloads/connector/net/).

Vous devez depuis le serveur MySQL autoriser d'être accédé par une autre machine. Pour cela, ouvrez une console MySQL et tapez:
GRANT ALL ON *.* to root@'192.168.1.196' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;

Une fois le connector installé, localisez la DLL MySQL.Data.dll (normalement localisée a C:\Program Files\MySQL\MySQL Connector Net \Binaries\.NET 2.0\MySQL.Data.dll en 5.X ou C:\Program Files\MySQL\MySQL Connector Net \Assemblies\v2.0\MySQL.Data.dll en 6.X

Il faut alors charger l'assemblie dans la session Powershell comme ceci :
[system.reflection.Assembly]::LoadFrom(“C:\Program Files\MySQL\MySQL Connector Net 6.4.4\Assemblies\v2.0\MySQL.Data.dll”)

Puis voici deux petites commandes similaires au PHP:
function mysql_connect([string]$ip,[string]$Login,[string]$Password,[string]$Database) {  
  $dbconnect = New-Object MySql.Data.MySqlClient.MySqlConnection  
  $dbconnect.ConnectionString = “server=$ip;user id=$login;password=$password;database=$database;pooling=false”  
  $dbconnect.Open()  
  $global:db = $dbconnect  
  $dbconnect  
}  
  
function mysql_query([string]$Query,[MySql.Data.MySqlClient.MySqlConnection]$Db) {  
  $sql = New-Object MySql.Data.MySqlClient.MySqlCommand  
  if($db -eq $null) {  
 if($global:db -eq $null) { throw "Pas de connexion spécifiée" }  
 else { $db = $global:db }  
  }  
  if($db.State -ne "Open") {  
 #throw "Connexion spécifié non établie"  
 # on ré-ouvre la connexion car elle peut-être fermée...  
 $db.Open()  
  }  
  $sql.Connection = $db  
  $sql.CommandText = $query  
  $sql.ExecuteNonQuery()  
}

Que l'on utilise comme ceci :
$dbconnect = mysql_connect -Ip 192.168.1.243 -Login "root" -Password "azerty" -Database "stock"
mysql_query -Query "insert into connexion VALUES (NULL, '$mavar_powershell', NOW())"

27 nov. 2011

[snippet] Récupérer les adresses IP courrantes en powershell

Il est souvent intéressant de récupérer la ou les adresses IP d'un serveur qui exécute un script powershell, quand par exemple, celui-ci envoi un message électronique ou écrit des logs. Il existe plusieurs façons, mais voici un petit morceau de code en PowerShell qui permet de récupérer facilement toutes les adresses IP actives du serveur.

(Get-WmiObject win32_networkadapterconfiguration | ForEach-Object { echo $_.IPAddress }) -join "; "

Chez moi retournera 5.42.143.38; 192.168.100.196; 192.168.1.196; 172.16.10.196.

Explication :
 Get-WmiObject est une cmdlet très utile qui permet de récupérer des valeurs sur un peu près tout ce qui tourne dans le système (périphériques, paramétrages, pilotes, comptes ...) en l’occurrence la classe Win32_NetworkAdapterConfiguration permet de récupérer les valeurs des cartes réseaux.
 ForEach-Object permet d'afficher chaque adresse IP de chaque carte réseau.
 Les parenthèses permettront de retourner tout le résultat du pipeline à l'opérateur join, et non pas seulement insérer l'opérateur dans le dernier pipe, à savoir ForEach-Object ou echo.
 -join "; " permet de faire l'équivalent d'un implode en PHP, c'est à dire rassembler en une chaîne tous les éléments d'un tableau, en séparant ces éléments par la chaîne spécifiée à l'opérateur join.

6 avr. 2011

[snippet] Lister les fichiers ouverts sur un serveur distant

Parfois il peut être utile de voir la liste des fichiers ouverts sur un serveur, sans forcément y ouvrir une session pour obtenir cette liste.
C'est possible facilement, grâce au programme psfile (disponible ici).


Syntaxe :
psfile.exe \\ip -u login -p password chemin|descid

Couplé avec un bout de powershell, et on peut filtrer les résultats, par exemple, tous les fichiers Excel...
.\psfile.exe \\10.10.0.253 | foreach-Object { if($_.StartsWith("[") -and $_.EndsWith(".xls")) { echo $_ } }
On peut également fermer les fichiers que l'on souhaite grâce à l'option -c.

22 déc. 2010

[snippet] Eteindre des PC en masse

Vous avez un grand parc informatique à gérer, mais les utilisateurs ne prennent pas l'habitude d'éteindre leur machine en parant le soir. La solution est de mettre une tâche planifiée avec un shutdown /s. Mais si on veut être plus flexible, par exemple changer l'heure d'extinction, il faut rassembler les machines en un batch qui va lancer l'ordre shutdown à distance. Le problème d'un tel script, c'est qu'il peut durer des heures pour quelques machines. En effet, un shutdown /m machine peut prendre un certain temps, surtout si la machine est déjà éteinte.
La solution, pinguer la machine avant de lancer l'ordre, si elle ne répond pas, alors on ne lance pas l'ordre.

Voici un petit script powershell qui fait ça très bien :
 
echo "Synthaxe: script.ps1 `"Reseau`" `"Plage1[;Plage2;Plage3...]`" IP_min IP_max"
echo "Exemple: shutdown.ps1 "172.16" `"100;101;102`" 1 24"
echo "Exemple: shutdown.ps1 "192.168" `"1`" 1 255"

if($args.Length -lt 4) {
    echo "Erreur de synthaxe : nombre d'arguement incorrect"
    exit 1
}

$date = date
echo "Début shutdown : $date" >> ShutdownLog.txt
$reseau = [string]$args[0]
$plages = [string]$args[1]
$min = [int]$args[2]
$max = [int]$args[3]
$count = 0
$countPing = 0
echo "Args: réseau=$reseau, plages=$plages, min=$min, max=$max" >> ShutdownLog.txt

$plages.Split(";") | ForEach-Object {
    $plage = $_
    for($i=$min; $i -le $max; $i++) {
        $ip = "$reseau.$plage.$i"
        $count++
        ping -n 1 -w 1000 $ip
        if($?) {
           echo " shutdown /t 60 /f /m \\$ip"
           $countPing++
        }
    }
}


echo "$count IP scannées, $countPing ordres d'extinctions" >> ShutdownLog.txt
$date = date
echo "Fin shutdown: $date" >> ShutdownLog.txt


Pensez à la planète !