Hauptteil

Dieser Teil des Skriptes richtet sich am Inhalt der manuellen Installation des AD anhand der Übungen des vergangenene Schuljahres aus. Es wird davon ausgegangen, dass die AD-Module des Powershelle aktiviert sind. Im Unterschied zu den alten Namen wird jeweils bei allen Objekten das Kürzel PS hinzugefügt.

Im unserem Beispiel wird die Anlage des AD mit Hilfe der Powershell in folgenden Schritten ablaufen.

  1. Anlegen der Organisatorischen Einheiten
  2. Anlegen der Gruppen
  3. Anlegen der User
  4. Zuordnen der User zu den Gruppen
  5. Anlegen der Homeverzeichnisse der User inkl. Rechte
  6. Zuordnen der Homeverzeichnisse zum jeweiligen User

Aus UML-Sicht könnte sich folgendes Usecase-Diagramm ergeben.

_images/uc_ad_verwalten.png

Nützliche CommandLets

Da man in den Skripten immer wieder auf einige Informationen des ActiveDirectory zugreifen muss, sind einige CommandLets der PS sehr wertvoll.

Dazu gehören:

  • get-ADDomain: Liefert Informationen zum Domänennamen bzw. DistinguishedName
  • get-ADDomainController
  • get-ADGroupMember

Herangehensweise

Das zu schreibende Skript soll möglichst wenig “hart” kodiert sein, sondern sich seine Werte und Strukturinformationen aus der csv-Datei holen.

Aus der Struktur der csv-Datei können folgende Informationen abgeleitet werden.

  1. Es gibt verschiedene User mit bestimmten Eigenschaften wie Name, Vorname, LoginName
  2. Es gibt verschiedene globale Gruppen ohne eine bestimmte hierarchiache Anordnung
  3. Es gibt zwei Ebenen der Organisatorischen Einheiten. Manche OUs haben UnterOUs.
  4. User sind immer einer OU zugeordnet; dies kann eine OU der 1. Ebene oder eine OU der 2. Ebene sein.
  5. User können mehreren Gruppen zugeordnet sein; eine Gruppe kann mehrere User beinhalten.

Organisatorische Einheiten

Um eine Trennung zur bereits vorhandenen Installation zu erkennen, wird unterhalb der OU CREATIV.STE eine neue OU PS_CREATIV erzeugt. Innerhalb dieser werden dann alle OUs der Firma angelegt.

Das Erzeugen von OUs erfolgt mit Hilfe des CommandLets New-ADOrganizationalUnit. Die Online-Hilfe zeigt mehrere Beispiele für die benutzung des CommandLets. Wir verwenden folgenden Befehl.

New-ADOrganizationalUnit -Name "UserAccounts" -Path "DC=FABRIKAM,DC=COM" -ProtectedFromAccidentalDeletion $False

Um das Skript flexibel zu halten, wird in einem ersten Schritt aus der vorhandenen csv-Datei die OU-Namen der ersten und zweiten Hierarchie extrahiert. Diese Informationen sind wichtig für den Namen der OU bzw. für den Pfad-Parameter. Dieser setzt sich wie folgt zusammen.

  • OU der ersten Ordnung: Basis-DistinguishedName der Domäne

    New-ADOrganizationalUnit -Name "HauptOU -Path "DC=FABRIKAM,DC=COM"
    -ProtectedFromAccidentalDeletion $False
    
  • OU der zweiten Ordnug: OU-Name der ersten Ebene + Basis-Distinguished-Name der Domäne

    New-ADOrganizationalUnit -Name "UnterOU" -Path "OU=HauptOU,
    DC=FABRIKAM,DC=COM" -ProtectedFromAccidentalDeletion $False
    

Dazu wird folgende Vorgehensweise angewendet:

  • Import der csv-zeilen mit Hilfe des import-csv-CommandLets
  • Herausfiltern der Haupt-OUs und speichern in einem Hash OU_HList. Der Hash hat als Key den OU-Namen und als Value den DistinguishedName.
  • Herausfiltern der UnterOUs in einen Hash OU_UList. Der Hash hat als key ebenfalls den OU-Namen, als Value trät er aber einen um die Haupt-OU erweiterten DistinguishedName.

Die Value-Eigenschaften des Hashes werde dem Path-Parameter des CommandLets übergeben.

Das Skript sieht abschließend wie folgt aus:

$DebugPreference = "Continue"

$domainInfos = get-ADDomain
$baseDN = "OU=_CREATIVE,"+$domainInfos.DistinguishedName
$OU_HList = @{} #Aufname der HauptOUs
$OU_UList = @{} #Aufnahme der UnterOUs


