+ Post New Thread
Results 1 to 11 of 11
Scripts Thread, Powershell create user and setup home folder - error when running quickly in Coding and Web Development; ...
  1. #1
    morganw's Avatar
    Join Date
    Apr 2009
    Location
    Cambridge
    Posts
    816
    Thank Post
    46
    Thanked 132 Times in 126 Posts
    Rep Power
    40

    Powershell create user and setup home folder - error when running quickly

    I've pieced together a powershell script based on the Quest cmdlets that will provision users, set attributes, add to groups, create a home folder, share the home folder, set security on the home folder. I created this in a virtual environment with one DC and had no issues, when running on the new production environment I will get intermittent errors when adding a user to a security group or when setting the file security. The problems always relate to the username being invalid, but if I slow down the process by inserting 5 second delays at various points will always seems to work. I initially presumed this was down to the AD user object not existing in time to be used later in the script but i've add loops to wait until the user exists and the loop are never triggered (therefore the user must exist at that point in the script).
    If I rerun the import the users that the script errors on are not the same every time, the only thing I can think of is that part of the script is working with the primary DC, the next part is connecting to the backup DC (or vice versa). If this is the case is there anywhere to force a particular DC when using the Quest cmdlets? Or does anyone know of any other reason why a delay of a few seconds is enough to make the script run properly. Any help appreciated.
    Morgan

    Code:
    Function New-SecurityDescriptor (
    $ACEs = (throw “Missing one or more Trustees”),
    [string] $ComputerName = “.”)
    {
    #Create SeCDesc object
    $SecDesc = ([WMIClass] “\\$ComputerName\root\cimv2:Win32_SecurityDescriptor”).CreateInstance()
    #Check if input is an array or not.
    if ($ACEs -is [System.Array])
    {
    #Add Each ACE from the ACE array
    foreach ($ACE in $ACEs )
    {
    $SecDesc.DACL += $ACE.psobject.baseobject
    }
    }
    else
    {
    #Add the ACE
    $SecDesc.DACL = $ACEs
    }
    #Return the security Descriptor
    return $SecDesc
    }
    
    Function New-ACE (
    [string] $Name = (throw “Please provide user/group name for trustee”),
    [string] $Domain = (throw “Please provide Domain name for trustee”),
    [string] $Permission = “Read”,
    [string] $ComputerName = “.”,
    [switch] $Group = $false)
    {
    #Create the Trusteee Object
    $Trustee = ([WMIClass] “\\$ComputerName\root\cimv2:Win32_Trustee”).CreateInstance()
    #Check for Special cases Everyone and Authenticated Users)
    switch ($Name.ToUpper()) {
    “EVERYONE” {
    $Trustee.Domain = $Null
    $Trustee.Name = “EVERYONE”
    $Trustee.SID = @(1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0)
    }
    “AUTHENTICATED USERS” {
    $Trustee.Domain = “NT AUTHORITY”
    $Trustee.Name = “Authenticated Users”
    $Trustee.SID = @(1, 1, 0, 0, 0, 0, 0, 5, 11, 0, 0, 0)
    }
    default {
    #Search for the user or group, depending on the -Group switch
    if (!$group)
    { $account = [WMI] “\\$ComputerName\root\cimv2:Win32_Account.Name=’$Name’,Domain=’$Domain’” }
    else
    { $account = [WMI] “\\$ComputerName\root\cimv2:Win32_Group.Name=’$Name’,Domain=’$Domain’” }
    #Get the SID for the found account.
    $accountSID = [WMI] “\\$ComputerName\root\cimv2:Win32_SID.SID=’$($account.sid)’”
    #Setup Trusteee object
    $Trustee.Domain = $Domain
    $Trustee.Name = $Name
    $Trustee.SID = $accountSID.BinaryRepresentation
    }
    }
    #Create ACE (Access Control List) object.
    $ACE = ([WMIClass] “\\$ComputerName\root\cimv2:Win32_ACE”).CreateInstance()
    #Select the AccessMask depending on the -Permission parameter
    switch ($Permission)
    {
    “Read” { $ACE.AccessMask = 1179817 }
    “Change” { $ACE.AccessMask = 1245631 }
    “Full” { $ACE.AccessMask = 2032127 }
    default { throw “$Permission is not a supported permission value. Possible values are ‘Read’,'Change’,'Full’” }
    }
    #Setup the rest of the ACE.
    $ACE.AceFlags = 3
    $ACE.AceType = 0
    $ACE.Trustee = $Trustee
    #Return the ACE
    return $ACE
    }
    
    Function New-Share (
    [string] $FolderPath = (throw “Please provide the share folder path (FolderPath)”),
    [string] $ShareName = (throw “Please provide the Share Name”),
    $ACEs,
    [string] $Description = “”,
    [string] $ComputerName = “.”,
    $MaxUsers = $null,
    $Password = $null)
    {
    #Start the Text for the message.
    $text = “$ShareName ($FolderPath): ”
    #Package the SecurityDescriptor via the New-SecurityDescriptor Function.
    $SecDesc = New-SecurityDescriptor $ACEs
    #Create the share via WMI, get the return code and create the return message.
    $Share = [WMICLASS] “\\$ComputerName\Root\Cimv2:Win32_Share”
    $result = $Share.Create($FolderPath, $ShareName, 0, $MaxUsers, $Description, $Password, $SecDesc)
    switch ($result.ReturnValue)
    {
    0 {$text += “has been success fully created” }
    2 {$text += “Error 2: Access Denied” }
    8 {$text += “Error 8: Unknown Failure” }
    9 {$text += “Error 9: Invalid Name”}
    10 {$text += “Error 10: Invalid Level” }
    21 {$text += “Error 21: Invalid Parameter” }
    22 {$text += “Error 22 : Duplicate Share”}
    23 {$text += “Error 23: Redirected Path” }
    24 {$text += “Error 24: Unknown Device or Directory” }
    25 {$text += “Error 25: Net Name Not Found” }
    }
    #Create Custom return object and Add results
    $return = New-Object System.Object
    $return | Add-Member -type NoteProperty -name ReturnCode -value $result.ReturnValue
    $return | Add-Member -type NoteProperty -name Message -value $text
    #Return result object
    $return
    }
    
    Function ProvisionInputCSV {
      Param ([string]$filename)
      $users = Import-CSV $filename
      foreach ($user in $users) {
        $ht = @{'givenName'=$user."First Name";
                'sn'= $user."Last Name";
                'displayName'= $user."First Name" + " " + $user."Last Name";
                'email'= $user."email";
                'password'= $user.Password;
                'samAccountName'= $user."Logon Name";
                'OU'= $user."OU";
                'HomeServer' = $user."HomeServer";
                'HomeServerDrive' = $user."HomeServerDrive";
                'HomeServerPath' = $user."HomeServerPath";
                'HomeDrive'= $user."HomeDrive";
                'NetBIOS'= $user."NetBIOS";
                'FQDN'= $user."FQDN Ext.";
                'ProfilePath'= $user."Profile";
                'TsProfilePath'= $user."TSProfile";
                'Staff'= $user."Staff";
                'Access Write Student Folders' = $user."Access Write Student Folders";
                'Access Read Student Folders' = $user."Access Read Student Folders";
                'Sept07'= $user."Sept07"
               }
        Write-Output $ht
      }
    }
    
    
    Function Provision {
      PROCESS {
        CreateUser $_
        CreateHomeFolder $_
        SetFileSecurity $_
        AddToGroup $_
      }
    }
    
    Function CreateUser {
      Param($userinfo)
    
    $homedirectory= "\\" + $userinfo['HomeServer'] + "\" + $userinfo['samAccountName'] + "$"
    $userprincipalname= $userinfo['samAccountName'] + "@" + $userinfo['NetBIOS'] + "." + $userinfo['FQDN']
    $OU= $userinfo['OU'] + ",DC=" + $userinfo['NetBIOS'] + ",DC=" + $userinfo['FQDN']
    $HomeDrive= $userinfo['HomeDrive'] + ":"
    
    New-QADUser -UserPrincipalName $userprincipalname –samAccountName $userinfo['samAccountName'] –ParentContainer $OU –FirstName $userinfo['givenName'] –LastName $userinfo['sn'] –Name ($userinfo['givenName'] + ' ' + $userinfo['sn']) –displayName ($userinfo['givenName'] + ' ' + $userinfo['sn']) -email $userinfo['email'] –userPassword $userinfo['password'] -HomeDrive $HomeDrive -HomeDirectory $homedirectory -ProfilePath $userinfo['ProfilePath'] | Enable-QADUser
    
    $u = get-qaduser -samaccountname $userinfo['samAccountName']
    $u.TsProfilePath = $userinfo['TsProfilePath']
    $u.CommitChanges()
    
    Start-Sleep -s 5
    
    }
    
    Function CreateHomeFolder {
      Param($userinfo)
    
      $folder= $userinfo['samAccountName']
      $completepath= "\\" + $userinfo['HomeServer'] + "\" + $userinfo['HomeServerDrive'] + "$\" + $userinfo['HomeServerPath'] + "\" + $folder
      $server= $userinfo['HomeServer']
    
      new-item $completepath -itemType Directory
    
      Start-Sleep -s 5
    
      $completepath= $userinfo['HomeServerDrive'] + ":\" + $userinfo['HomeServerPath'] + "\" + $folder
    
      #Create Share Permission
      $ACE = @(New-ACE -Name “Authenticated Users” -Domain “NT AUTHORITY” -Permission “Full” -Group)
      #Create the share
      $result = New-Share -FolderPath “$completepath" -ShareName "$folder$" -ACEs $ACE -Description “$folder” -Computer “$server” 
      #Output result message from new-share
      Write-Output $result.Message
    
    }
    
    Function SetFileSecurity {
      Param($userinfo)
    
    $folder = "\\" + $userinfo['HomeServer'] + "\" + $userinfo['HomeServerDrive'] + "$\" + $userinfo['HomeServerPath'] + "\" + $userinfo['samAccountName']
    $inherit = [system.security.accesscontrol.InheritanceFlags]"ContainerInherit, ObjectInherit"
    $propagation = [system.security.accesscontrol.PropagationFlags]"None"
    
    $acl = Get-Acl $folder
    
    #This removes existing permissions
    #$acl.SetAccessRuleProtection($true,$false)
    
    $username= $userinfo['samAccountName']
    $netbios= $userinfo['NetBIOS']
    
    Start-Sleep -s 5
    $loopvar= Get-QADUser -samaccountname $username
    While ($loopvar -eq $null) {
       Write-Output "User Doesn't Exist Yet"
       Start-Sleep -s 5
       $loopvar= Get-QADUser -samaccountname $username
       }
    
    $accessrule = New-Object system.security.AccessControl.FileSystemAccessRule("$netbios\$username", "Modify", $inherit, $propagation, "Allow")
    $acl.AddAccessRule($accessrule)
    
    #$accessrule = New-Object system.security.AccessControl.FileSystemAccessRule("$netbios\Domain Admins", "FullControl", $inherit, $propagation, "Allow")
    #$acl.AddAccessRule($accessrule)
    
    #$accessrule = New-Object system.security.AccessControl.FileSystemAccessRule("SYSTEM", "FullControl", $inherit, $propagation, "Allow")
    #$acl.AddAccessRule($accessrule)
    
    #$accessrule = New-Object system.security.AccessControl.FileSystemAccessRule("$netbios\Administrator", "FullControl", $inherit, $propagation, "Allow")
    #$acl.AddAccessRule($accessrule)
    
    set-acl -aclobject $acl $folder
    
    }
    
    
    Function AddToGroup {
      Param($userinfo)
    
    #Start-Sleep -s 2
    $username= $userinfo['samAccountName']
    $loopvar= Get-QADUser -samaccountname $username
    While ($loopvar -eq $null) {
       Write-Output "User Doesn't Exist Yet"
       Start-Sleep -s 5
       $loopvar= Get-QADUser -samaccountname $username
       }
    
    $groups = 'Staff','Access Read Student Folders','Access Write Student Folders','Sept07'
      Foreach ($group in $groups) {
        If ([int]$userinfo[$group]) {
          $currentgroup= $userinfo['NetBIOS'] + "\"+ $group
          $username= $userinfo['NetBIOS'] + "\" + $userinfo['samAccountName']
          Add-QADGroupMember -identity $currentgroup -member ($username)}
    }
    }
    
    ProvisionInputCSV C:\Scripts\import.csv | Provision

  2. #2

    Join Date
    Aug 2005
    Location
    London
    Posts
    3,156
    Thank Post
    116
    Thanked 529 Times in 452 Posts
    Blog Entries
    2
    Rep Power
    124
    I've not used powershell like this but I used to create accounts using vbscript and didn't have this sort of problem - I wouldn't have thought that powershell was doing anything that different under the hood but not really sure.

    It does sound like it's a problem where (say) the account is being created on the PDC emulator but but the rest of the script is working against another DC (possibly all working against another DC but "create user" requests are automatically forwarded to the PDCE)

    Can you set up a test domain (eg with VMs) and run your script against it with only 1 domain controller? If everything works then I would think it's definitely a timing issue.

    One other thing - are all your DCs global catalogs? If not, make them GCs (you need a DC to be a GC if it's going to process logons so it's a good idea unless you have a multi-domain forest)

  3. Thanks to srochford from:

    morganw (19th May 2010)

  4. #3

    Join Date
    Jan 2006
    Location
    Surburbia
    Posts
    2,178
    Thank Post
    74
    Thanked 307 Times in 243 Posts
    Rep Power
    115
    Just looked at the stuff you're talking about because it might be useful one day. There's a manual you can read which says this about New-QADUser:

    The cmdlet has optional parameters that determine the server and the security context for the operation.
    The connection parameters could be omitted since a connection to a server is normally established prior
    to using this cmdlet. In this case, the server and the security context are determined by the
    Connect-QADService ..
    Last edited by PiqueABoo; 19th May 2010 at 08:42 PM.

  5. Thanks to PiqueABoo from:

    morganw (19th May 2010)

  6. #4
    morganw's Avatar
    Join Date
    Apr 2009
    Location
    Cambridge
    Posts
    816
    Thank Post
    46
    Thanked 132 Times in 126 Posts
    Rep Power
    40
    Thanks guys, after having a read around I think I need to specify -service 'domaincontroller.fqdn' on each function that uses the Quest cmdlets.

  7. #5

    Join Date
    Jan 2006
    Location
    Surburbia
    Posts
    2,178
    Thank Post
    74
    Thanked 307 Times in 243 Posts
    Rep Power
    115
    I'd certainly try that - one way or another it should settle your (plausible) AD binding & replication theory.

  8. #6
    morganw's Avatar
    Join Date
    Apr 2009
    Location
    Cambridge
    Posts
    816
    Thank Post
    46
    Thanked 132 Times in 126 Posts
    Rep Power
    40
    All of the Quest cmdlets don't produce errors now, either by specifying -service 'domaincontroller' on each command, or by using the Connect-QADService to set the value globally for all other Quest cmdlets. I've got one problem left, this code doesn't use a Quest cmdlet so how would I force the object lookup onto a particular DC?

    Code:
    $acl = Get-Acl $folder
    
    $accessrule = New-Object system.security.AccessControl.FileSystemAccessRule("$netbios\$username", "Modify", $inherit, $propagation, "Allow")
    $acl.AddAccessRule($accessrule)
    
    set-acl -aclobject $acl $folder

  9. #7
    morganw's Avatar
    Join Date
    Apr 2009
    Location
    Cambridge
    Posts
    816
    Thank Post
    46
    Thanked 132 Times in 126 Posts
    Rep Power
    40
    Just got this sorted I think,

    Used a Quest cmdlet to get the user's SID, then used that to set the access rules rather than the username.

    For anyone else doing this who doesn't want to waste an hour wondering why it doesn't work, you need to pass [object].sid into the access rule not [object].sid.value.

  10. #8
    somabc's Avatar
    Join Date
    Oct 2007
    Location
    London
    Posts
    2,337
    Thank Post
    83
    Thanked 388 Times in 258 Posts
    Rep Power
    111
    Nice Work!

  11. #9

    Join Date
    Nov 2010
    Location
    USA
    Posts
    2
    Thank Post
    1
    Thanked 0 Times in 0 Posts
    Rep Power
    0
    Quote Originally Posted by morganw View Post
    Just got this sorted I think,

    Used a Quest cmdlet to get the user's SID, then used that to set the access rules rather than the username.

    For anyone else doing this who doesn't want to waste an hour wondering why it doesn't work, you need to pass [object].sid into the access rule not [object].sid.value.
    Could you provide the cmdlet used and possibly the syntax? Thanks!

  12. #10
    morganw's Avatar
    Join Date
    Apr 2009
    Location
    Cambridge
    Posts
    816
    Thank Post
    46
    Thanked 132 Times in 126 Posts
    Rep Power
    40
    Quote Originally Posted by Zelig View Post
    Could you provide the cmdlet used and possibly the syntax? Thanks!
    Get the sid for a user with this:
    Code:
    $userSID= (Get-QADUser -service 'DC.domain.com' -samaccountname $username).sid
    If you don't specify '-service' it will just use any domain controller

  13. Thanks to morganw from:

    Zelig (22nd November 2010)

  14. #11

    Join Date
    Nov 2010
    Location
    USA
    Posts
    2
    Thank Post
    1
    Thanked 0 Times in 0 Posts
    Rep Power
    0
    Awesome! This is exactly what I needed. Thanks for the reply.

SHARE:
+ Post New Thread

Similar Threads

  1. SIMS From Home running on a TS, setup help needed.
    By farquea in forum MIS Systems
    Replies: 16
    Last Post: 16th March 2010, 09:27 AM
  2. Home Folder Error Message
    By Neville in forum Mac
    Replies: 3
    Last Post: 5th February 2010, 08:46 AM
  3. User's Network Home Folder Permissions
    By Chuckster in forum Windows
    Replies: 4
    Last Post: 6th September 2009, 09:11 PM
  4. User home folder, unsure of problem
    By itschad in forum Wireless Networks
    Replies: 4
    Last Post: 12th September 2008, 05:03 PM
  5. Home Drive / User Area Setup
    By burgemaster in forum Windows
    Replies: 10
    Last Post: 21st February 2008, 12:43 PM

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •