12 April 2016

Import Active Directory Module into Windows PE

One thing I have been wanting to have is access to active directory in a WinPE environment. The main reason I want it is to be able to delete systems from active directory during a build. When I first started researching, I found this blog that guided me on writing this script. The blog tells how to inject the AD module into the WIM file. That is fine, but do you really want to do that every time you generate a new WIM file? I don't. I started testing to see if the directories could be copied into the WinPE environment while it was running without the need of a reboot. It worked. Currently, this script only makes the Active Directory module available in the WinPE environment. I am going to write more scripts to take advantage of the AD module.

To use this script, you will need to place it on a network share, or if you are using WinPE, you can place it within the scripts folder of the DeploymentShare so the image will have access to it. I wrote this with four parameters so that you can use the domain username and password within a task sequence without putting it inside the script to possibly expose it. The username and password give the script access to map to the NetworkPath. The NetworkPath points to the location where the Active Directory components reside to copy over to the WinPE environment. The DriveLetter pertains to the drive letter you wish for the script to use when mapping to the NetworkPath. If you want, you could enter default values for the parameters if you want.

The next thing you will need to do is to create the source folders on the NetworkPath, which will contain all of the files.

For 32-Bit WinPE, create the following directories on your NetworkPath. This is what my source directory looks like:

NOTE: The last two directories will have different names as the module is updated by Microsoft. You will have to search for the first part of the name to find them if it changes. That is why in the script I have it to search for the name of the directory knowing that it might change.

For 32-bit WinPE, copy the following directories from a Windows 10 machine to the appropriate directories created above. Make sure you copy all subdirectories along with the full contents:

  • %windir%\System32\WindowsPowerShell\v1.0\Modules\ActiveDirectory
  • %windir%\Microsoft.NET\assembly\GAC_32\Microsoft.ActiveDirectory.Management
  • %windir%\Microsoft.NET\assembly\GAC_32\Microsoft.ActiveDirectory.Management.Resources
  • %windir%\WinSxS\x86_microsoft.activedirectory.management_31bf3856ad364e35_6.3.9431.0_none_b85eb2e785c286ef
  • %windir%\WinSxS\msil_microsoft-windows-d..ivecenter.resources_31bf3856ad364e35_6.3.9431.0_en-us_38f21d039944539f
For 64-Bit WinPE, I have included an If statement, but it has not been tested, so I can't guarantee it will work. I am not sure if you still need to copy the 32-bit folders also, or if they can be removed and just the 64-bit folders installed. Here is the list of folders to copy from a Windows 10 x64 system:
  • %windir%\SysWOW64\WindowsPowerShell\v1.0\Modules\ActiveDirectory 
  • %windir%\Microsoft.NET\assembly\GAC_64\Microsoft.ActiveDirectory.Management
  • %windir%\Microsoft.NET\assembly\GAC_64\Microsoft.ActiveDirectory.Management.Resources
  • %windir%\WinSxS\amd64_microsoft.activedir..anagement.resources_31bf3856ad364e35_6.3.9431.0_en-us_fb186ae865900ae8
As for the last 64-bit entry above, I have included a variable in the script to grab the name of the directory as the last part will likely change upon future updates to the PowerShell AD module.


Below is how I put the script into the task sequence build. I first map a T: drive to the location of where the directories above exist. I then execute the powershell script and finally unmap the T: drive.



After you get these source files copied to a network location, you can now use the script below to run during the WinPE environment. You can see the pop-up windows in the background as it robocopies the directories over to WinPE. Here is a video of the script in action:



One thing you will encounter when executing the script is that it will give you the following warning when you import the module:


I racked my brain last week trying to get this to go away. I was trying to use the new-psdrive to open a connection to the active directory server and I just couldn't get it to work. I finally posted to the Facebook PowerShell group and one advised me to ignore the message and use the -server parameter for each cmdlet. That works. You can ignore this message. I ran the Get-ADComputer cmdlet and specificed the AD server in the -server parameter. It worked perfectly.

You can download the script from here.

InstallActiveDirectoryModule.ps1



1:  <#  
2:       .SYNOPSIS  
3:            Install PowerShell Active Directory Module  
4:         
5:       .DESCRIPTION  
6:            Copies the PowerShell Active Directory Module to the WinPE environment. This allows the use of the PowerShell module without having to mount, inject the directories, and dismount a WIM everytime a new WIM is generated.  
7:         
8:       .PARAMETER DomainUserName  
9:            Username with domain access used to map drives  
10:         
11:       .PARAMETER DomainPassword  
12:            Domain password used to map network drives  
13:         
14:       .PARAMETER NetworkPath  
15:            Network path to map where the Active Directory PowerShell module exists  
16:         
17:       .PARAMETER DriveLetter  
18:            Drive letter mapping where the PowerShell Active Directory module files exists  
19:         
20:       .NOTES  
21:            ===========================================================================  
22:            Created with: SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.119  
23:            Created on:   4/8/2016 12:41 PM  
24:            Created by:   Mick Pletcher  
25:            Organization:  
26:            Filename:     InstallActiveDirectoryModule.ps1  
27:            ===========================================================================  
28:  #>  
29:  [CmdletBinding()]  
30:  param  
31:  (  
32:       [string]  
33:       $DomainUserName,  
34:       [string]  
35:       $DomainPassword,  
36:       [string]  
37:       $NetworkPath,  
38:       [string]  
39:       $DriveLetter  
40:  )  
41:    
42:  function Copy-Folder {  
43:  <#  
44:       .SYNOPSIS  
45:            Copy Folder  
46:         
47:       .DESCRIPTION  
48:            Copy folder to destination  
49:         
50:       .PARAMETER SourceFolder  
51:            A description of the SourceFolder parameter.  
52:         
53:       .PARAMETER DestinationFolder  
54:            A description of the DestinationFolder parameter.  
55:         
56:       .EXAMPLE  
57:                      PS C:\> Copy-Folder -SourceFolder 'Value1' -DestinationFolder 'Value2'  
58:         
59:       .NOTES  
60:            Additional information about the function.  
61:  #>  
62:         
63:       [CmdletBinding()]  
64:       param  
65:       (  
66:            [string]  
67:            $SourceFolder,  
68:            [string]  
69:            $DestinationFolder  
70:       )  
71:         
72:       $Executable = $env:windir + "\system32\Robocopy.exe"  
73:       $Switches = $SourceFolder + [char]32 + $DestinationFolder + [char]32 + "/e /eta /mir"  
74:       Write-Host "Copying "$SourceFolder"....." -NoNewline  
75:       $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Switches -Wait -Passthru).ExitCode  
76:       If (($ErrCode -eq 0) -or ($ErrCode -eq 1)) {  
77:            Write-Host "Success" -ForegroundColor Yellow  
78:       } else {  
79:            Write-Host "Failed with error code"$ErrCode -ForegroundColor Red  
80:       }  
81:  }  
82:    
83:  function Get-Architecture {  
84:  <#  
85:       .SYNOPSIS  
86:            Get-Architecture  
87:         
88:       .DESCRIPTION  
89:            Returns whether the system architecture is 32-bit or 64-bit  
90:         
91:       .EXAMPLE  
92:            Get-Architecture  
93:         
94:       .NOTES  
95:            Additional information about the function.  
96:  #>  
97:         
98:       [CmdletBinding()][OutputType([string])]  
99:       param ()  
100:         
101:       $OSArchitecture = Get-WmiObject -Class Win32_OperatingSystem | Select-Object OSArchitecture  
102:       $OSArchitecture = $OSArchitecture.OSArchitecture  
103:       Return $OSArchitecture  
104:       #Returns 32-bit or 64-bit  
105:  }  
106:    
107:    
108:  function New-NetworkDrive {  
109:  <#  
110:       .SYNOPSIS  
111:            Map network drive  
112:         
113:       .DESCRIPTION  
114:            Map the network drive for copying down the PowerShell Active Directory files to the WinPE environment  
115:         
116:       .EXAMPLE  
117:            PS C:\> New-NetworkDrive  
118:         
119:       .NOTES  
120:            Additional information about the function.  
121:  #>  
122:         
123:       [CmdletBinding()]  
124:       param ()  
125:         
126:       $Executable = $env:windir + "\system32\net.exe"  
127:       $Switches = "use" + [char]32 + $DriveLetter + ":" + [char]32 + $NetworkPath + [char]32 + "/user:" + $DomainUserName + [char]32 + $DomainPassword  
128:       Write-Host "Mapping"$DriveLetter":\ drive....." -NoNewline  
129:       $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Switches -Wait -Passthru).ExitCode  
130:       If ((Test-Path $DriveLetter":\") -eq $true) {  
131:            Write-Host "Success" -ForegroundColor Yellow  
132:       } else {  
133:            Write-Host "Failed" -ForegroundColor Yellow  
134:       }  
135:  }  
136:    
137:  function Remove-NetworkDrive {  
138:  <#  
139:       .SYNOPSIS  
140:            Delete the mapped network drive  
141:         
142:       .DESCRIPTION  
143:            Delete the mapped network drive  
144:         
145:       .EXAMPLE  
146:                      PS C:\> Remove-NetworkDrive  
147:         
148:       .NOTES  
149:            Additional information about the function.  
150:  #>  
151:         
152:       [CmdletBinding()]  
153:       param ()  
154:         
155:       $Executable = $env:windir + "\system32\net.exe"  
156:       $Switches = "use" + [char]32 + $DriveLetter + ":" + [char]32 + "/delete"  
157:       Write-Host "Deleting"$DriveLetter":\ drive....." -NoNewline  
158:       $ErrCode = (Start-Process -FilePath $Executable -ArgumentList $Switches -Wait -Passthru).ExitCode  
159:       If ((Test-Path $DriveLetter":\") -eq $true) {  
160:            Write-Host "Failed" -ForegroundColor Yellow  
161:       } else {  
162:            Write-Host "Success" -ForegroundColor Yellow  
163:       }  
164:  }  
165:    
166:  cls  
167:  #Get WinPE Architecture  
168:  $Architecture = Get-Architecture  
169:  #Map network drive to PowerShell active directory module  
170:  New-NetworkDrive  
171:  #Get msil_microsoft-windows-d..ivecenter.resources Directory Name  
172:  $MicrosoftWindowsIvecenterResources = Get-ChildItem $DriveLetter":\" | where { $_.Attributes -eq 'Directory' } | Where-Object { $_.FullName -like "*msil_microsoft-windows-d..ivecenter.resources*" }  
173:  #Get WinSxS x86_microsoft.activedirectory.management Name  
174:  $WinSxSMicrosoftActiveDirectoryManagementResources = Get-ChildItem $DriveLetter":\" | where { $_.Attributes -eq 'Directory' } | Where-Object { $_.FullName -like "*x86_microsoft.activedirectory.management*" }  
175:  #Get WinSxS amd64_microsoft.activedir..anagement.resources Name  
176:  $WinSxSMicrosoftActiveDirectoryManagementResources_x64 = Get-ChildItem $DriveLetter":\" | where { $_.Attributes -eq 'Directory' } | Where-Object { $_.FullName -like "*amd64_microsoft.activedir..anagement.resources*" }  
177:    
178:  #Copy ActiveDirectory Folder  
179:  Copy-Folder -SourceFolder $NetworkPath"\ActiveDirectory" -DestinationFolder $env:windir"\System32\WindowsPowerShell\v1.0\Modules\ActiveDirectory"  
180:  #Copy Microsoft.ActiveDirectory.Management Folder  
181:  Copy-Folder -SourceFolder $NetworkPath"\Microsoft.ActiveDirectory.Management" -DestinationFolder $env:windir"\Microsoft.NET\assembly\GAC_32\Microsoft.ActiveDirectory.Management"  
182:  #Copy Microsoft.ActiveDirectory.Management.Resources Folder  
183:  Copy-Folder -SourceFolder $NetworkPath"\Microsoft.ActiveDirectory.Management.Resources" -DestinationFolder $env:windir"\Microsoft.NET\assembly\GAC_32\Microsoft.ActiveDirectory.Management.Resources"  
184:  #Copy msil_microsoft-windows-d..ivecenter.resources Folder  
185:  Copy-Folder -SourceFolder $NetworkPath"\"$MicrosoftWindowsIvecenterResources -DestinationFolder $env:windir"\WinSxS\"$MicrosoftWindowsIvecenterResources  
186:  #Copy x86_microsoft.activedirectory.management Folder  
187:  Copy-Folder -SourceFolder $NetworkPath"\"$WinSxSMicrosoftActiveDirectoryManagementResources -DestinationFolder $env:windir"WinSxS\"$WinSxSMicrosoftActiveDirectoryManagementResources  
188:    
189:  If ($Architecture -eq "64-bit") {  
190:       #Copy ActiveDirectory x64 Folder  
191:       Copy-Folder -SourceFolder $NetworkPath"\ActiveDirectory" -DestinationFolder $env:SystemDrive"\"  
192:       #Copy Microsoft.ActiveDirectory.Management x64 Folder  
193:       Copy-Folder -SourceFolder $NetworkPath"\Microsoft.ActiveDirectory.Management" -DestinationFolder $env:windir"\Microsoft.NET\assembly\GAC_64\Microsoft.ActiveDirectory.Management"  
194:       #Copy Microsoft.ActiveDirectory.Management.Resources x64 Folder  
195:       Copy-Folder -SourceFolder $NetworkPath"\Microsoft.ActiveDirectory.Management.Resources" -DestinationFolder $env:windir"\Microsoft.NET\assembly\GAC_64\Microsoft.ActiveDirectory.Management.Resources"  
196:       #Copy amd64_microsoft.activedir..anagement.resources x64 Folder  
197:       Copy-Folder -SourceFolder $NetworkPath"\"$WinSxSMicrosoftActiveDirectoryManagementResources_x64 -DestinationFolder $env:windir"\WinSxS\"$WinSxSMicrosoftActiveDirectoryManagementResources_x64  
198:  }  
199:    
200:  #Unmap Network Drive  
201:  Remove-NetworkDrive  
202:    

6 comments:

  1. Hi mick, Some wonderful creativity ....has this persisted to be operational with all the regular updates, and version changes in Win10? I see you raised it as a concern, and have addressed it with a wildcard type search on the folder name that's imported. Have you tested this while deploying the latest versions of Windows 10? I'm asking because I work in a very fast moving organization, where Windows image updates, and versions get changd regularly.

    ReplyDelete
  2. Would it not just be more efficient to copy all of the AD module files to a package, then copy the files from the root of the package directory to the Windows dir under the WinPE RAMdisk without needing to pass around credentials to connect to a network share?

    ReplyDelete
    Replies
    1. I am actually working on redoing this as to use task sequences for each step instead of using a PowerShell script.

      Delete
  3. I created the following folders in Deployment Share's ROOT called "MDTShare\ADPowerShell\x64" and "MDTShare\ADPowerShell\x86". The folders contain everything copied from the above tutorial. I have a batch file called "MDTShare\ADPowerShell\import.bat" that has a series of xcopy commands that drop all above files into place. "import.bat" launches as a Task Sequence item. Works like a charm!

    ReplyDelete
  4. I zipped it up and uploaded it here for anyone that wants to try it. I could not have done it without this tutorial!

    https://www.dropbox.com/s/k6c88cp36neixi1/ADPowerShell.zip?dl=0

    ReplyDelete