Managing the list of 1C 8.2 databases using Active Directory

Greetings, dear reader!
By tradition, I ask you not to kick too much, because This is my first post.

So, about six months ago, the task was to automate the management of the list of 1C databases (of which more than 20 have already been divorced) from users of the domain.
This was done not only for the sake of convenience, but also within the framework of the project to introduce a “role-based access model”. In short, the meaning of this model is that each user in the domain is a member of a certain group (named according to the position), which has a predefined set of privileges, including a list of infobases.

Because we have an Active Directory domain, it’s logical to use group policies to complete our task.
Googling produced quite a few implementations (and even paid ones), but all of them, most often, came down to pre-generated files with lists of databases (ibases.v8i). We wanted to:
a) Centrally manage the settings for connecting to infobases (we have a client-server version with SQL databases).
b) Centrally manage the list of information bases available to the user according to his “role”.

As a result, I will talk about a solution that has been working for more than six months in our company.


So let's get started.

Step 1 .

1C 8.2 stores the list of infobases in the ibases.v8i file, such a file is present in the profile of each user. The format and operation of this file are well described here and here., so I don’t see the point here.
Also, in the same directory with the ibases.v8i file, there is the 1CEStart.cfg file, a feature of this file is that it can contain paths to individual * .v8i files containing parameters for connecting to specific infobases.
At startup, 1C takes the parameters of connections to infobases from the files registered in 1CEStart.cfg and puts them in ibases.v8i. We will use this feature.
First, create a v8i file for each infobase.
The easiest way to create such a file is to right-click on the desired database in the list and select the option “Save link to file”:
image
However, it should be borne in mind that the v8i file formed in this way contains some "extra" lines that we do not need. For normal operation, it is enough to leave only the following:

[%NAME% ]
Connect=Srvr="%server%";Ref="%base%";
ClientConnectionSpeed=Normal
App=Auto
WA=1
Version=8.2

Next, you need to place these files in a public place for local network users, and give them "read" rights. I did not bother, and just placed them in the NETLOGON folder of the domain controller. There are several reasons for this - this is both directory replication between domain controllers and fault tolerance (due to the fact that there are three controllers, and at least one of them is available at any given time).

Step 2 .

Since we are going to manage the list of infobases on the basis of the user's membership in a particular AD group, we will create the necessary number of security groups in it according to our 1C databases:
image

The prefix “1C_82” is mandatory, and it will become clear why.

Now, in each newly created security group, in the “notes” field, we indicate the path to the corresponding v8i file:
image

We are done with the groups.

Step 3 .

Create a group policy that will run the following vbs script every time the user logon:

Vbs code
On Error Resume Next
Const PROPERTY_NOT_FOUND  = &h8000500D
Dim sGroupNames
Dim sGroupDNs
Dim aGroupNames
Dim aGroupDNs
Dim aMemof
Dim oUser
Dim tgdn
Dim fso
Dim V8iConfigFile
Dim dir
Const ForReading = 1, ForWriting = 2, ForAppending = 8
'Настраиваем лог файл
Set fso = CreateObject("Scripting.FileSystemObject")
Set WshShell = WScript.CreateObject("Wscript.Shell")
strSysVarTEMP = WshShell.ExpandEnvironmentStrings("%TEMP%")
Set oScriptLog = fso.OpenTextFile(strSysVarTEMP + "\_dbconn.log",ForWriting,True)
oScriptLog.Write ""
strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Start..."
oScriptLog.WriteLine(strToLog)
'Проверяем, что 1С установлена
Set objFSO = CreateObject("Scripting.FileSystemObject")
If Not (objFSO.FolderExists("C:\Program Files\1cv82") Or objFSO.FolderExists("C:\Program Files (x86)\1cv82")) Then
 strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "1C 8.2 not installed... Quit..."
 oScriptLog.WriteLine(strToLog)
    WScript.quit
End If
'Проверяем есть ли старый файл и удаляем в случае наличия'
 APPDATA = WshShell.ExpandEnvironmentStrings("%APPDATA%")
 v8i = APPDATA + "\1C\1CEStart\ibases.v8i"
 If fso.FileExists(v8i) Then 
	fso.DeleteFile(v8i)
	Set V8iConfigFile = fso.CreateTextFile(v8i ,True)
	strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Удален файл v8i и создан новый"
	oScriptLog.WriteLine(strToLog)
' Если файла нет (1С только установлена), то создаем файла по указанному пути
 Else
	Set dir = fso.CreateFolder(APPDATA + "\1C")
	Set dir = fso.CreateFolder(dir + "\1CEStart")
	Set V8iConfigFile = fso.CreateTextFile(v8i ,True)
	strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Создан файл v8i"
	oScriptLog.WriteLine(strToLog)
 End if
