Difference between revisions of "WPKG with Active Directory"

From WPKG | Open Source Software Deployment and Distribution
Jump to: navigation, search
(Multiple Profile Method: new code - insert additional manual profiles)
Line 259: Line 259:
 
' Modifed by: Marc Ozin
 
' Modifed by: Marc Ozin
 
' Modified Date: 2010-03-02
 
' Modified Date: 2010-03-02
 
 
' Modification:
 
' Modification:
 
' Hosts  generated with multiple Profile-IDs to show each sub OU the Host resides within.
 
' Hosts  generated with multiple Profile-IDs to show each sub OU the Host resides within.
 
' e.g. if Computer1 is contained within Head-Office/Finance/Payroll the following Profile-IDs will be generated:
 
' e.g. if Computer1 is contained within Head-Office/Finance/Payroll the following Profile-IDs will be generated:
' <host name="Computer1" profile-id="/root">
+
' <host name="Computer1">
' <profile id="/root/Head-Office" />
+
' <profile-id="/root" />
' <profile id="/root/Head-Office/Finance" />
+
' <profile-id="/root/Head-Office" />
' <profile id="/root/Head-Office/Finance/Payroll" />
+
' <profile-id="/root/Head-Office/Finance" />
 +
' <profile-id="/root/Head-Office/Finance/Payroll" />
 
' </host>
 
' </host>
  
 
+
' Modifed by: Marc Ozin
 +
' Modified Date: 2011-08-09
 +
' Modification:
 +
' after compiling profiles from AD, any computers listed that also exist in the hosts-manual.xml file have extra profiles attached.
 +
' the format of the hosts-manual.xml is as follows:
 +
' <wpkg>
 +
' <profile>
 +
' <profile id="manualprofile1">
 +
' <host name="computer1"/>
 +
' <host name="computer2"/>
 +
' <host name="computer3"/>
 +
' </profile>
 +
' <profile id="manualprofile2">
 +
' <host name="computer1"/>
 +
' <host name="computer4"/>
 +
' <host name="computer6"/>
 +
' </profile>
 +
'</wpkg>
  
 
Const ADS_SCOPE_SUBTREE = 2
 
Const ADS_SCOPE_SUBTREE = 2
 
Const VBquot = """"
 
Const VBquot = """"
 +
Const ManualHostFile = "hosts-manual.xml"
 +
Const wpkgPath = "\\myserver\wpkg$"
 +
 +
Dim objProfileNodes, ProfileNodeItem, objHostNodes
 +
 
   
 
   
 
  set fs = CreateObject("Scripting.FileSystemObject")
 
  set fs = CreateObject("Scripting.FileSystemObject")
  set textstream = fs.CreateTextFile("hosts.xml", True)
+
  set textstream = fs.CreateTextFile(wpkgPath & "\hosts.xml", True)
 
  textstream.WriteLine "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
 
  textstream.WriteLine "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
 
  textstream.WriteLine "<!-- automagically generated with " & Wscript.ScriptFullName  
 
  textstream.WriteLine "<!-- automagically generated with " & Wscript.ScriptFullName  
Line 333: Line 355:
 
  textstream.WriteLine "</wpkg>"
 
  textstream.WriteLine "</wpkg>"
 
  textstream.close
 
  textstream.close
  Wscript.Echo "Finished..."
+
   
 +
 +
' * Add manual entries from hosts-manual.xml *************************************************************************************************************
 +
 +
 +