$result = Import-Csv C:\temp\ad_struktur_eng.csv

#$result

function set-OUPath
{

    begin{
        Write-Debug "Adding OU-Infos zu imported Objects"
    }

    process{
       # Write-Debug "in process"

        #NAme der OU
                #Add-Member -InputObject $_ -Name OUName -Value "" -MemberType NoteProperty
        #Pfad von
                #Add-Member -InputObject $_ -Name OU_DN_PATH -Value "" -MemberType NoteProperty

        #Haupt-OUs werden immer angelegt
        $OUName = $_.HauptOU
                $OU_DN_PATH = "" + $baseDN

                $OU_HList[$OUName] = $OU_DN_PATH


        #falls UnterOU vorhanden
        if($_.UnterOU -ne "")
        {
            $OU_DN_PATH = "OU="+$_.HauptOU+"," + $baseDN
                        $OUName = $_.UnterOU
                $OU_UList[$OUName] = $OU_DN_PATH
                }

    }

    end{
        Write-Debug "Finished"
    }
}

$result | set-OUPath


 foreach($element in $OU_HList.Keys)
        {
                New-ADOrganizationalUnit -Name $element -Path $OU_HList[$element] -ProtectedFromAccidentalDeletion $False
        }


        foreach($element in $OU_UList.Keys)
        {
                New-ADOrganizationalUnit -Name $element -Path $OU_UList[$element] -ProtectedFromAccidentalDeletion $False

        }


function remove-AllOUs
{
    foreach($element in $OU_HList.Keys)
    {
        $DN = "OU="+ $element+ "," + $OU_HList[$element]
        #Write-Host $DN
        #Remove-ADOrganizationalUnit -Identity "'OU='+ $element + ',' + $OU_HList[$element]" -confirm:$false -Recursive:$true
        Remove-ADOrganizationalUnit -Identity $DN -confirm:$false -Recursive:$true
    }
}


#remove-AllOUs

Gruppen

Das Anlegen der Gruppen kann analog wie bei den OUs vorgenommen werden. Da eine hierarchische Anordnung entfällt, kann mit lediglich einem Hash gearbeitet werden.

Die Anlage erfolgt mit Hilfe des CommandLets New-ADGroup im Rahmen einer Pipelineverarbeitung. Der Quellcode wird mit Hilfe einer Funktion organisiert, die aus 3 Bereichen besteht. Die Funktion ist für die Verarbeitung innerhalb einer Pipeline ausgelegt.

  • begin: Erstellen der OU zur Aufnahme der Gruppen
  • process: Auslesen der Gruppen aus der csv-Datei und Speichern im Hash $Group_List
  • end: Anlegen der Gruppen durch Auslesen des Hashes mit Hilfe des New-ADGroup-Commandlets.
function set-Groups
{
    begin{

        $Group_List.Clear()
        New-ADOrganizationalUnit -Name Gruppen -Path $baseDN -ProtectedFromAccidentalDeletion $False

    }

    process{

        $Group_List[$_.Globale_Gruppe1]=$_.Globale_Gruppe1

        if($_.Globale_Gruppe2 -ne "")
        {
            $Group_List[$_.Globale_Gruppe2]=$_.Globale_Gruppe2
        }

        if($_.Globale_Gruppe3 -ne "")
        {
            $Group_List[$_.Globale_Gruppe3]=$_.Globale_Gruppe3
        }

    }

    end{

        #New-ADGroup -name Personal.G -Path "OU=gruppen, OU= _CREATIVE, DC=CREATIV, DC=STE"
        $Group_List
         foreach($element in $Group_List.Keys)
        {
            $path = "OU=Gruppen, " + $baseDn + ""
                New-ADGroup -Name $Group_List[$element] -Path $path  -GroupScope Global
        }

    }
}

User

Im nächsten Schritt erfolgt nun das Erstellen der User. Eine erste Schwierigkeit besteht im Bestimmen der jeweiligen OU, innerhalb derer der User angelegt werden soll. Dies ist abhängig vom Wert der Eigenschaft Unter_OU. Falls diese gesetzt ist, muss der User dort angelegt werden, ansonsten in der Haupt-OU.

Der distinguishedName des jeweiligen User-Onjektes ist dahingehend anzupassen. Da diese Eigenschaft eventuell häufiger gebraucht wird, ist es überlegenswert, die Eigenschaften der Pipeline-Objekte um weitere Eigenschaften zu erweitern. Dies erfolgt im Skript bereits in der Funktion set-OUs, da diese zuerst in der Ablaufsteuerung aufgerufen wird.

function set-OUs
{

    process{
       # Write-Debug "in process"


      #Objekte der Pipeline erhalten eine neue Eigenschaft
      #DistinguishedName
      Add-Member -InputObject $_ -Name DName -Value "" -MemberType NoteProperty

      ....
      ....

       #Setzen des DistiguishedName zur einfacheren Anlage des Users
       $_.DName = "OU="+ $_.HauptOU +", " + $baseDN



       #falls UnterOU vorhanden
       if($_.UnterOU -ne "")
       {
         ...
         ...
         #Dname für Unter-OUs
         $_.DName = "OU=" +$_.UnterOU +"," + $OU_DN_PATH
      }

    }
}

Die Funktion set-User kann dann sehr bequem diese Informationen für die path-Eigenschaft des CommandLets new-ADUser verwenden

function  set-User
{
    begin{
        Write-Debug "Adding User"
    }

    process{

        #write-host $_.DName
        New-ADUser -Path $_.DName -SamAccountName $_.Anmeldename -Name $_.Name  -AccountPassword (ConvertTo-SecureString -force -AsPlainText "Pa$$w0rd") -ChangePasswordAtLogon $true
    }

    end{
        write-debug "finished adding user"
    }

}

Zuordnung User-Gruppen

Die Zuordnung der User zu den Gruppen erfolgt mit Hilfe des CommandLets Add-ADGroupMember.

Eine Analyse der Online-Hilfe zu diesem Commandlet ergibt folgende Anwendungsmöglichkeit:

#Add-ADGroupMember SvcAccPSOGroup SQL01,SQL02
#Adds the user accounts with SamAccountNames SQL01,SQL02 to the group SvcAccPSOGroup.
  • SamAccountName: dies ist der bereits für den User vergebene Anmeldename
  • GroupName: Der Gruppenname der bereits angelegten Gruppen

Da unsere User unter Umständen mehreren Gruppen zugeordnet sein können, benötigen wir einen Mechanismus für das wiederholte Ausführen des Befehls für einen vorgegebenen User (Schleife).

Wie schon beim distinguishedName der set-User-Funktion zeigt sich, dass es sinnvoll wäre, die Information über die Gruppenzugehörikeiten in einer eigenen Eigenschaft des User-Objektes zu kapseln. Um die set-OU-Funktion nicht weiter mit Ballast zu füllen, wird entschieden, das Erstellen der notwendigen Eigenschaften in eine eigene Funktion auszulagern und diese als Erstes in der Pipeline aufzurufen.

Die Many-Beziehung des User-Objektes zu Group-Objekten wird damit realisiert.

function prepareUser {
<#
.synopsis
    Adds properties to each User-Object to handle AD-Management better
    Groups
    SamAccountName
    DistinguishedName

    Values for this properties are sometimes set in other functions
#>

    begin{
        Write-Debug "Adding Properties"
    }

    process{
        #Gruppenzugehörigkeit
        $myGroups = @() #ArrayList for adding GroupName to User-Object
        Add-Member -InputObject $_ -Name Gruppen -Value $myGroups -MemberType NoteProperty

        $myGroups += $_.Globale_Gruppe1

        if($_.Globale_Gruppe2 -ne "")
        {
            $myGroups += $_.Globale_Gruppe2

        }

        if($_.Globale_Gruppe3 -ne "")
        {
            $myGroups += $_.Globale_Gruppe3

        }

        $_.Gruppen = $myGroups


        #DistinguishedName
        Add-Member -InputObject $_ -Name DName -Value "" -MemberType NoteProperty
    }
}

Diese neue Eigenschaft kann dann in der Funktion addUserToGroups verwendet werden.

function addUserToGroups
{

        begin{
            Write-Debug "Adding User to Groups"
        }

        process{

            foreach($gruppe in $_.Gruppen)
            {
                #Add-ADGroupMember SvcAccPSOGroup SQL01,SQL02
                #Adds the user accounts with SamAccountNames SQL01,SQL02 to the group SvcAccPSOGroup.

                Add-ADGroupMember $gruppe $_.Anmeldename
            }
        }

        end{
            Write-Debug "Finished Adding User To Groups"
        }
}

Homeverzeichnisse anlegen

http://blogs.technet.com/b/heyscriptingguy/archive/2013/06/04/creating-a-home-drive-with-windows-powershell-part-1.aspx

http://blogs.technet.com/b/heyscriptingguy/archive/2013/06/04/creating-a-home-drive-with-windows-powershell-part-2.aspx

http://blogs.technet.com/b/heyscriptingguy/archive/2013/06/06/creating-a-home-drive-with-windows-powershell-part-3.aspx

http://gallery.technet.microsoft.com/scriptcenter/How-to-create-home-folder-d968f1d4

http://mikefrobbins.com/2014/06/26/create-active-directory-users-home-folder-and-assign-permissions-with-powershell/

Problematik

Die Homeverzeichnisse der User sollten in unserem Beispiel unterhalb des Root-Verzeichnisses C:Home angelegt werden.

Innerhalb des AD müssen für den jeweiligen User-Account die Eigenschaften wie HomeDrive und HomeDirectory gesetzt werden. Dies kann bereits beim Erzeugen der User vorgenommenn werden – bedeutet aber nicht, dass der Ordner und die korrekten Rechte damit tatsächlich erzeugt worden wären.

Das reine Erstellen von Ordnern in Powershell stellt keine besondere Aufgabe dar.

NEW-ITEM –path $HomeDirectory -type directory -force

Problematischer ist schon das Zuweisen der entsprechenden NTFS-Rechte. Um dies in einer konsistenten Weise zu bewerkstelligen, zieht die PowerShell das .NET-Framework zu Hilfe.

Die EIngabe von

GET-ACL C:\Users | GET-MEMBER

gibt folgendes Ergebnis.

_images/get_acl_member.png

Die oberste Zeile gibt an, welche Klasse des .NET-Frameworks benutzt wird. Mit diesem Wissen kann man in den entsprechenden .NET-MSDN-Dokumentation weiter stöbern.

grundsätzlich interessieren uns aber nur zwei Dinge – nämlich den Eigentümer des jeweiligen Objektes und die jeweiligen Zugriffsregeln

Im Falle der NTFS-Rechte müssen wir ein Regelwerk aufbauen, welches folgenden Anforerungen entspricht.

  • Keine Rechte weren vom übergeordneten Ordner geerbt
  • Domänenadmins haben vollen Zugriff auf die Ordnerstruktur
  • Der User, für den der Ordner definiert wurde, aht vollen Zugriff auf diesen Ordner.

Um dieses Ziel zu erreichen, müssen wir eine Liste von Zugriffsregeln für diesen Ordner definieren. Nach der Definition müssen diese Regeln mit Hilfe des Set-ACL Cmdlets angewendet werden.

Für den ordner C:Users gelten nach einer Standardinstallation folgende Regeln.

_images/get_acl_c_users_access.png

Wir erhalten einen Array von Regeln zurück. Auf die einzelnen Regeln kann man wie bei einem normalen Array zugreifen

_images/get_acl_c_users_access_array.png

Um eine neue Zugriffsregel zu erstellen, muss man zunächst wissen, welcher .NET-Typ sich dahinter versteckt.

_images/get_acl_rule_type.png

Wie man sieht, ist die

From the output returned, we can see from “TypeName” that the access rules are from [System.Security.AccessControl.FileSystemAccessRule].

We need to define five components to create an access rule. You will see them as five of the properties returned from an access rule. They are:

IdentityReference FileSystemAccessRights InheritanceFlags PropagationFlags AccessControlType

IdentityReference will be a user name within Active Directory or the local system. The IdentityReference is strictly a string that contains a valid account name. It can be a user or a group for our purposes. We will define the IdentityReference for our first rule as ‘CONTOSOJohnnyTest’:

$IdentityReference=’CONTOSOJohnnyTest’

FileSystemAccessRights identifies what type of access we are defining, whether it is Full Access, Read, Write, Modify or any of several rights that are available to apply to an object in file system. This is a [System.Security.AccessControl.FileSystemRights] object. For our home folder, we are going to assign FullControl as the right we are defining:

$FileSystemAccessRights=[System.Security.AccessControl.FileSystemRights]”FullControl”

InheritanceFlags defines how the security propagates to child objects by default, such as to other files or folders. For the purposes of our home folder, we will allow the same rights on all files (objects) and folders (containers). This is a [System.Security.AccessControl.InheritanceFlags] object:

$InheritanceFlags=[System.Security.AccessControl.InheritanceFlags]”ContainerInherit, ObjectInherit”

PropagationFlags specifies which characteristics (if any) are to be inherited from the parent object. We want to have an exclusive set of rights on our Home folder. No rights will be inherited. This object is of the type [System.Security.AccessControl.PropagationFlags].

$PropagationFlags=[System.Security.AccessControl.PropagationFlags]”None”

AccessControlType actually defines if the rule we are creating is an Allow or Deny rule. That’s it. We are defining a way to allow access, and will set this rule as Allow. This object is of the type [System.Security.AccessControl.AccessControlType]:

$AccessControl=[System.Security.AccessControl.AccessControlType]”Allow”

When we have defined the five objects for our access rule, we need to create the actual access rule, which is an object in itself. Do you remember TypeName from when we ran Get-Member against the access rule? We will be creating an object of this type, which was [System.Security.AccessControl.FileSystemAccessRule].

Note The definition for how to create an access rule is on MSDN if you use System.Security.AccessControlFileSystemAccessRule as your search parameter. It is referenced as a constructor.

To create an access rule, we need to pass the five parameters we have defined to the [System.Security.AccessControl.FileSystemAccessRule] object:

$AccessRule=NEW-OBJECT [System.Security.AccessControl.FileSystemAccessRule]($IdentityReference,$FileSystemAccessRights,$InheritanceFlags,$PropogationFlags,$AccessControl)

part 3

To add the rule, we perform the following four steps:

Get the current access control list from the folder in question by using Get-ACL. Build a new access rule for our user. Run the AddAccessRule method against the current ACL object. Store the new access control list on the folder with Set-ACL.

As simple as it sounds, use Get-ACL on the new user home folder to store the object into a Windows Powershell variable for editing and reuse:

$HomeFolderACL=GET-ACL \CONTOSO-FPSUsers$JohnnyTest

Next build the access rule. In our case, this is for JohnnyTest in the CONTOSO domain:

$IdentityReference=’CONTOSOJohnnyTest’

$FileSystemAccessRights=[System.Security.AccessControl.FileSystemRights]”FullControl”

$InheritanceFlags=[System.Security.AccessControl.InheritanceFlags]”ContainerInherit, ObjectInherit”

$PropagationFlags=[System.Security.AccessControl.PropagationFlags]”None”

$AccessControl=[System.Security.AccessControl.AccessControlType]”Allow”

$AccessRule=NEW-OBJECT [System.Security.AccessControl.FileSystemAccessRule]($IdentityReference,$FileSystemAccessRights,$InheritanceFlags,$PropogationFlags,$AccessControl)

Now add the rule to the access list that is presently stored in $HomeFolderACL:

$HomeFolderACL.AddAccessRule($AccessRule)

Then store the new access rule on the folder with Set-ACL:

SET-ACL –path \CONTOSO-FPSUsers$JohnnyTest -AclObject $HomeFolderACL

At this point, we’ll take these new changes and build a small script in Windows Powershell to properly do all the work for us—right down to creating the folder on the remote server. New-Homedrive.ps1 will look like this:

New-Homedrive.ps1

PARAM(

$Alias

)

# Assign the Drive letter and Home Drive for

# the user in Active Directory

$HomeDrive=’Z:’

$UserRoot=’\CONTOSO-FPSUsers$’

$HomeDirectory=$UserRoot+$AccountName

SET-ADUSER $Alias –HomeDrive $HomeDrive –HomeDirectory $HomeDirectory

# Create the folder on the root of the common Users Share

NEW-ITEM –path $HomeDirectory -type directory -force

$Domain=’CONTOSO’

$IdentityReference=$Domain+’’+$Accountname

# Set parameters for Access rule

$FileSystemAccessRights=[System.Security.AccessControl.FileSystemRights]”FullControl”

$InheritanceFlags=[System.Security.AccessControl.InheritanceFlags]”ContainerInherit, ObjectInherit”

$PropagationFlags=[System.Security.AccessControl.PropagationFlags]”None”

$AccessControl=[System.Security.AccessControl.AccessControlType]”Allow”

# Build Access Rule from parameters

$AccessRule=NEW-OBJECT [System.Security.AccessControl.FileSystemAccessRule]($IdentityReference,$FileSystemAccessRights,$InheritanceFlags,$PropogationFlags,$AccessControl)

# Get current Access Rule from Home Folder for User

$HomeFolderACL.AddAccessRule($AccessRule)

SET-ACL –path $HomeDirectory -AclObject $HomeFolderACL

Now, I could have said, “Here’s a script to do this.” But I think knowing some of the deeper pieces of the process should help you understand how to manipulate the data for Set-ACL.

At the end of it all? Take this script, use it, be more efficient and consistent, and get yourself home a lot earlier. That’s where it all really matters.

Homeverzeichnisse zuordnen