Changes

Jump to: navigation, search

WPKG with Active Directory

12,259 bytes added, 15:32, 17 July 2012
Scheduled Task (Windows)
</source>
replace '''\\myserver\wpkg$''' with the path to your wpkg folder
 
 
=Pulling Workstation names from Active Directory by Group Membership=
 
I found it easier to control package deployment via group membership. This means that packages can apply regardless of OU.
 
Below is my working script. I'm sure there's better ways of doing some things, but I hope it's useful anyway.
 
It pulls all the windows computers and generates a hosts.xml with the profile ids as the groups the computer is a member of. It then updates the profiles.xml with a list of the groups.
 
 
<source lang="vb">
' Short Description:
' Generate hosts.xml for all AD computers, based on group membership
' It will then update the profiles.xml file with the names of AD groups
' Modified from original source code: http://wpkg.org/WPKG_with_Active_Directory#Pulling_workstation_names_to_hosts.xml_from_Active_Directory_OUs_automatically_with_vbScript
'
' Written by: Carl van Eijk
' Mail: carl.vaneijk (at) hotmail.com
' Date: 25 May 2012
'
' Long Description:
' The script will retrieve all the current domain's windows computers and all the groups that each computer is a member of.
' It will format all the information and output it to your hosts.xml (destructive)
' It will then check the profiles.xml and add any missing profile ids (addative)
' This is to prevent failures for non-existent profile ids caused by AD group membership import.
' All you need to do is amend the profile ids that match your AD group names with the relevant packages.
'
'
' example hosts.xml
'- if Computer1 is a member of "Adobe Acrobat Pro" and "London Office Computers" "London Finance Applications" the following Profile-IDs will be generated for each host:
' <wpkg>
' <host name="Computer1">
' <profile-id="Adobe Acrobat Pro" />
' <profile-id="London Office Computers" />
' <profile-id="London Finance Applications" />
' </host>
' </wpkg>
' "Domain Computers" is excluded for some reason that I'm not worried about at this point as we don't apply installation profiles at that level anyway
'
' example profiles.xml
'- if Computer1 is a member of "Adobe Acrobat Pro" and "London Office Computers" "London Finance Applications" the following Profile-IDs will be generated for each host:
'<profiles:profiles xmlns:profiles="http://www.wpkg.org/profiles" xmlns:wpkg="http://www.wpkg.org/wpkg" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.wpkg.org/profiles xsd/profiles.xsd ">
' <profile id="Pre-existing Profile">
' <depends profile-id="default"/>
' <package package-id="openoffice"/>
' </profile>
' <!--AD groups will be added as below-->
' <profile-id="Adobe Acrobat Pro" />
' <profile-id="London Office Computers" />
' <profile-id="London Finance Applications" />
'</profiles:profiles>
'
'IMPORTANT: This script will overwrite the existing hosts.xml and alter your packages.xml
' This is by design, as the packages are controlled by group membership
' The profiles.xml will only contain profiles that have computers as members.
 
 
' Be sure to run with Cscript
REM cscript "\\wbl-lon-util-1\wpkg\tools\PullComputers.vbs"
' Schedule or trigger to run regularly to update hosts and any new groups.
 
 
'Notes on XML node references
REM Node type NodeType
REM Element 1
REM Attribute 2
REM Text 3
REM Comment 8
REM Document 9
 
 
'Set Constants
 
Const VBquot = """"
'Set the network path of your wpkg folder
Const wpkgPath = "\\wbl-lon-util-1\wpkg\"
'Change these if you've moved or renamed your hosts.xml or profiles.xml
profilesXML = wpkgpath & "profiles.xml"
hostsXML = wpkgpath & "hosts.xml"
 
 
 
 
'Main Program.
' You shouldn;t need to change anything for this to work. Just run as is
 
'get the existing profiles from the profiles.xml
existingProfiles = GetExistingProfiles()
 
'Output the exsiting profiles, for visual reference
wscript.echo "Existing profiles: "
For each existingProfile in existingProfiles
wscript.echo existingProfile
Next
 
'build the hosts.xml and get the unique group names
 
set dict = PullADComputers
 
'output the unique group names
Wscript.echo " Unique Group Names"
For Each groupname In dict
'************************************************
' if the profile does not exist - add it
'wscript.echo dict(groupname) & ":" & groupname
'For each groupname - check against the existing profiles
For Each existingProfile in ExistingProfiles
If Groupname = ExistingProfile Then
' set the ixists flag if we find it in each loop
GroupExists = True
Else
'nothing to see here
End If
Next
'check the exists flag and act accordingly
If GroupExists = True Then
wscript.echo GroupName & " already exists, kewl"
Else
wscript.echo GroupName & " needs to be added to profiles.xml"
' call the code to amend profiles.xml
AddMissingProfiles(GroupName)
End If
'reset exists flag
GroupExists = False
Next
 
'Function to Build Existing profile Array
Function GetExistingProfiles()
'build object and load file
set xmlDoc=CreateObject("Microsoft.XMLDOM")
xmlDoc.async="false"
xmlDoc.load(profilesXML)
' we can build something better later to geneic value
'something like GetExistingProfiles(XMLfile, ElementTagName, AttributeName)
Set Root = xmlDoc.documentElement
set profiles = Root.getElementsByTagName("profile")
'count the number of profiles
n_profiles = profiles.length
wscript.echo "Number of Profiles = " & n_profiles
 
REM wscript.echo profiles.item(0).getAttribute("id")
 
'quickest method to load an array
'http://stackoverflow.com/questions/4605270/add-item-to-array-in-vbscript
'index from 0, so -1
ReDim existingProfiles(n_profiles - 1)
 
'index from 0, so -1
for i = 0 to (n_profiles - 1)
'wscript.echo i
set profile = profiles.item(i)
p_name = profile.getAttribute("id")
'wscript.echo p_name
'now we have the profile name p_name
'we can match the AD group names against each pname
'we should load these into an array to speed things up
existingProfiles(i) = p_name
'wscript.echo existingProfiles(i)
Next
GetExistingProfiles = existingProfiles
End Function
 
'Retrieve the list of computers and their group membership from AD
Function PullADComputers()
Dim myGroups
set myGroups = Createobject("Scripting.Dictionary")
' set the groupnamecounter
uniquegrp_count=0
 
 
Const VBquot = """"
'Not used in this script
'Const ManualHostFile = "hosts-manual.xml"
 
' Setup ADO objects.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection
 
' Search entire Active Directory domain.
Set objRootDSE = GetObject("LDAP://RootDSE")
'You can override the domain here if you need to
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strBase = "<LDAP://" & strDNSDomain & ">"
 
' Filter
' Filter on Computers only and Filter out Mac OS X Computers
'strFilter = "(&(objectCategory=computer)(!operatingSystem=Mac OS X*))"
 
'Filter for Only Windows Computers
strFilter = "(&(objectCategory=computer)(operatingSystem=Windows*))"
 
' Comma delimited list of attribute values to retrieve.
strAttributes = "Name,memberof,sAMAccountName,operatingSystem"
 
' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 10000 ' I was getting truncated records with a lower page size
adoCommand.Properties("Timeout") = 60
adoCommand.Properties("Cache Results") = False
'Construct Dictionary Object to return
set MyGroups = CreateObject("Scripting.Dictionary")
'Run Query
Set adoRecordSet = adoCommand.Execute
 
'Build Output File
set fs = CreateObject("Scripting.FileSystemObject")
set textstream = fs.CreateTextFile(wpkgPath & "\hosts.xml", True)
textstream.WriteLine "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
textstream.WriteLine "<!-- automagically generated with " & Wscript.ScriptFullName
textstream.WriteLine " Domain " & strDNSDomain & " Computers assigned profile id by AD Group Membership"
textstream.WriteLine " Date: " & Date() & " -->" & vbCrLf & vbCrLf
'open wpkg
textstream.WriteLine "<wpkg>"
 
'first record
adoRecordSet.MoveFirst
if not adoRecordSet.EOF Then
Do Until adoRecordSet.EOF
'Had some issues matching names, so forced LCase on everything
strHostName = LCase(adoRecordSet.Fields("Name").Value)
'Write Host Line
'<host name="Computername" profile-id="root">
textstream.WriteLine vbTab & "<host name="& VBquot & strHostName & VBquot & " profile-id=" & VBquot & "root" & VBquot & ">"
Wscript.Echo "Computer Name: " & strHostName
arrMemberof = adoRecordset.Fields("memberof").Value
Wscript.Echo "OS : " & adoRecordset.Fields("operatingSystem").Value
Wscript.Echo "Member of: "
'If there's no membership, close off the host
If IsNull(arrMemberof) Then
wscript.echo "ERM, that's not right: "
 
'strMembers = ""
''NOT WORKING YET comment out this line, if you do not want to generate individiual host profile ids based on host name
'IE: <profile id="Computername" />
'textstream.WriteLine vbTab & vbTab & "<profile id=" & VBquot & strHostName & strGrpName & VBquot & " />"
'Close host entry
' </host>
textstream.WriteLine vbTab & "</host>"
'go to the next record
adoRecordset.MoveNext
Else
For Each strGrpName In arrMemberof
'clean up the result to return a clean computer name
'output the member name without all the extra syntax
strGrpName = Mid(strGrpName, 4, 330)
arrItem = Split(strGrpName, "," )
strGrpName = arrItem(0)
'Had some issues matching names, so forced LCase
strGrpName = LCase(strGrpName)
Wscript.echo strGrpName
'Build unique group names
if (myGroups.Exists(strGrpName) = False) Then
myGroups.Add strGrpName,uniquegrp_count
uniquegrp_count = uniquegrp_count +1
End If
 
'Write Profile ID/Group Name
'<profile id="Test_Computer_Group" />
textstream.WriteLine vbTab & vbTab & "<profile id=" & VBquot & strGrpName & VBquot & " />"
'Wscript.Echo "-------------------" & VBCRLF
Next
'NOT WORKING YET comment out these lines, if you do not want to generate individiual host profile ids based on host name
' <profile id="Computername" />
'textstream.WriteLine vbTab & vbTab & "<profile id=" & VBquot & strHostName & VBquot & " />"
'add the profile to profiles.xml
'TODO: ' need to add check for existence first on this
' Possibly pump them all to an array first
'call UpdateXML(profilesXML, strHostName)
'Close host entry
' </host>
textstream.WriteLine vbTab & "</host>"
End If
adoRecordSet.MoveNext
Loop
Else
wscript.echo "END OF RECORD SET"
End If
'Close off text file
'</wpkg>
textstream.WriteLine "</wpkg>"
textstream.close
 
Set PullADComputers = myGroups
 
 
End Function
 
 
Function AddMissingProfiles(ProfileName)
wscript.echo "Code for updating profiles.xml goes here"
'.....
'this is a separate function so we can genericise it later if needed
call UpdateXML(profilesXML, ProfileName)
wscript.echo ProfileName & " added......"
 
End Function
 
 
'There is an issue, with the additions all appearing on one line...
' I think I should add a piece to clean up the format, basically just insert "VBCRLF" in between "><" (split?)
Function UpdateXML(XMLFile, ProfileName)
' for now the tree is mostly hard coded, but can expand later with
'Node Type
'Node Name
'Addtribute Name
'Attribute text
wscript.echo "Updating " & XMLFile & " with " & ProfileName
'need to add new profiles\profile id
Set xmlDoc = CreateObject("Microsoft.XMLDOM")
 
xmlDoc.Async = "False"
xmlDoc.Load(XMLFile)
 
Set objRoot = xmlDoc.documentElement
set NewNode = xmlDoc.createNode(1, "profile", "")
objRoot.appendChild(NewNode)
 
NewNode.setAttribute "id", ProfileName
'NewNode.setAttribute "name", strName
'NewNode.text = "some sample text"
'Add newline
objRoot.appendChild xmlDoc.createTextNode(vbCrLf)
'Save the changes
xmlDoc.Save XMLFile
 
End Function
 
Set xmlDoc = Nothing
 
</source>
 
 
[[category:Documentation]]
[[category:Installation]]
3
edits

Navigation menu