Recently, my firm has started using the AD extension attributes for custom data. I wrote a script a while back that read the data from a .CSV and imported it into a specific ExtensionAttribute. This came up again and it was brought to my attention that we may start using more of the fields. In order to automate this so that I don't have to modify the script each time, I decided to write one that gets all of its information from the .CSV file. With the help of Sapien's PowerShell Studio, this script was easy to write and easy to document.
This script will import a populated .CSV file that resides in the same directory as the script. The first line in the .CSV file contains the headers. The script reads that line to know what fields to populate in AD. You enter the exact extensionAttribute name in the header field and all data beneath it will be populated in that field for each user. Here is a screenshot of a sample .CSV file.
The first line is what tells it which extension attribute to populate the data to. It can have as many attribute lines as you need. I also added the features to remove data from an extension attribute. If a cell is left blank or CLEAR is input into the cell, the extension attribute will be cleared. If you do not want anything done to an attribute, input NO CLEAR into the cell and the script will not do anything to the attribute.
Logging has also been added to the script. If you want a log file written, then populate the -LogFile parameter. If this is left unpopulated, the script will not create a log file.
Another feature it -ProcessDelay. I added this as a precautionary feature. This puts a specified XX seconds delay between each record. This allows you to slowly implement the changes in AD without it all happening at once. Say you accidentally made an error in the .CSV file and discover it midway through. At least not all records got changed at that time. If you leave that field blank, the script will not pause between each record.
The final feature I added was for the script to go back and verify the change was actually made.
I do highly recommend that you test this script out on a test account first before using it against all of your user accounts. It works in my environment, but it is no guarantee that it will work in yours.
Here is a screenshot of the script in action.
You can download the script from here.
This script will import a populated .CSV file that resides in the same directory as the script. The first line in the .CSV file contains the headers. The script reads that line to know what fields to populate in AD. You enter the exact extensionAttribute name in the header field and all data beneath it will be populated in that field for each user. Here is a screenshot of a sample .CSV file.
The first line is what tells it which extension attribute to populate the data to. It can have as many attribute lines as you need. I also added the features to remove data from an extension attribute. If a cell is left blank or CLEAR is input into the cell, the extension attribute will be cleared. If you do not want anything done to an attribute, input NO CLEAR into the cell and the script will not do anything to the attribute.
Logging has also been added to the script. If you want a log file written, then populate the -LogFile parameter. If this is left unpopulated, the script will not create a log file.
Another feature it -ProcessDelay. I added this as a precautionary feature. This puts a specified XX seconds delay between each record. This allows you to slowly implement the changes in AD without it all happening at once. Say you accidentally made an error in the .CSV file and discover it midway through. At least not all records got changed at that time. If you leave that field blank, the script will not pause between each record.
The final feature I added was for the script to go back and verify the change was actually made.
I do highly recommend that you test this script out on a test account first before using it against all of your user accounts. It works in my environment, but it is no guarantee that it will work in yours.
Here is a screenshot of the script in action.
You can download the script from here.
1: <#
2: .SYNOPSIS
3: A brief description of the ImportADExtensions.ps1 file.
4:
5: .DESCRIPTION
6: This script will import data from a CSV file to be written to the desired extension attributes in active directory
7:
8: .PARAMETER DataFile
9: Name of the csv file that contains the data to import into active directory
10:
11: .PARAMETER LogFile
12: Name of the log file to write the status of each change to
13:
14: .PARAMETER ProcessDelay
15: This will pause the script for XX number of seconds before processing the next AD user. This is intended as a safety measure in the event that wrong data is being written to each AD profile. This allows for not all profile to be affected at once.
16:
17: .EXAMPLE
18: Run with no logging and no delays between entry changes
19: powershell.exe -executionpolicy bypass -file ImportADExtensions.ps1 -DataFile Data.csv
20:
21: Run with logging and no delays between entry changes
22: powershell.exe -executionpolicy bypass -file ImportADExtensions.ps1 -DataFile Data.csv -LogFile ADExtensions.log
23:
24: Run with logging and 10 second delay between entry changes
25: powershell.exe -executionpolicy bypass -file ImportADExtensions.ps1 -DataFile Data.csv -LogFile ADExtensions.log -ProcessDelay 10
26:
27: You can also pre-populate the parameters within the Param fields inside the script.
28:
29: .NOTES
30: ===========================================================================
31: Created with: SAPIEN Technologies, Inc., PowerShell Studio 2016 v5.2.126
32: Created on: 7/28/2016 8:55 AM
33: Created by: Mick Pletcher
34: Organization:
35: Filename: ImportADExtensions.ps1
36: ===========================================================================
37: #>
38: [CmdletBinding()]
39: param
40: (
41: [ValidateNotNullOrEmpty()][string]
42: $DataFile,
43: [string]
44: $LogFile,
45: [int]
46: $ProcessDelay
47: )
48:
49: function Get-RelativePath {
50: <#
51: .SYNOPSIS
52: Get the relative path
53:
54: .DESCRIPTION
55: Returns the location of the currently running PowerShell script
56:
57: .NOTES
58: Additional information about the function.
59: #>
60:
61: [CmdletBinding()][OutputType([string])]
62: param ()
63:
64: $Path = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent) + "\"
65: Return $Path
66: }
67:
68: function Import-DataFile {
69: <#
70: .SYNOPSIS
71: Import data file
72:
73: .DESCRIPTION
74: Import the data from a csv file
75:
76: .EXAMPLE
77: PS C:\> Import-DataFile
78:
79: .NOTES
80: Additional information about the function.
81: #>
82:
83: [CmdletBinding()][OutputType([object])]
84: param ()
85:
86: #Get the path this script is being executed from
87: $RelativePath = Get-RelativePath
88: #Associate the relative path with the data file to be imported
89: $File = $RelativePath + $DataFile
90: #Read the data file to a variable
91: $FileData = Get-Content -Path $File -Force
92: #Get the attribute fields
93: $Fields = ($FileData[0]).Split(",")
94: $ImportedRecords = @()
95: foreach ($Record in $FileData) {
96: If ($Record -notlike "*extensionattribute*") {
97: $SplitRecord = $Record.Split(",")
98: $objRecord = New-Object System.Management.Automation.PSObject
99: for ($i = 0; $i -lt $Fields.Length; $i++) {
100: $objRecord | Add-Member -type NoteProperty -Name $Fields[$i] -Value $SplitRecord[$i]
101: }
102: $ImportedRecords += $objRecord
103: }
104: }
105: Return $ImportedRecords
106: }
107:
108: function New-Logfile {
109: <#
110: .SYNOPSIS
111: Create a new log file
112:
113: .DESCRIPTION
114: This will create a new log file. If an old one exists, it will delete it.
115:
116: .EXAMPLE
117: PS C:\> New-Logfile
118:
119: .NOTES
120: Additional information about the function.
121: #>
122:
123: [CmdletBinding()]
124: param ()
125:
126: $RelativePath = Get-RelativePath
127: $Logs = $RelativePath + $LogFile
128: If ((Test-Path $Logs) -eq $true) {
129: $Output = "Deleting old log file....."
130: Remove-Item -Path $Logs -Force | Out-Null
131: If ((Test-Path $Logs) -eq $false) {
132: $Output += "Success" + "`n"
133: } else {
134: $Output += "Failed" + "`n"
135: }
136: }
137: If (($LogFile -ne "") -and ($LogFile -ne $null)) {
138: $Output += "Creating new log file....."
139: New-Item -Path $Logs -ItemType File -Force | Out-Null
140: If ((Test-Path $Logs) -eq $true) {
141: $Output += "Success"
142: } else {
143: $Output += "Failed"
144: }
145: Write-Output $Output
146: }
147: }
148:
149: function Write-ExtensionAttributes {
150: <#
151: .SYNOPSIS
152: Write Extension Attributes to Active Directory
153:
154: .DESCRIPTION
155: This script will write the extension attributes to active directory. It reads the name of the object field to associate with the correct extension attribute in AD.
156:
157: .PARAMETER Records
158: List of imported objects
159:
160: .EXAMPLE
161: PS C:\> Write-ExtensionAttributes -Records $value1
162:
163: .NOTES
164: Additional information about the function.
165: #>
166:
167: [CmdletBinding()]
168: param
169: (
170: [ValidateNotNullOrEmpty()][object]
171: $Records
172: )
173:
174: #Get all member of $Records
175: $Fields = $Records | Get-Member
176: #Filter for just the extension attribute properties
177: $Fields = ($Fields | Where-Object { (($_.MemberType -eq "NoteProperty") -and ($_.Name -like "*extensionattribute*")) }).name
178: for ($i = 0; $i -lt @($Records).Count; $i++) {
179: #Get all active directory properties for specified user
180: $User = Get-ADUser $Records[$i].Username -Properties *
181: $Output += "User " + ($i+1) + " of " + @($Records).Count + "`n"
182: $Output += "Username: " + $Records[$i].Username + "`n"
183: foreach ($Field in $Fields) {
184: $Output += $Field + ": " + $Records[$i].$Field + "`n"
185: If ((($Records[$i].$Field -eq "Clear") -or ($Records[$i].$Field -eq "") -or ($Records[$i].$Field -eq $null)) -and ($Records[$i].$Field -ne "NO CLEAR")) {
186: $Output += "Clearing " + $Field + "....."
187: Set-ADUser -Identity $Records[$i].Username -Clear $Field
188: #Get the field that was change from active directory
189: $Test = Get-ADUser $Records[$i].Username -Properties * | select $Field
190: #Test if the data in the AD field matches the data from the imported file
191: if ($Test.$Field -eq $null) {
192: $Output += "Success" + "`n"
193: } else {
194: $Output += "Failed" + "`n"
195: }
196: } elseif ($Records[$i].$Field -ne "NO CLEAR") {
197: $User.$Field = $Records[$i].$Field
198: $Output += "Setting " + $Field + "....."
199: #Write change to active directory
200: Set-ADUser -Instance $User
201: #Get the field that was change from active directory
202: $Test = Get-ADUser $Records[$i].Username -Properties * | select $Field
203: #Test if the data in the AD field matches the data from the imported file
204: if ($Test.$Field -eq $Records[$i].$Field) {
205: $Output += "Success" + "`n"
206: } else {
207: $Output += "Failed" + "`n"
208: }
209: }
210: }
211: Write-Output $Output
212: #If the Logfile parameter is populated, then write the output to a logfile
213: If (($LogFile -ne "") -and ($LogFile -ne $null)) {
214: #Get the path where this script is being executed from
215: $RelativePath = Get-RelativePath
216: #Define the log file path
217: $Logs = $RelativePath + $LogFile
218: #Write the output to the log file
219: Add-Content -Value $Output -Path $Logs -Encoding UTF8 -Force
220: }
221: $Output = $null
222: If (($ProcessDelay -ne $null) -and ($ProcessDelay -ne "")) {
223: Start-Sleep -Seconds $ProcessDelay
224: }
225: cls
226: }
227: }
228:
229: Import-Module -Name ActiveDirectory
230: #Delete old log file and create a new one
231: New-Logfile
232: #Import all records from the csv file
233: $Records = Import-DataFile
234: #Apply changes to active directory
235: Write-ExtensionAttributes -Records $Records
236:
0 comments:
Post a Comment