'
' Initialise strings. We make the assumption that every account is a member of two system groups
'
sGroupNames = "Authenticated Users(S),Everyone(S)"
'
' Enter the DN for the user account here
Set objSysInfo = CreateObject("ADSystemInfo")
strUserName = objSysInfo.UserName
strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Logged user DN: "+strUserName
oScriptLog.WriteLine(strToLog)
'  Получаем имя залогиненного пользователя
Set oUser = GetObject("LDAP://" + strUserName)
If Err.Number <> 0 Then
        strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "There is an error retrieving the account. Please check your distinguished name syntax assigned to the oUser object."
        oScriptLog.WriteLine(strToLog)
  WScript.quit
End If
'
' Determine the DN of the primary group
' We make an assumption that every user account is a member of a primary group
' 
iPgid = oUser.Get("primaryGroupID")
sGroupDNs = primgroup(iPgid)
tgdn = sGroupDNs
'
' Call a subroutine to extract the group name and scope
' Add the result to the accumulated group name String
'
Call Getmemof(tgdn)
'
' Check the direct group membership for the User account
'
aMemOf = oUser.GetEx("memberOf")
If Err.Number <> PROPERTY_NOT_FOUND Then
'
' Call a recursive subroutine to retrieve all indirect group memberships
'
        Err.clear
    For Each GroupDN in aMemof
        Call AddGroups(GroupDN)
        Call Getmemof(GroupDN)
    Next
End If
aGroupNames = Split(sGroupNames,",")
aGroupDNs = Split(sGroupDNs,":")
'Откидываем все группы, кроме начинающихся с 1C_82
For Each strGroupDN in aGroupDNs
 if StrComp(Mid(strGroupDN,1,8), "CN=1C_82", vbTextCompare) = 0 Then
  strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "User is member of: " + strGroupDN
  oScriptLog.WriteLine(strToLog)
  Set objGroup = GetObject("LDAP://" & strGroupDN)
  If Err.Number <> 0 Then
   strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "There is an error retrieving the group. Please check your distinguished name syntax assigned to the objGroup object: " + strGroupDN
   oScriptLog.WriteLine(strToLog)
   WScript.quit
  End If
  strInfo = objGroup.Get("info")
  strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Group " + strGroupDN +" info field: " + strInfo
  oScriptLog.WriteLine(strToLog)
  strAllInfo = strAllInfo & ":" & strInfo
 End If
Next
aInfoStrings = Split(strAllInfo,":")
Call WriteDBSettings()
Sub WriteDBSettings()
'Прописываем ссылки на v8i файлы в 1CEStart.cfg
strSysVarAPPDATA = WshShell.ExpandEnvironmentStrings("%APPDATA%")
strDBConfigFilePath = strSysVarAPPDATA + "\1C\1CEStart\1CEStart.cfg"
strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "1C Config file is: " + strDBConfigFilePath
oScriptLog.WriteLine(strToLog)
If (fso.FileExists(strDBConfigFilePath)) Then
 Set objDBConfigFile = fso.OpenTextFile(strDBConfigFilePath,ForWriting,True)
 objDBConfigFile.Write ""
 For each strInfo in aInfoStrings
  objDBConfigFile.WriteLine("CommonInfoBases=" + strInfo)
  strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Add Line: " + "CommonInfoBases=" + strInfo
  oScriptLog.WriteLine(strToLog)
 next
'Изменить на 0, если аппаратные лицензии не используются
 objDBConfigFile.WriteLine("UseHWLicenses=1")
 strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Add Line: " + "UseHWLicenses=1"
 oScriptLog.WriteLine(strToLog)
 strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Ready"
 oScriptLog.WriteLine(strToLog)
 objDBConfigFile.Close
Else
 Set fso = CreateObject("Scripting.FileSystemObject")
 Set WshShell = WScript.CreateObject("Wscript.Shell")
 Set objDBConfigFile = fso.OpenTextFile(strDBConfigFilePath,ForWriting,True)
 objDBConfigFile.Write ""
 For each strInfo in aInfoStrings
  objDBConfigFile.WriteLine("CommonInfoBases=" + strInfo)
  strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "Add Line: " + "CommonInfoBases=" + strInfo
  oScriptLog.WriteLine(strToLog)
 next
 strToLog = CStr(Date())+" "+CStr(Time()) + " - " + "1C Config file" + strDBConfigFilePath + " Not Exist! Create!"
 oScriptLog.WriteLine(strToLog)
 WScript.Quit
