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:
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.
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:
Nice job Mick. Thanks for the share.
ReplyDeleteHi 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.
ReplyDeleteWould 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?
ReplyDeleteI am actually working on redoing this as to use task sequences for each step instead of using a PowerShell script.
DeleteI 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!
ReplyDeleteI zipped it up and uploaded it here for anyone that wants to try it. I could not have done it without this tutorial!
ReplyDeletehttps://www.dropbox.com/s/k6c88cp36neixi1/ADPowerShell.zip?dl=0