if bFileExists(wpkgPath & "\" & ManualHostFile) then
 +
Set objDOMHosts  = CreateObject("Microsoft.XMLDOM")
 +
objDOMHosts.async = false
 +
objDOMHosts.load(wpkgPath & "\hosts.xml")
 +
 +
Set objDOMHostsManual = CreateObject("Microsoft.XMLDOM")
 +
objDOMHostsManual.async = False
 +
objDOMHostsManual.load(wpkgPath & "\" & ManualHostFile)
 +
 
 +
 
 +
Set objProfileNodes = objDOMHostsManual.documentElement.childNodes
 +
 
 +
For Each ProfileNodeItem In objProfileNodes
 +
if ProfileNodeItem.nodeName  = "profile" then
 +
' wscript.echo ProfileNodeItem.getAttribute("id")
 +
Set objHostNodes = ProfileNodeItem.childNodes
 +
For Each HostNodeItem In objHostNodes
 +
' wscript.echo HostNodeItem.getAttribute("name")
 +
strXPath = "/wpkg/host[@name='" & HostNodeItem.getAttribute("name") & "']"
 +
Set objParentNode = CreateObject("Microsoft.XMLDOM")
 +
set objParentNode = objDOMHosts.selectSingleNode(strXPath)
 +
AddProfile objParentNode, ProfileNodeItem.getAttribute("id")
 +
next
 +
end if
 +
Next
 +
 
 +
objDOMHosts.save(wpkgPath & "\hosts.xml")
 +
end if
 +
 
 +
 
 +
' * Subs & Functions *************************************************************************************************************
 +
 
 +
 +
sub AddProfile(objParentNode, strValue)
 +
Dim objDOMHosts, objNode, strName, objAttrib
 +
Set objDOMHosts  = CreateObject("Microsoft.XMLDOM") 
 +
Set objNode = objDOMHosts.createElement("Profile")
 +
objNode.setAttribute "id", strValue
 +
objParentNode.appendChild objNode
 +
End sub
 +
 
 +
 
 +
Function bFileExists(strFile)
 +
set objFSO = createobject("Scripting.FileSystemObject")
 +
bFileExists = objFSO.FileExists(strFile)
 +
end function
 +
 
</source>
 
</source>
 +
You'll need to edit the line:<source lang="vb">Const wpkgPath = "\\myserver\wpkg$"</source>...replacing it with the path to your wpkg folder.
 
Don't forget you'll need to create a profile definition in the profiles.xml for all of the profiles that this script will create!
 
Don't forget you'll need to create a profile definition in the profiles.xml for all of the profiles that this script will create!
  
 +
This version also allows you to build manual profile entries as well.
 +
to use additional manual profiles, create a file called hosts-manual.xml in the wpkg folder like this:
 +
<source lang="xml">
 +
<wpkg>
 +
<profile>
 +
<profile id="manualprofile1">
 +
<host name="computer1"/>
 +
<host name="computer2"/>
 +
<host name="computer3"/>
 +
</profile>
 +
<profile id="manualprofile2">
 +
<host name="computer1"/>
 +
<host name="computer4"/>
 +
<host name="computer6"/>
 +
</profile>
 +
</wpkg>
 +
</source>
 
[[category:Documentation]]
 
[[category:Documentation]]
 
[[category:Installation]]
 
[[category:Installation]]

Revision as of 09:32, 9 August 2011

Active Directory settings

Below settings for Active Directory - screenshots are in German, but I think anyone who doesn't know German, but saw Active Directory will know what to do:

Note that the above can be accomplished using the "Group Policy Management" tool.

Next, you have to choose the right settings for the execution of scripts:

Navigate to Computer Configuration -> Administrative Templates -> System -> Scripts

Make sure that Run startup scripts asynchronously is set to enabled

This make sure that the user can log in, even though the software is still being installed. It's a good choice, because some unpatient users will just press Reboot button when they can't log in immediately, which can have unexpected results (software not installed properly etc.).

Next thing to set is setting "Maximum wait time for Group Policy scripts". Default is 600 seconds (10 minutes), which can be often not enough for installing some software, or when you install more than one software package. So a safe bet is 1800 seconds (30 minutes).


The last thing you have to do, is to select a script which will start WPKG on a system start. This script is located in a batch file:

\\server\path\to\WPKG\wpkg-start.bat

and contains the folowing line which starts WPKG:

cscript \\server\path\to\WPKG\wpkg.js /synchronize /quiet /nonotify

Notes

Sometimes a group policy is not applied when it is setup and the workstations are rebooted for the first time. The reason for this is that group policy is pulled from the server by default every ~90 minutes. If you require group policy to take effect immediately, you may run "gpupdate /force" (XP) or "secedit /refreshpolicy user_policy" and "secedit /refreshpolicy machine_policy" (Win2K) from each workstation.

Also, remember that "Authenticated Users" need to be allowed access to the share holding the script.

Pulling workstation names to hosts.xml from Active Directory OUs automatically with bash

Here you can download a script which will generate separate XML hosts files for each OU:

generate_hosts_xml.gz

It's compressed with gzip, and contains some special characters (namely, DOS/Windows end-of-line characters), so make sure to edit it in the editor which won't break it (mcedit from mc will edit it without problems).

You will need to edit some variables, see the beginning of a script for details.

Pulling workstation names to hosts.xml from Active Directory OUs automatically with perl

If you have different OUs in your AD, and these OUs use different software settings, you will likely want to generate hosts.xml from your AD tree.

Here is a simple perl script for that - you have to execute it on a Linux server, and you need to have ldapsearch tool installed (it comes with OpenLDAP). It's not particularly beautiful, but it works.
Make sure you change the text in bold to match your settings:

 #!/usr/bin/perl
 
 # All OUs that contain computer accounts
 @ous = (''''ou=GR15-R1,ou=GR15,ou=classrooms,ou=uni'''',
          ''''ou=FR7-FL,ou=FR7,ou=classrooms,ou=uni'''',
          ''''ou=FR7-R3,ou=FR7,ou=classrooms,ou=uni'''',
          ''''ou=FR5-R1,ou=FR5,ou=classrooms,ou=uni'''',
          ''''ou=FR5-R1,ou=FR5,ou=classrooms,ou=uni'''',
          ''''ou=Newinstallation,ou=uni'''',
          ''''ou=Testinstallation,ou=uni'''',
          ''''ou=temp,ou=uni'''');
 
 # umount a share on AD (just in case), and then mount it
 
 $server = "'''192.168.55.66'''";
 $share = "'''/mnt/$server'''";
 
 system "umount $share";
 system "mount.cifs //$server/'''Admin''' $share -o username='''user''',pass='''password'''";
 
 # Work on each OU
 foreach (@ous) {
 
 # Get "OU name"
 $_ =~ m/ou=([a-zA-Z0-9-]*)/;
 
 $OU = "$1";
 
 # LDAP command to retrieve data from a given OU
 $ldapcommand = "ldapsearch -h $server -b \"$_,'''dc=example,dc=com'''\" -x -s sub \"objectclass=computer\""  .
                 " -w '''password''' -D \"'''cn=LDAP user,ou=IT,ou=uni,dc=example,dc=com'''\"";
 
  
 # execute LDAP command
     open( LDAPQUERY, "$ldapcommand |" ) or die "LDAP query error: $!";
 
 # Get all fields that have cn=...
     while ( <LDAPQUERY> ) {
     next if ! /^cn: (.*)$/;
     $cn = $1;
 
 # ...and append them to @results
     push @results, "$cn";
     }
 
 
 # Create $hosts variable with some content...
 $hosts = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<wpkg>\n";
 
 # ...and append hosts that we found (cn=) to that variable...
 foreach (@results) {
 
 $computer = $_;
 
 # ...with proper syntax / formatting
 $hosts = $hosts."<host name=\"$computer\" profile-id=\"$OU\" />\n";
 
 
 }
 
 # Append an ending to the file
 $hosts = $hosts."</wpkg>\n";
 
 # Where to put the xml file - in this case, we don't overwrite what's in WPKG/hosts
 $data_file = "$share/'''WPKG/hosts/created_from_AD/$OU.xml'''";
 
 # Open the file for writing
 open DATA, ">$data_file" or die "can't open $data_file $!";
 
 # Append data to the file
 print DATA "$hosts";
 
 # Clear @results
 undef @results;
 
 }
 
 # close the file and umount a share on EDU DC
 close DATA;
 system "umount $share";

Cron entry

You can start it every hour via cron on your Linux system:

# generate hosts.xml from AD
01 * * * * root perl /opt/ldap-wpkg.pl &>/dev/null

Pulling workstation names to hosts.xml from Active Directory OUs automatically with python

if you have different OUs in your AD, and these OUs use different software settings, you will likely want to generate hosts.xml from your AD tree. Here is a simple python script for that - you have to execute it on a Linux server, and you need to have python-ldap installed.

#!/usr/bin/python
import ldap, string, os, time, sys
hostfilepath = "/path/WPKG/hosts/created_from_AD/hosts.xml"             
domain = "example.com" 
l=ldap.initialize("ldap://dc1."+domain+":389")
l.simple_bind_s("domain\\username","password") 
scope	= 'dc=example,dc=com'
os.system('/bin/rm -rf %s'  % ("/path/WPKG/hosts/created_from_AD/hosts.xml"))  

HostFile = open("/path/WPKG/hosts/created_from_AD/hosts.xml","a+") 
HostFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<wpkg>\n")  
try:
    res = l.search_s(scope, ldap.SCOPE_SUBTREE, "(&(ObjectCategory=computer) )", ['name', 'canonicalName'])

    for (dn, vals) in res:
      accountname = vals['name'][0].lower()
      try:
        ou = vals['canonicalName'][0].lower()
      except: 
        ou = vals['name'][0].lower()

      ou = ou.replace(accountname,' ')
      ou = ou.replace(' ','') 
      ou = ou.replace('/','-')
      ou = ou.rstrip('-') 


      HostFile.writelines("<host name=" + '"' + accountname + '"' +   " profile-id=" + '"' +  ou + '"' + " />\n")
      
except ldap.LDAPError, error_message:
  print error_message          

HostFile.writelines("</wpkg>\n")
HostFile.close()
l.unbind_s()

Pulling workstation names to hosts.xml from Active Directory OUs automatically with vbScript

Single Profile Method

This script takes out all computer objects from your whole Active directory Tree, and writes the OU in the hosts.xml

 Const ADS_SCOPE_SUBTREE = 2
 
 set fs = CreateObject("Scripting.FileSystemObject")
 set textstream = fs.CreateTextFile("hosts.xml", True)
 textstream.WriteLine "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
 textstream.WriteLine "<!-- automagically generated with " & Wscript.ScriptFullName 
 textstream.WriteLine "     Date: " & Date() & "  -->" & vbCrLf & vbCrLf
 textstream.WriteLine "<wpkg>"
 
 Set rootDSE = GetObject("LDAP://RootDSE")
 domainContainer =  rootDSE.Get("defaultNamingContext")
 
 Set objConnection = CreateObject("ADODB.Connection")
 Set objCommand =   CreateObject("ADODB.Command")
 objConnection.Provider = "ADsDSOObject"
 objConnection.Open "Active Directory Provider"
 
 Set objCOmmand.ActiveConnection = objConnection
 objCommand.CommandText = _
     "Select Name, distinguishedName from 'LDAP://" & domainContainer & "' " _
         & "Where objectClass='computer'"  
 objCommand.Properties("Page Size") = 1000
 objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
 Set objRecordSet = objCommand.Execute
 objRecordSet.MoveFirst
 
 Do Until objRecordSet.EOF
     'Wscript.Echo "Computer Name: " & objRecordSet.Fields("Name").Value
     'Wscript.Echo "distinguishedName: " & objRecordSet.Fields("distinguishedName").Value
     arrPath = Split(objRecordSet.Fields("distinguishedName").Value, ",")
     strOU = ""
     for each a in arrPath
 		  if left(a,2) = "OU" Then
 			  strOU = "/" & right(a,len(a) - 3) & strOU 
 			End If
 		Next
 		'Wscript.Echo "Path: " & StrOU
 		textstream.WriteLine vbTab & "<host name=""" & objRecordSet.Fields("Name").Value & """ profile-id=""" & StrOU & """ />"
     objRecordSet.MoveNext
 Loop
 
 textstream.WriteLine "</wpkg>"
 textstream.close
 Wscript.Echo "Finished..."

Cron entry

You can start it every hour via cron on your Linux system:

# generate hosts.xml from AD
01 * * * * root python /opt/ldap-wpkg.py &>/dev/null

Multiple Profile Method

Alternatively if you want hosts listed with each OU they reside within separately to allow you to apply software to the higher level OUs, this should do the trick:

' Short Description: Output AD Hosts to hosts.xml file
' Original source code:  http://wpkg.org/WPKG_with_Active_Directory#Pulling_workstation_names_to_hosts.xml_from_Active_Directory_OUs_automatically_with_vbScript

' Modifed by: Marc Ozin
' Modified Date: 2010-03-02
' Modification:
' Hosts  generated with multiple Profile-IDs to show each sub OU the Host resides within.
' e.g. if Computer1 is contained within Head-Office/Finance/Payroll the following Profile-IDs will be generated:
'	<host name="Computer1">
'		<profile-id="/root" />
'		<profile-id="/root/Head-Office" />
'		<profile-id="/root/Head-Office/Finance" />
'		<profile-id="/root/Head-Office/Finance/Payroll" />
'	</host>

' Modifed by: Marc Ozin
' Modified Date: 2011-08-09
' Modification:
' after compiling profiles from AD, any computers listed that also exist in the hosts-manual.xml file have extra profiles attached.
' the format of the hosts-manual.xml is as follows:
' <wpkg>
'	<profile>
'	<profile id="manualprofile1">
'		<host name="computer1"/>
'		<host name="computer2"/>
'		<host name="computer3"/>
'	</profile>
'	<profile id="manualprofile2">
'		<host name="computer1"/>
'		<host name="computer4"/>
'		<host name="computer6"/>
'	</profile>
'</wpkg>

Const ADS_SCOPE_SUBTREE = 2
Const VBquot = """"
Const ManualHostFile = "hosts-manual.xml"
Const wpkgPath = "\\myserver\wpkg$"

Dim objProfileNodes, ProfileNodeItem, objHostNodes

 
 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 "     Date: " & Date() & "  -->" & vbCrLf & vbCrLf
 textstream.WriteLine "<wpkg>"
 
 Set rootDSE = GetObject("LDAP://RootDSE")
 domainContainer =  rootDSE.Get("defaultNamingContext")
 
 Set objConnection = CreateObject("ADODB.Connection")
 Set objCommand =   CreateObject("ADODB.Command")
 objConnection.Provider = "ADsDSOObject"
 objConnection.Open "Active Directory Provider"
 
 Set objCOmmand.ActiveConnection = objConnection
 objCommand.CommandText = _
     "Select Name, distinguishedName from 'LDAP://" & domainContainer & "' " _
         & "Where objectClass='computer'"  
 objCommand.Properties("Page Size") = 1000
 objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
 Set objRecordSet = objCommand.Execute
 objRecordSet.MoveFirst
 
 Do Until objRecordSet.EOF
     'Wscript.Echo "Computer Name: " & objRecordSet.Fields("Name").Value
     'Wscript.Echo "distinguishedName: " & objRecordSet.Fields("distinguishedName").Value
     arrPath = Split(objRecordSet.Fields("distinguishedName").Value, ",")
     strOU = ""
	 
	 textstream.WriteLine vbTab & "<host name="& VBquot & objRecordSet.Fields("Name").Value & VBquot &  " profile-id=" & VBquot & "root" & VBquot & ">"
	 
     for each a in arrPath
 		if left(a,2) = "OU" Then
			strOU = "/" & right(a,len(a) - 3) & strOU 
 		End If
 	Next
 

		arrProfiles=Split(StrOU,"/")
		sFullProfile = "root"
		iDepth=0
		for each sProfile in arrProfiles
			iDepth = iDepth + 1
			if iDepth > 1 then
				sFullProfile = sFullProfile & "/" & sProfile
				textstream.WriteLine vbTab & vbTab & "<profile id=" & VBquot & sFullProfile & VBquot & " />"
			end if
			
		next
		textstream.WriteLine vbTab & "</host>"
		
		
		
     objRecordSet.MoveNext
 Loop
 
 textstream.WriteLine "</wpkg>"
 textstream.close
 
 
 ' * Add manual entries from hosts-manual.xml *************************************************************************************************************
 
 
 if bFileExists(wpkgPath & "\" & ManualHostFile) then
	Set objDOMHosts  = CreateObject("Microsoft.XMLDOM") 
	objDOMHosts.async = false 
	objDOMHosts.load(wpkgPath & "\hosts.xml")
 
	Set objDOMHostsManual = CreateObject("Microsoft.XMLDOM")
	objDOMHostsManual.async = False
	objDOMHostsManual.load(wpkgPath & "\" & ManualHostFile)


	Set objProfileNodes = objDOMHostsManual.documentElement.childNodes

	For Each ProfileNodeItem In objProfileNodes
		if ProfileNodeItem.nodeName  = "profile" then
'			wscript.echo ProfileNodeItem.getAttribute("id")
			Set objHostNodes = ProfileNodeItem.childNodes
			For Each HostNodeItem In objHostNodes
'				wscript.echo HostNodeItem.getAttribute("name")
				strXPath = "/wpkg/host[@name='" & HostNodeItem.getAttribute("name") & "']"
				Set objParentNode = CreateObject("Microsoft.XMLDOM")
				set objParentNode = objDOMHosts.selectSingleNode(strXPath)
				AddProfile objParentNode, ProfileNodeItem.getAttribute("id")
			next
		end if
	Next

	objDOMHosts.save(wpkgPath & "\hosts.xml") 
 end if


' * Subs & Functions *************************************************************************************************************

	
sub AddProfile(objParentNode, strValue)
	Dim objDOMHosts, objNode, strName, objAttrib
		Set objDOMHosts  = CreateObject("Microsoft.XMLDOM")  
		Set objNode = objDOMHosts.createElement("Profile")
		objNode.setAttribute "id", strValue
		objParentNode.appendChild objNode
End sub


Function bFileExists(strFile)
	set objFSO = createobject("Scripting.FileSystemObject")
	bFileExists = objFSO.FileExists(strFile) 
end function
You'll need to edit the line:
Const wpkgPath = "\\myserver\wpkg$"
...replacing it with the path to your wpkg folder.

Don't forget you'll need to create a profile definition in the profiles.xml for all of the profiles that this script will create!

This version also allows you to build manual profile entries as well. to use additional manual profiles, create a file called hosts-manual.xml in the wpkg folder like this:

 <wpkg>
	<profile>
	<profile id="manualprofile1">
		<host name="computer1"/>
		<host name="computer2"/>
		<host name="computer3"/>
	</profile>
	<profile id="manualprofile2">
		<host name="computer1"/>
		<host name="computer4"/>
		<host name="computer6"/>
	</profile>
</wpkg>