Недавно передо мной возникла задача отделять в SCCM рабочие станции, управляемые IT-отделом, от рабочих станций, управляемых пользователями самостоятельно. Критерием самостоятельно управляемой рабочей станции было выбрано наличие каких-либо учётных записей в группе локальных администраторов, кроме Administrator, Domain Admins и группы сотрудников техподдержки. Все рабочие станции расположены в одном OU, поэтому создание коллекций по OU не подходило.
Как вы возможно знаете, SCCM 2012 не имеет встроенных средств для получения членства в локальных группах рабочих станций и серверов. Спасибо Sherry Kissinger, которая решила эту проблему с использованием Compliance Settings.
После установки её пакета, вы получите новые Configuration Baseline, Configuration Item и 2 таблицы и 1 view в базе SCCM: LocalGroupMembers_DATA, LocalGroupMembers_HIST и v_GS_LocalGroupMembers0.
В дальнейшем вы можете использовать данные из view v_GS_LocalGroupMembers0 для построения отчётов о членах локальных групп управляемых вами компьютеров (Не забудьте распространить baseline на нужную коллекцию компьютеров, но ни в коем случае не на контроллеры домена! Например, создайте коллекцию с правилом Include для стандартной коллекции All Systems и правилом Exclude для коллекции All Domain Controllers. Вы можете загрузить MOF-файл такой коллекции здесь).
НО, к сожалению, ни таблица LocalGroupMembers_DATA, ни v_GS_LocalGroupMembers0 недоступны для WQL-запросов, которые используются при создании коллекций.
Казалось бы, я застрял. Что у меня было на руках на тот момент:
- У меня есть все данные о членстве в локальных группах на всех компьютерах.
- Я могу построить любые отчёты по ним.
- У меня есть средства для построения коллекций по данным из стандартных таблиц SCCM.
- SCCM не может использовать данные из нестандартных таблиц для построения коллекций.
Итак, нам нужен способ поместить данные из таблицы LocalGroupMembers_DATA в стандартные таблицы SCCM для построения коллекции по ним и PowerShell нам в этом поможет.
Есть как минимум 2 способа получения данных из SQL в PowerShell:
- Подключаться напрямую к базе и использовать SQL-запросы при помощи командлетов SQL.
- Или подключаться к SQL Server Reporting Services с использованием командлета New-WebServiceProxy. Stefan Stranger и Jin Chen написали сценарий для этого.
Т.к. это PowerShell, дальше мы можем делать с данными из SQL всё, что угодно. Наша задача – это наполнение коллекции устройств, и тут опять существует как минимум 2 варианта:
- Добавлять компьютеры в группу AD DS, по которой затем создавать коллекцию устройств в SCCM. В этом случае, метод обнаружения Active Directory Group Discovery для сайта и домена, где будет расположена эта группа, должен быть активирован.
- Напрямую добавлять компьютеры в коллекцию SCCM используя командлет Add-CMDeviceCollectionDirectMembershipRule.
Т.к. и отчёт, и группа в AD DS будут мне полезны в будущем, я выбрал вариант именно с ними.
Итак, общий сценарий действий такой:
- Активировать Active Directory Group Discovery.
- При помощи Compliance Settings собирать сведения о членстве в локальных группах на компьютерах.
- Строить отчёт по этой таблице на любом SSRS.
- Используя командлет New-WebServiceProxy, получать имена компьютеров из этого отчёта.
- Добавлять эти компьютеры в специальную группу.
- Стандартными средствами SCCM наполнять коллекцию устройств по группе AD DS.
Я построил отчёт в котором вывожу списком все компьютеры, не удовлетворяющие условиям, описанным выше. Вот, как выглядит запрос для первого DataSet’а:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
SELECT distinct Computer.Name0 AS [Computer Name] FROM v_GS_LocalGroupMembers0 AS LGMs INNER JOIN v_R_System AS Computer ON LGMs.ResourceID = Computer.ResourceID INNER JOIN v_RA_System_SystemOUName AS OUs ON Computer.ResourceID = OUs.ResourceID WHERE ( (OUs.System_OU_Name0 IN (@CompOU)) AND (LGMs.Name0 = N'Administrators' OR LGMs.Name0 = N'Администраторы') AND NOT ( LGMs.Account0 = N'Domain Admins' AND LGMs.Category0 = N'Group' AND LGMs.Type0 = N'Domain' ) AND NOT ( LGMs.Account0 = N'Administrator' AND LGMs.Category0 = N'UserAccount' AND LGMs.Type0 = N'Local' AND LGMs.Name0 = N'Administrators' ) AND NOT ( LGMs.Account0 = N'ServiceDesk Workstations Administrators' AND LGMs.Category0 = N'Group' AND LGMs.Type0 = N'Domain' AND LGMs.Domain0 = N'EXAMPLE' ) AND NOT ( LGMs.Account0 = N'Администратор' AND LGMs.Category0 = N'UserAccount' AND LGMs.Type0 = N'Local' AND LGMs.Name0 = N'Администраторы' ) ) |
Вы легко можете расширить его для исключения как-то других групп.
Обратите внимание на параметр CompOU: в веб-интерфейсе вы можете выбрать одно или несколько OU, откуда выводить компьютеры.
Для получения полного списка этих OU из ваших лесов, служит второй DataSet с таким кодом:
1 2 3 4 5 |
SELECT DISTINCT v_RA_System_SystemOUName.System_OU_Name0 FROM v_RA_System_SystemOUName ORDER BY v_RA_System_SystemOUName.System_OU_Name0 ASC |
Далее я модифицировал сценарий RenderSQLReportFromPosh.v1.000.ps1 таким образом, чтобы он не только получал отчёт из SSRS, но и на его основе формировал группу в AD DS. Вот его полный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
#requires -version 2.0 ############################################################################### # Render SQL Reports using PowerShell # This script is using the new Posh v 2.0 cmdlet New-WebServiceProxy # FileName: RenderSQLReportFromPosh.v1.000.ps1 # Authors: Stefan Stranger (Microsoft) # Help from: Jin Chen (Microsoft) # Example of Rendering the OpsMgr Licenses Report from the Generic Report Library # # v1.000 – 15/05/2010 - stefstr - initial sstranger's release # 10/29/2013 - Modified by Kirill 'kf' Nikolaev to satisfy SCCM needs ############################################################################### #Log file path $ScriptPath = Split-Path $MyInvocation.MyCommand.Path $ScriptName = ($MyInvocation.MyCommand.Name).Substring(0,($MyInvocation.MyCommand.Name).Length-4) $Log = Join-Path $ScriptPath "$ScriptName.txt" Add-Content $Log (Get-Date) #Clear-Content $Log -ErrorAction SilentlyContinue $Error.Clear() #Define Variables #Enter URI to asmx file on Report Server $URI = 'https://reportserver.example.com//ReportServer//ReportExecution2005.asmx?wsdl' #Enter Report Path $ReportPath = '/ConfigMgr_CAS/Compliance and Settings Management/Workstations with non-default local administrators' #Enter group name to fill with workstations $GroupName = 'Self-Managed Workstations' $format = 'csv' $deviceinfo = '' $extention = '' $mimeType = '' $Result = '' $render = '' $encoding = 'UTF-8' $warnings = $null $streamIDs = $null $Reports = New-WebServiceProxy -Uri $URI -UseDefaultCredential -namespace 'ReportExecution2005' $rsExec = new-object ReportExecution2005.ReportExecutionService $rsExec.Credentials = [System.Net.CredentialCache]::DefaultCredentials $execInfo = @($ReportPath, $null) #Load the selected report. $rsExec.GetType().GetMethod('LoadReport').Invoke($rsExec, $execInfo) | out-null #Report Parameters #Depending on the number of Parameters being used in the Report you need to add more Parameters. #Search the rdl file for the correct parameter names. $param1 = new-object ReportExecution2005.ParameterValue $param1.Name = 'CompOU' $param1.Value = 'EXAMPLE.COM/WORKSTATIONS' $param2 = new-object ReportExecution2005.ParameterValue $param2.Name = 'CompOU' $param2.Value = 'EXAMPLE.COM/WORKSTATIONS-OLD' $parameters = [ReportExecution2005.ParameterValue[]] ($param1, $param2) $ExecParams = $rsExec.SetExecutionParameters($parameters, 'en-us'); $render = $rsExec.Render($format, $deviceInfo,[ref] $extention, [ref] $mimeType,[ref] $encoding, [ref] $warnings, [ref] $streamIDs) $Result = [text.encoding]::ascii.getString($render) $ComputerNames = @() $SplittedResult = $Result.Split("`n") for ($i = 1; $i -le $SplittedResult.Count-3) { #For unknown reason, last 3 rows of SSRS answer are blank, so we have to cut them and a title too. $ComputerNames += ($SplittedResult[$i]).Substring(0,($SplittedResult[$i]).Length-1) #Again, for unknown reason, there is an invisible line-feed symbol, we remove it. $i++ } $ComputerObjects = @() foreach ($Computer in $ComputerNames) { $ComputerObjects += Get-ADComputer $Computer } $CurrentMembers = @() $CurrentMembers = Get-ADGroupMember -Identity $GroupName $ToAdd = @() $ToRemove = @() if ($CurrentMembers) { if ($ComputerObjects) { $CompareResult = Compare-Object -ReferenceObject $ComputerObjects -DifferenceObject $CurrentMembers foreach ($Item in $CompareResult) { $DN = $Item.InputObject.DistinguishedName if ($Item.SideIndicator -eq '<=') { $ToAdd += $Item.InputObject Add-Content $Log "$DN - ToAdd" } elseif ($Item.SideIndicator -eq '=>') { $ToRemove += $Item.InputObject Add-Content $Log "$DN - ToRemove" } } } else { foreach ($Item in $CompareResult) { $DN = $Item.DistinguishedName $ToRemove += $Item.DistinguishedName Add-Content $Log "$DN - ToRemove" } } } else { foreach ($Item in $ComputerObjects) { $DN = $Item.DistinguishedName $ToAdd += $Item.DistinguishedName Add-Content $Log "$DN - ToAdd" } } if ($ToAdd) { try { Add-ADGroupMember -Identity $GroupName -Members $ToAdd -ErrorAction SilentlyContinue } catch { Add-Content $Log 'Cannot add' Add-Content $Log $Error[0] } } if ($ToRemove) { try { Remove-ADGroupMember -Identity $GroupName -Members $ToRemove -ErrorAction SilentlyContinue -Confirm:$false } catch { Add-Content $Log 'Cannot remove' Add-Content $Log $Error[0] } } |
Сценарий получает отчёт с SSRS, определяемому в переменных $URI и $ReportPath, сравнивает список компьютеров из него с группой $GroupName и добавляет/удаляет компьютеры из неё таким образом, чтобы она совпадала со списком из отчёта.
Сценарий ведёт журнал работы по пути $Log, в который записывает, удалил он компьютер в группу или, наоборот, добавил.
В качестве параметров я передаю отчёту 2 OU ($param1 и $param2). Если вам нужно больше, просто создавайте новые параметры и добавляйте их в переменную $parameters.
Наконец, я создал стандартную коллекцию устройств на основе членства в группе $GroupName и всё заверте…
Загрузить RDL-файл отчёта и сценарий вы можете здесь. Не забудьте в отчёте создать DataSource для подключения к вашему серверу SSRS.