Active Directory/Powershell: Delegation et liste de controle d’acces

Peu de documentation en Français, mais cependant, pas mal d’explications sur MSDN, notamment concernant la classe "ActiveDirectoryAccessRule"  permettant la délégation d’Active Directory/gestion des permissions en .NET (et donc en PowerShell).

 

DACL/SACL & ACE ?

Avant de rentrer dans le vif du sujet, il est important de bien comprendre les listes d’accès Active Directory. Une liste de contrôle d’accès (ACL) est une liste d’entrées de contrôle d’accès (ACE). Chaque ACE dans une ACL identifie un "trustee" (Un compte utilisateur, un groupe, ou une session sur lequel un accès de controle (ACE) s’applique) et précise les droits d’accès autorisés, refusés ou vérifiés pour l’administrateur. Le descripteur de sécurité (security descriptor) pour un objet sécurisé peut contenir deux types d’ACL: une DACL et une SACL.

Une DACL (Discretionary access control list) identifie les "trustees" qui sont autorisés ou non à accéder à un objet. Quand un processus essaie d’accéder à un objet, le système vérifie l’ACE dans la DACL de l’objet pour déterminer s’il faut accorder l’accès. Si l’objet n’a pas de DACL, le système autorise l’accès complet à tout le monde. Si la DACL de l’objet n’a pas de ACE, le système refuse toutes les tentatives pour accéder à l’objet parce que la DACL ne permet pas de droits d’accès. Le système vérifie l’ACE dans l’ordre jusqu’à ce qu’il trouve une ou plusieurs entrées de contrôle qui permettent à tous les droits d’accès requis, ou jusqu’à ce que l’un des droits d’accès demandé soit refusé.

Une SACL (System access control list) permet aux administrateurs de logger les tentatives d’accéder à un objet. Chaque ACE précise les types de tentatives d’accès par un "trustee" qui a causé la génération d’un enregistrement dans le journal des évènements de sécurité. Une ACE dans une liste SACL peut générer des enregistrements d’audit lors d’une tentative d’accès échoue, quand elle réussit, ou les deux.

Rentrons maintenant dans le sujet, jouons avec les droits standard, les droits étendus, ou les droits spécifiques. Pour l’affection de droits, la classe System.DirectoryServices.ActiveDirectoryAccessRule sera utilisé. Commençons par découvrir les constructeurs. Si vous êtes allergique à MSDN, utilisons la commande suivante:

[System.DirectoryServices.ActiveDirectoryAccessRule].GetConstructors()

img1

 

Les droits standards

Commençons par les droits standards, en récupérant le SID de l’utilisateur qui recevra la délégation:

$Login = "Utilisateur-Test"   
$NTAccount = New-Object system.security.principal.ntaccount($Login)    
$SID = $NTAccount.translate([system.security.principal.securityidentifier])

Ajoutons maintenant les droits AD en utilisant le constructeur:

.ctor(    
    System.Security.Principal.IdentityReference    
    System.DirectoryServices.ActiveDirectoryRights    
    System.Security.AccessControl.AccessControlType    
)

Spécifions les droits a ajouter et spécifions le type d’accès

$rights = "GenericAll"    
$AccessType = "Allow"

Ajoutons les droits et sauvegardons l’objet

$root = [adsi]"LDAP://RootDSE"    
$OU = [adsi]"LDAP://ou=OU-Test,"+$root.distinguishedName    
$ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($SID, $rights, $AccessType)    
$OU.psbase.get_objectsecurity().AddAccessRule($ace)    
$OU.psbase.CommitChanges()

 

Les droits étendus

Passons maintenant aux droits étendus ainsi qu’aux droits spécifiques, nous allons utiliser le constructeur suivant:

.ctor(    
    System.Security.Principal.IdentityReference    
    System.DirectoryServices.ActiveDirectoryRights    
    System.Security.AccessControl.AccessControlType    
    System.Guid    
    System.DirectoryServices.ActiveDirectorySecurityInheritance    
)

Pour les droits étendus, il est nécessaire de récupérer le GUID du droit (stocké dans la partition de configuration) stocké dans l’attribut "rightsGUID". Pour ce faire, je vous propose la fonction suivante:

function Get-RightsGUID    
{    
    Param ($ExtendedRight, $dse)    
    
    $ER = [adsi]("LDAP://cn=Extended-Rights,"+$dse.configurationNamingContext)    
    $OU = [adsi]("LDAP://cn="+$ExtendedRight+","+$ER.distinguishedName)    
    [system.guid]$guid = $OU.rightsGUID    
    return $guid.ToString()    
}

Pour l’utiliser :

$guid = Get-RightsGUID "Change-PDC" $root

Même principe que précédemment pour les droits standards:

$Login = "Utilisateur-Test"    
$NTAccount = New-Object system.security.principal.ntaccount($Login)    
$Rights = "ExtendedRight"    
$AccessType = "Allow"    
$Inherit = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"    
$ace    = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($NTAccount, $Rights, $AccessType, $guid, $Inherit)    
$OU.psbase.get_objectSecurity().AddAccessRule($ace)    
$OU.psbase.CommitChanges()

 

Les droits spécifiques

Pour les droits spécifiques, il est nécessaire de récupérer le GUID du droit (stocké dans la partition "schéma") stocké dans l’attribut "schemaIDGUID". Pour ce faire, je vous propose la fonction suivante:

function get-schemaGUID    
{    
    Param ($OU, $dse)

    $obj = [adsi]"LDAP://cn="+$OU+","+$dse.schemaNamingContext   
    [system.guid]$guid = $obj.schemaIDGUID[0]    
    return $guid.ToString()    
}

 

Pour l’utiliser:

$guid = get-schemaGUID "computer" $root

Même principe que précédemment:

$Login = "Utilisateur-Test"    
$NTAccount = New-Object system.security.principal.ntaccount($Login)    
$Rights = “ReadProperty, WriteProperty”    
$AccessType = "Allow"    
$Inherit = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"    
$ace    = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($NTAccount, $Rights, $AccessType, $guid, $Inherit)    
$OU.psbase.get_objectSecurity().AddAccessRule($ace)    
$OU.psbase.CommitChanges()

 

Voila qui conclu cette introductions aux ACL Active Directory. Vous trouverez plus d’informations sur la gestion de l’Active Directory en .NET sur MSDN

Outlook, overquota & Powershell

On a tous un jour ou l’autre été confronté à un problème de quota. Même si Exchange 2010 apporte des BAL plus larges dû aux coûts de stockages réduits, faire du tri dans sa boite aux lettres relève du calvaire. Pour ajouter à ça, il arrive bien souvent qu’on veuille supprimer une pièce jointe, mais conserver un mail…fonctionnalité non disponible sous Outlook
Outlook-Delete-Attachment

Pour palier à ça, je vous propose un script permettant de « dumper » dans un répertoire les fichiers plus gros qu’une taille définie, et de les supprimer du mail qui les contient, sans supprimer le mail en question.

Côté serveur en utilisant la console MMC Exchange Management Shell, les informations récupérées sont assez peu convaincantes, et surtout trop peu complètes pour pouvoir être exploitées.
GetMailboxFolderStats

L’avantage des produits Microsoft sont les synergies possibles entre ceux ci. Exploiter Outlook via Powershell est d’une simplicité déconcertante.

param(
    [int]$MaxSize = $(throw "Please specify size of max file"),
    [string] $DeleteAttachedFiles = $false
    )

# Attached files will be saved in this folder
$AttachmentsFolder = "C:\tmp\"

# Check folder existence
if (-not (test-path $AttachmentsFolder) )
{
    New-Item -ItemType Directory $AttachmentsFolder | out-null
}

# Attached files list
$ResultList = @()

# Create an outlook object
$OutlookObj = New-Object -ComObject Outlook.Application
$objNamespace = $OutlookObj.GetNamespace("MAPI")
$objFolder = $objNamespace.GetDefaultFolder(6)

# Get all items
$Items = $objFolder.Items 

# Items - Loop
foreach ($item in $Items)
{
    # Check if any file is attached
    if ($item.Attachments) { 

        # Attachments - Loop
        $item.Attachments | % {
            $FileName = $_.FileName
            $SavedName = join-path $AttachmentsFolder $FileName

            # Check size
            if( $($_.size) -ge $MaxSize )
            {
                # Save file
                $_.SaveAsFile($SavedName)

                $Result = new-Object -typename System.Object
                $Result | add-Member -memberType noteProperty -name "Subject" $Subject
                $Result | add-Member -memberType noteProperty -name "FileName" $FileName
                $Result | add-Member -memberType noteProperty -name "Size (Mb)" $(($_.size/1mb).tostring("0.000"))
                $ResultList += $Result

                if ($DeleteAttachedFiles -eq $true)
                {
                    # Delete attachment
                    $_.delete()
                    $item.save()
                    write-host "[Deleted] [$Subject] [$(($_.size).tostring("0.000"))] $FileName"
                }   

            }

        }
    }
}

# Close Outlook
$OutlookObj.quit()

$ResultList | sort size

Une bride d’explications, l’objet $item est le mail en court de traitement, cet objet possède la propriété « Attachments » qui renvoie les pièces jointes du message. Les objets pièces jointes peuvent ensuite être utilisées en utilisant les méthodes « SaveAsFile » ou « Delete »
Attachments-GetMember

Une fois la pièce jointe supprimée du message, regardons les méthodes disponibles pour cet objet:

Item-Get-Member

La méthode « Save » permet de faire ça!

Le script accepte deux paramêtres:
- La taille au dessus de laquelle les fichiers seront dumpés
- Le paramètre de suppression (non mandatory), par default défini à $false. Si ce paramètre n’est pas défini, le script n’effectuera que le dump des fichiers, et ne les supprimeras pas de la boite aux lettres.

Exemple 1:
script.ps1 2mb -> Dumperas les fichiers, aucune suppression

Example1

Exemple 2:
script.ps1 2mb $true -> Dumperas les fichiers, et supprimera les pièces jointes
Example2

Gestion des groupes Active Directory en Powershell

La gestion des groupes AD est assez simple en Powershell, et la synthaxe assez proche de celle qu’on pouvait trouver en VBS. Pour effectuer une requête LDAP sur un objet AD, il suffit de recuperer son Distringuished Name (DN)

$DNGroup = [Completer]
$dom = New-Object DirectoryServices.DirectoryEntry("LDAP://$DNGroup")

Les méthodes de l’objet $dom peuvent maintenant etre utilisées:

$dom | get-member

Il est maintenant assez facile de lister les membres

$dom.member

Pour ajouter un utilisateur, il faut maintenant utiliser la methode add en passant en parametre le distringuishedName de l’utilisateur. Voila ce que ça donne

$dom.member.Add($DNUser)
$dom.setInfo()

AD4

Dans le cas d’une connexion à un domaine non trusté. Il est nécessaire de recupérer les credentials de l’utilisateur

$cred = get-credential

AD5

Notre mot de passe est maintenant chiffré, mais la création d’un objet directory entry nécessite que le mot de passes ne soit pas chiffré. Pas de probleme, powershell fournis une méthode pour le faire

$pwd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred.Password))

Et maintenant, il ne reste plus qu’à recreer l’objet directory entry en passant en parametre, la chaine ldap, le username ainsi que le mot de passe

$dom = New-Object DirectoryServices.DirectoryEntry("LDAP://$domain/$DNGroup",$cred.UserName,$pwd)

Considérons maintenant que nous nous trouvons dans un domaine (DomaineA) et un autre domaine, qui sera trusté (DomaineB). Admettons maintenant que nous souhaitions ajouter dans un groupe de DomaineA, un utilisateur de DomaineB. Si nous utilisons la méthode vu plus haut

$dom.member.Add($DNUser)
$dom.setInfo()

Lors du setinfo une erreur se produira, car il n’est pas possible d’ajouter une utilisateur d’un domaine étranger en passant son DN.

Il est nécessaire de passer par son SID. Pour recuperer le SID d’un utilisateur, il faut faire comme suit:

$User = "Domaine\Utilisateur"
$AccountSID = ((New-Object System.Security.Principal.NTAccount($User)).Translate([System.Security.Principal.SecurityIdentifier])).Value

Une fois le SID recupéré, il est maintenant possible de l’ajouter dans le groupe en forgeant sa chaine ldap comme suit :

$LDAP = "LDAP:// < SID=$AccountSID>"
$dom.member.Add($LDAP)
$dom.setInfo()

Et voila qui conclu pour cet article sur la gestion des groupes AD en powershell

PowerShell, WPF et MSChart

PowerShell étant basé sur le Framework .NET il est possible d’utiliser Microsft Chart Controls directement en important les assemblies adaptées. L’objectif étant lors d’automatisation de tâches de pouvoir grapher les résultats les rendant ainsi beaucoup plus lisibles.

Le but étant de pouvoir générer des graphs de ce type

Ce dont on a besoin :

  • PowerShell V1 (ou supérieur)
  • Framework .NET 3.5
  • Microsoft Chart Controls

Première étape, on importe les assemblies

[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms.DataVisualization")

Pour générer notre graph nous allons simuler un traitement par lot ayant retourné des codes retours de 3 types: OK, ERROR, UNKNOW

$nbError = random(1..10)
$nbOK = random(1..3)
$nbUnknow = random(1..10)

$labelError = "Erreur ("+$nbError.toString()+")"
$labelOK = "OK ("+$nbOK.toString()+")"
$labelUnknow = "Unknow ("+$nbUnknow.toString()+")"

Nous créons une liste contenant nos labels ainsi que le nombre qui lui est associé en fonction de son type

$resultList = @{$labelError=$nbError.toString();
$labelOK=$nbOK.toString();
$labelUnknow=$nbUnknow.toString(); }

Nous créons maintenant l’objet graph et définissons sa taille

$Chart = New-object System.Windows.Forms.DataVisualization.Charting.Chart
$Chart.Width = 500
$Chart.Height = 400
$Chart.Left = 50
$Chart.Top = 30

On crée ensuite un chartarea dans lequel on ajoutera le graph

$ChartArea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
$Chart.ChartAreas.Add($ChartArea)

On ajoute les données du graph à partir de la liste

[void]$Chart.Series.Add("Data")
$Chart.Series["Data"].Points.DataBindXY($resultList.Keys, $resultList.Values)

On définit le type de graph

$Chart.Series["Data"].ChartType = [System.Windows.Forms.DataVisualization.Charting.SeriesChartType]::Pie

On définit ensuite les couleurs pour les données du  graph. Par exemple ici, nous définissons que la valeur minimum sera en rouge (et max en bleu) en utilisant la méthode FindMinByValue FindMaxByValue)

$maxValuePoint = $Chart.Series["Data"].Points.FindMaxByValue()
$maxValuePoint.Color = [System.Drawing.Color]::lightblue
$minValuePoint = $Chart.Series["Data"].Points.FindMinByValue()
$minValuePoint.Color = [System.Drawing.Color]::Red

On définit les options du graph

$Chart.Series["Data"]["PieLabelStyle"] = "Outside"
$Chart.Series["Data"]["PieLineColor"] = "Black"
$Chart.Series["Data"]["PieDrawingStyle"] = "Concave"
($Chart.Series["Data"].Points.FindMaxByValue())["Exploded"] = $true

Pour afficher le graph, il est nécessaire de créer un objet de type Windows.Form.Form dans lequel on ajoutera l’objet graph

$Form = New-Object Windows.Forms.Form
$Form.Text = "Powershell/MSChart"
$Form.Width = 600
$Form.Height = 500
$Form.controls.add($Chart)
$Form.Add_Shown({$Form.Activate()})
$Form.ShowDialog()

Et voila le résultat

mschart1

Pour plus d’informations sur MSChart je viens invite a visiter cette page ou vous pourrez aussi trouver des samples (vbs/C#)

Appeler des fonctions PowerShell depuis du WPF

De la même façon, il est possible d’utiliser du WPF depuis PowerShell en important les assemblies adaptées. Dans cet exemple nous allons créer un mini Task-Manager permettant d’afficher les ressources utilisées par un processus.

On importe l’assembly

[Reflection.Assembly]::LoadWithPartialName("PresentationFramework")

Il est nécessaire de créer une interface en XAML pour notre interface, Expression blend est parfait pour faire ça
xaml1

On stocke le code XAML généré par blend

$xaml = [xml]'

Selectionner un process

'

On récupère la liste des processus en cours d’exécution. Pour simplifier la suite du code, je fais fi des doublons en utilisant le pipe unique

$Processes = Get-Process | unique | Sort-Object -Property WS | Select-Object Name,WS,ID
$ProcNames = @(foreach($Proc in $Processes){$Proc.Name})

On utilise le code XAML pour créer notre fenêtre

$XAML=(New-Object System.Xml.XmlNodeReader $xaml)
$XAMLR=[Windows.Markup.XamlReader]::Load($XAML)

On bind nos variables

$PR = $Form.FindName('ComBoxProcess')
$OU = $Form.FindName('TextBoxProcess')

On itére sur les processus pour les ajouter à notre liste

foreach ($row in $ProcNames) {
[void]$PR.Items.Add($row)
}

On utilise la méthode add_SelectionChanged pour que lorsque qu’un processus différent est sélectionné, le TextForm soit actualisé avec les bonnes données.

$PR.add_SelectionChanged({
$OU.clear()
$Proc = Get-Process -Name $PR.SelectedItem | unique
$disp = (($Proc.WS)/1MB).tostring()
$disp +=" MB"+[environment]::NewLine
$disp += ($Proc.Handles).tostring()
$disp +=" Handles"+[environment]::NewLine
$OU.AppendText($disp)
})

Et enfin, on affiche !

[void]$XAMLR.ShowDialog()

Et voila le résultat
wpf
Voila pour cette introduction aux interfaces et au graph au travers de Powershell, dans un prochain billet, je reviendrais plus en détail sur les synergies .NET/Powershell

SharePoint Server 2010

La team SharePoint a récemment annoncé que SharePoint 2010 serait uniquement livré en version 64 bits. Petit à petit, Microsoft pousse les entreprises a se tourner uniquement vers des version 64 bits pour faire fi des limitations du 32 bits et ainsi s’affranchir de devoir maintenir les versions pour deux architectures différentes (Windows Server 2008 R2 est lui aussi uniquement disponible en 64 bits).

Mais revenons en à SharePoint Server 2010. Il ne sera utilisable que sur un Windows Server 2008 (64bits) ou Windows Server 2008 R2 (64 bits) et nécessitera obligatoirement un SQL Server 2005 ou 2008 (en 64 bits là encore).

Par ailleurs, nous apprenons que les navigateurs compatibles avec la nouvelle mouture seront Explorer 7+ et Firefox 3+ ainsi que Safari 3+ afin de respecter les standards XHTML 1.0 (Je vous l’accorde Microsoft a toujours du mal a respecter les standards Web, et c’est encore le cas de IE 8).

En ce qui concerne Internet Explorer 6, l’équipe a annoncé qu’il ne sera pas officiellement supporté malgré qu’il représente encore aujourd’hui 20% des parts de marché des navigateurs (Mars 2009). Cependant, le support d’IE 6 s’arrêtera officiellement en juillet 2010.

Redimensionner une image disque VMware

Il vous est peut être déjà arrivé, de monter une machine virtuelle VMware pour une maquette, mais de voir un peu court au niveau du disque dur. Une solution existe pour palier à ce problème, c’est VMware Converter.
0

Continue reading « Redimensionner une image disque VMware »

Cluster Asterisk: IP virtuelle

Après cette introduction à Asterisk, passons maintenant à la partie concrète de la mise en place de notre solution haute disponibilité (HA). Dans cet exemple nous considérons que nous avons deux serveurs organisés comme suis :

  • SrvAsterisk-1 : 192.168.1.201
  • SrvAsterisk-2 : 192.168.1.202
  • L’adresse P virtuelle pour y accéder sera : 192.168.1.200

Nous allons dans cet article nous atteler a mettre en place une IP virtuelle qui assura une continuité de service lors de la perte d’un des serveurs.

Continue reading « Cluster Asterisk: IP virtuelle »

Asterisk, le PBX open source par excellence

Cet article est les prémisses d’une série d’articles à venir sur Asterisk et sur sa mise en place via des solutions haute disponibilité (HA). Asterisk est un PABX open source pour systèmes GNU/Linux, publié sous licence GPL. Il permet, entre autres, la messagerie vocale, les conférences, les files d’attente, les agents d’appels, les musiques d’attente et bon nombre de fonctionnalités qui le placent au niveau des PBX les plus complexes.

Au sein des très grandes installations, il est courant de répartir les fonctionnalités sur plusieurs serveurs. L’un sera dédié au traitement des appels et celui-ci sera épaulé par des serveurs secondaires traitant d’autres tâches comme la base de données, les boîtes vocales ou les conférences. Pour des installations plus petites, un serveur suffisamment costaud est capable de gérer l’ensemble assez facilement et sera épauler par un serveur de backup. Continue reading « Asterisk, le PBX open source par excellence »

Les WAFS

Jusqu’à peu la gestion des sites distants était un vrai calvaire pour les ressources informatiques d’une entreprise. Le coût actuel de cet éclatement informatique est jugé exorbitant par bon nombre d’entre elles que ce soit en termes de ressources, de matériel, ou de prestations. Les architectures montrant souvent trop d’hétérogénéité, augmentant les coûts liés à la prestation externe et à l’utilisation des ressources internes. Ils engendrent par ailleurs des coûts d’administration importants notamment pour l’exécution des procédures de sauvegarde ainsi que pour les coûts de maintenance.

Pour faire face à ces couts, l’idée a été de centraliser les données, les bases de données, les serveurs antivirus et de messagerie ainsi que les applications métiers dans un seul est même lieux, dans un Datacenter. Mais cette solution conduit le plus souvent à diminuer fortement les performances du réseau pour les utilisateurs. Les temps d’accès aux ressources ainsi que les besoins en bande passante sur la partie WAN du réseau explosent. En effet les protocoles de gestion de fichiers n’ayant pas été conçu pour une utilisation WAN, ils se révèlent être très bavard et donc très gourmands. Mais heureusement les WAFS ont fait leur apparition. Continue reading « Les WAFS »

Déployer un OS Linux via WDS

Continuons dans la série WDS (Windows deployment Service). Déployer des Windows c’est bien, mais j’ai aussi besoin de déployer du Linux moi. Comment je fais, c’est pas possible ? Et bien si ! Je vous livre ici une méthode très inspirée de ce billet (US).

Nous allons ici déployer un Ubuntu 8.04 x86. Je vous invite bien évidemment à ne pas troller dans les commentaires sur les différentes distrib ;)

Pré-requis:

Gauthier Chanliau

Vous trouvez plus d'informations sur moi sur mon site personnel

FlickR

The teamIC 09 French FinaleHelp'Aged - GamesTour Eiffel - IC 09 French final