Imagine, that half of your company users are local administrators at their machines. They pretty often reinstall operating systems and request IT Service Desk to join that newly installed OSes into corporate Active Directory domain. One usually has two options to help the users: either to come to the user’s workstation and use one’s credentials to join a PC into domain, or to recreate workstation’s computer account, at the same time allowing employee’s user account to join the computer into a domain by him-/herself. In the first case, ServiceDesk employee needs to walk to a user’s desk, which may be quite exhausting, especially for remote locations, in the second case, computer’s group membership will be probably lost and a new account must be added into all appropriate groups manually.
Is it possible to decrease time and effort put into resolution of such requests? Yes, absolutely!
The main trick is to assign a user with following permissions to his computer’s account:
- Validated write to DNS host name
- Validated write to service principal name
- List the children of an object
- Read
- Read security information
- List the object access
- Control access right
- Delete an object and all of its children
- Delete
- Write to the following properties:
- sAMAccountName
- displayName
- description
- Logon Information
- Account Restrictions
User with abovementioned permissions will be able to join their PC into an AD domain without any assistance from Service Desk.
I made this little script to automate the permissions assigning. Please look into the help section (or use Get-Help cmdlet) to find out about its syntax and usage examples.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
<# MIT License Copyright (c) 2016 Kirill Nikolaev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #> #Requires -Version 3.0 #Requires -Modules ActiveDirectory <# .SYNOPSIS Allows a user to join their computer into Active Directory domain. .DESCRIPTION Allows a user to join their computer into Active Directory domain w/o assistance of Service Desk engineer if the computer account is already pre-created. After executing that script a user can reinstall OS multiple times and still be able to join the machine into domain, while computer name will stay the same. Right now the script works only for the domain where an account running the script resides. .PARAMETER ComputerName sAMAccountName of a computer. .PARAMETER UserName sAMAccountName of a user. .PARAMETER ComputerDomain FQDN of an AD domain where target computer is located. .PARAMETER UserDomain FQDN of an AD domain where target user is located. .EXAMPLE .\Allow-ADDomainJoin.ps1 -UserName ExampleUser -ComputerName ExampleComputer .NOTES NAME: Allow-ADDomainJoin AUTHOR: Kirill Nikolaev LASTEDIT: 03/11/2016 10:02:10 KEYWORDS: Active Directory, ADDS, self-service, service desk, workstations, end-users .LINK https://exchange12rocks.org/2016/03/14/how-to-allow-users-to-join-their-computers-into-ad-domain/ #> Param( [Parameter(Mandatory, HelpMessage='sAMAccount name of a computer account at which you like to delegate domain-joining permissions')] [string]$ComputerName, [Parameter(Mandatory, HelpMessage='sAMAccount name of a user account to which you like to delegate domain-joining permissions')] [string]$UserName, [string]$ComputerDomain = 'example.com', [string]$UserDomain = 'example.com' ) $ErrorActionPreference = 'Stop' Import-Module -Name ActiveDirectory [string]$ComputerWDC = (Get-ADDomainController -DomainName $ComputerDomain -Discover -Writable -NextClosestSite).HostName [string]$UserWDC = (Get-ADDomainController -DomainName $UserDomain -Discover -Writable -NextClosestSite).HostName [string]$ComputerDomainNB = (Get-ADDomain -Identity $ComputerDomain).NetBIOSName Try { $ADObject = Get-ADComputer -Identity $ComputerName -Server $ComputerWDC } Catch { Write-Error -Message ("Cannot get computer {0} from AD DS @ {1}:`r`n{2}`r`n{3}" -f $ComputerName, $ComputerWDC, $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Try { $null = New-PSDrive -Name $ComputerDomainNB -Root '' -PSProvider ActiveDirectory -Server $ComputerWDC } Catch { Write-Error -Message ("Cannot create new PSDrive for {0} AD domain @ {1}:`r`n{2}`r`n{3}" -f $ComputerDomain, $ComputerWDC, $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Try { #TODO: Get-Acl desn't support ShouldProcess $DACL = Get-Acl -Path ('{0}:\{1}' -f $ComputerDomainNB, $ADObject.DistinguishedName) } Catch { Write-Error -Message ("Cannot get DACL for object {0} @ {1}:`r`n{2}`r`n{3}" -f $ADObject.DistinguishedName, $ComputerWDC, $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Try { $UserSID = (Get-ADUser -Identity $UserName -Properties SID -Server $UserWDC).SID } Catch { Write-Error -Message ("Cannot get user {0} from AD DS @ {1}:`r`n{2}`r`n{3}" -f $UserName, $UserWDC, $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Try { $ACEs = @() } Catch { Write-Error -Message ("Cannot initialize empty ACEs object:`r`n{0}`r`n{1}" -f $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Try { $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'197076', 'Allow', [GUID]'00000000-0000-0000-0000-000000000000', 'None', [GUID]'00000000-0000-0000-0000-000000000000') $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'WriteProperty', 'Allow', [GUID]'3e0abfd0-126a-11d0-a060-00aa006c33ed', 'None', [GUID]'00000000-0000-0000-0000-000000000000') $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'WriteProperty', 'Allow', [GUID]'bf967953-0de6-11d0-a285-00aa003049e2', 'None', [GUID]'00000000-0000-0000-0000-000000000000') $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'WriteProperty', 'Allow', [GUID]'bf967950-0de6-11d0-a285-00aa003049e2', 'None', [GUID]'00000000-0000-0000-0000-000000000000') $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'WriteProperty', 'Allow', [GUID]'5f202010-79a5-11d0-9020-00c04fc2d4cf', 'None', [GUID]'00000000-0000-0000-0000-000000000000') $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'WriteProperty', 'Allow', [GUID]'4c164200-20c0-11d0-a768-00aa006e0529', 'None', [GUID]'00000000-0000-0000-0000-000000000000') $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'Self', 'Allow', [GUID]'f3a64788-5306-11d1-a9c5-0000f80367c1', 'None', [GUID]'00000000-0000-0000-0000-000000000000') $ACEs += New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList ($UserSID,'Self', 'Allow', [GUID]'72e39547-7b18-11d1-adef-00c04fd8d5cd', 'None', [GUID]'00000000-0000-0000-0000-000000000000') } Catch { Write-Error -Message ("Error while creating ACE objects:`r`n{0}`r`n{1}" -f $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Try { foreach ($ACE in $ACEs) { $DACL.AddAccessRule($ACE) } } Catch { Write-Error -Message ("Error while populating DACL with ACE objects:`r`n{0}`r`n{1}" -f $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Try { Set-Acl -AclObject $DACL -Path ('{0}:\{1}' -f $ComputerDomainNB, $ADObject.DistinguishedName) } Catch { Write-Error -Message ("Error while writing DACL back to {0} @ {1}:`r`n{2}`n{3}" -f $ADObject.DistinguishedName, $ComputerWDC, $Error[0].Exception.Message, $Error[0].InvocationInfo.PositionMessage) Exit } Write-Output -InputObject ('{0} is now allowed to join {1} to the domain {2}.' -f $UserName, $ComputerName, $ComputerDomain) |
In case you use Windows 8.1/Server 2012 R2, you might need to install KB 3092002, either way, only member of the “Domain Admins” group will be able to execute the script. This is due to a bug in the Set-Acl cmdlet. The fix for Windows 10 is included in the latest RSAT package.
If you unsure how to use the script or experience any errors, please leave a comment below or contact me directly.