End If
End Sub
'*************************************************************************************************
' End of mainline code
'*************************************************************************************************
Function primgroup(groupid)
' This function accepts a primary group id
' It binds to the local domain and returns the DN of the primary group
' David Zemdegs 6 May 2008
'
Dim oRootDSE,oConn,oCmd,oRset
Dim ADDomain,srchdmn
' Bind to loca domain
Set oRootDSE = GetObject("LDAP://RootDSE")
ADDomain = oRootDSE.Get("defaultNamingContext")
srchdmn = ""
'
' Initialise AD search and obtain the recordset of groups
' 
Set oConn = CreateObject("ADODB.Connection")
oConn.Open "Provider=ADsDSOObject;"
Set oCmd = CreateObject("ADODB.Command")
oCmd.ActiveConnection = oConn
oCmd.CommandText = srchdmn & ";(objectCategory=Group);" & _
        "distinguishedName,primaryGroupToken;subtree" 
Set oRset = oCmd.Execute
'
' Loop through the recordset and find the matching primary group token
' When found retrieve the DN and exit the loop
' 
Do Until oRset.EOF
    If oRset.Fields("primaryGroupToken") = groupid Then
        primgroup = oRset.Fields("distinguishedName")
        Exit Do
    End If
    oRset.MoveNext
Loop
'
' Close and tidy up objects
' 
oConn.Close
Set oRootDSE = Nothing
Set oConn = Nothing
Set oCmd = Nothing
Set oRset = Nothing
End Function
Sub Getmemof(sDN)
'
' This is recursive subroutine that calls itself for memberof Property
' David Zemdegs 6 May 2008
'
On Error Resume Next
Dim oGrp
Dim aGrpMemOf
Dim sGrpDN
Set oGrp = GetObject("LDAP://" & sDN)
aGrpMemOf = oGrp.GetEx("memberOf")
If Err.Number <> PROPERTY_NOT_FOUND Then
'
' Call a recursive subroutine to retrieve all indirect group memberships
'
        Err.clear
    For Each sGrpDN in aGrpMemOf
                Call AddGroups(sGrpDN)
        Call Getmemof(sGrpDN)
    Next
End If
Err.clear
Set oGrp = Nothing
End Sub
Sub AddGroups(sGdn)
'
' This subroutine accepts a disguished name
' It extracts the RDN as the group name and determines the group scope
' This is then appended to the group name String
' It also appends the DN to the DN String
'
Const SCOPE_GLOBAL = &h2
Const SCOPE_LOCAL = &h4
Const SCOPE_UNIVERSAL = &h8
Dim SNewgrp
'
' Retrieve the group name
'
iComma = InStr(1,sGdn,",")
sGrpName = Mid(sGdn,4,iComma-4)
'
' Add the results to the group name String
' Check that the group doesnt already exist in the list
'
sNewgrp = sGrpName
If InStr(1,sGroupNames,SNewgrp,1) = 0 Then
        sGroupNames = sGroupNames & "," & SNewgrp
End If
'
' Add the Groups DN to the string if not duplicate
'
If InStr(1,sGroupDNs,sGdn,1) = 0 Then
        sGroupDNs = sGroupDNs & ":" & sGdn
End If
End Sub


The logic of the script is as follows:
1. Checks if 1C is installed, if not, the script ends.
2. Checks if the ibases.v8i file exists, and overwrites it empty (or creates it if not).
3. Retrieves all groups from AD of which the user is a member.
4. Drops everything except those starting with 1C_82.
5. Gets the value of the "Notes" attribute.
6. It writes the value of this attribute to the 1CEStart.cfg file. A
log is written along the way:
For Windows 7 - C: \ Users \ username \ appdata \ Local \ Temp \ _dbconn.log
For Windows XP - C: \ Documents and Settings \ username \ Local Settings \ Temp \ _dbconn.log

Step 4 .

We "hang" group policy on the necessary OU or the entire domain. It is worth noting that in order for the script not to be used indiscriminately (not all users work with 1C), I added only the groups that we created in step 2 to the Group Policy security filter, so the script will only work for users included into at least one of these groups:
image

Step 5 .

We include the group (read position) of the user in those 1C groups that are provided for by the role-based access model (although it is possible for a single user as well - there are exceptions). After the reboot, the user will have a list of information bases that is individual for his position.
Well, that seems to be all.
By the way, to apply the changes, the user does not have to log in, you just need to force the user to execute this script in any convenient way, for example, by sending the script by e-mail.

Thank you for your attention, I will be very glad if the article helps someone.

Also popular now: