Scripts
All scripts are referenced on other pages within the documentation.
Scripts for installation and upgrades
Install Windows Server roles and features
The following PowerShell script is used on Windows Server roles and features. 1E Setup can do all of this except for Basic Authentication (required by 1E Core) and HTTP redirect (required if Content Distribution is supporting down-level clients after replacing ActiveEfficiency).
Import-Module ServerManager Get-WindowsFeature | Out-file $PSScriptRoot\ServerManager-1.txt -Append Install-WindowsFeature Web-Server, Web-Http-Redirect, Web-Dyn-Compression, Web-Basic-Auth, Web-IP-Security, Web-Windows-Auth, Web-Asp-Net45, Web-Mgmt-Console, Net-Framework-45-Core, Net-Framework-45-ASPNET Uninstall-WindowsFeature Web-DAV-Publishing Get-WindowsFeature | Out-file $PSScriptRoot\ServerManager-2.txt -Append
Changing the configuration of Network Adapters
The following extract is used here: Changing the configuration of Network Adapters.
If the configuration of any network adapters is changed, then the 1E website configuration for IP Address and Domain Restrictions may need to be updated with the server's new IPv4 and IPv6 addresses. Failure to update the configuration after a network change will cause issues between the Switch and the Core and prevent the 1E Server from functioning.
Use the PowerShell cmdlet Gevt-NetIPAddress to determine the current IP Addresses, and if necessary update the configuration of the Core web application.
The following AddIpSec.ps1 script can be used to refresh local IP Addresses. It does not remove old, unwanted, or remote IP Addresses. Nor does it add addresses for remote Switches, which would need to be added manually.
# Adds local IPv4, v6 and loopback addresses to the IP Address and Domain Restrictions of the specified web applications. It does not delete any addresses
# Remote addresses must be manually configured.
# It only adds addresses if the specified web application exists.
Import-Module "WebAdministration"
Function AddIpSec() {
param(
[Parameter(Mandatory=$true)][string]$SiteName,
[Parameter(Mandatory=$true)][string]$AppName
)
Write-Output "Adding IP restrictions to $AppName"
$fullAppPath = "IIS:\Sites\" + $SiteName + "\" + $AppName
if (Test-Path -Path $fullAppPath) {
$appPath = $SiteName + "/" + $AppName
$ipAddreses = Get-NetIPAddress | sort IpAddress | foreach { $_.IPAddress }
Set-WebConfigurationProperty /system.webserver/security/ipsecurity -Name allowUnlisted -Value "false" -Location $appPath -PSPath IIS:\
foreach ($ipAddress in $ipAddreses)
{
$existingRestriction = Get-WebConfiguration system.webServer/Security/ipSecurity/add -Location $appPath -PSPath IIS:\ | where { $_.ipAddress -eq $ipAddress }
if (-not ($existingRestriction)) {
Add-WebConfiguration system.webServer/security/ipSecurity -Location $appPath -Value @{ipAddress="$ipAddress";allowed="true"} -PSPath IIS:\
Write-Output "Allowed restriction on IP $ipAddress added to $AppName"
}
else {
Write-Output "Restriction on IP $ipAddress already exists in $AppName. So skipping."
}
}
}
else {
Write-Output "$AppName web application does not exist"
}
}
$siteName = "Tachyon"
AddIpSec -SiteName $siteName -AppName "Core"
AddIpSec -SiteName $siteName -AppName "WelcomeCore"Change Shopping 6.0 Sync from ActiveEfficiency to direct Configuration Manager
The following SQL script is used on In-place upgrade of ActiveEfficiency Server for Nomad.In-place upgrade of ActiveEfficiency Server for Nomad
-- Disclaimer: -- Your use of this script is at your sole risk. This script is provided "as-is", without any warranty, -- whether express or implied, of accuracy, completeness, fitness for a particular purpose, title or -- non-infringement, and is not supported or guaranteed by 1E. 1E shall not be liable for any damages -- you may sustain by using this script, whether direct, indirect, special, incidental or consequential, -- even if it has been advised of the possibility of such damages. UPDATE dbo.tb_Preference SET Description = 'Sets the interval for synchronizing the members in Computer Category groups held in the Shopping database with Active Directory. Also used for the ConfigMgr, Intune Machine sync and the ConfigMgr, Intune user sync. The value for the interval uses the units defined in the Active Directory Sync Units parameter.' WHERE preferencename = 'Users and Machines, AD Sync Interval' GO UPDATE dbo.tb_Preference SET Description = 'The unit is used by the AD, ConfigMgr, Intune Machine and User Sync Interval setting. This may be set to one of the following values: Days, Hours or Minutes.' WHERE preferencename = 'Users and Machines, AD Sync Units' GO UPDATE dbo.tb_Preference SET PreferenceValue = 'False' WHERE preferencename = 'Sync Machines and Users From Active Efficiency' GO UPDATE dbo.tb_Preference SET Hidden = 1 WHERE preferencename = 'Sync Machines From Old Sccm' GO UPDATE dbo.tb_Preference SET Hidden = 1 WHERE preferencename = 'ActiveEfficiency ServerName' GO
Granting Network Service access to Configuration Manager
The following SQL script is used on System Center Configuration Manager connector configuration.
-- Connect to the SQL instance using an account that has sysadmin rights.
-- Execute this SQL script on the Configuration Manager database.
-- The script creates a SQL Login on the instance for [hostname\ConfigMgr_DViewAccess]
-- and grants it the following rights on the Configuration Manager database:
-- [db_datareader]
-- EXECUTE on [dbo].[fn_GetAppState]
-- EXECUTE on [dbo].[fnGetSiteNumber]
DECLARE @SqlLogin nvarchar(2000)
DECLARE @SqlString nvarchar(max)
SET @SqlLogin = CAST(host_name() AS varchar(512)) + '\ConfigMgr_DViewAccess'
PRINT 'Granting permissions for ''' + @SqlLogin + ''' on database ' + DB_NAME() + ' ...'
SET @SqlString = '
IF NOT EXISTS ( SELECT schema_name FROM information_schema.schemata WHERE schema_name = '''+@SqlLogin +''' )
EXEC (''CREATE SCHEMA [' + @SqlLogin + ']'')
'
EXEC sp_executesql @SqlString
SET @SqlString = '
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = '''+ @SqlLogin +''')
CREATE LOGIN [' + @SqlLogin + '] FROM WINDOWS WITH DEFAULT_DATABASE=[master]
'
EXEC sp_executesql @SqlString
SET @SqlString = '
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = '''+ @SqlLogin +''')
CREATE USER [' + @SqlLogin + '] FOR LOGIN [' + @SqlLogin + '] WITH DEFAULT_SCHEMA=[' + @SqlLogin + ']
'
EXEC sp_executesql @SqlString
PRINT 'SQL Login created.'
SET @SqlString = '
ALTER ROLE [db_datareader] ADD MEMBER [' + @SqlLogin + ']
GRANT EXECUTE ON [dbo].[fn_GetAppState] TO [' + @SqlLogin + '] AS [dbo]
GRANT EXECUTE ON [dbo].[fnGetSiteNumber] TO [' + @SqlLogin + '] AS [dbo]
'
EXEC sp_executesql @SqlString
PRINT 'Permissions granted.'
Estimating the RAM requirements for AI Powered Auto-curation
The following scripts are used on AI Powered Auto-curation to calculate the amount of additional RAM that is required before you enable this feature.
The following script is run on the Configuration Manager database before 1E is installed.
/*SCCM database query to get number of distinct software titles from SCCM database*/
USE [YourSCCMDBName]
GO
SELECT COUNT(*) AS 'Distinct Software Titles'
FROM (
SELECT DISTINCT DisplayName0 AS Title FROM [v_ADD_REMOVE_PROGRAMS] WITH (NOLOCK)
UNION
SELECT DISTINCT [Caption0] AS Title FROM [v_GS_OPERATING_SYSTEM] WITH (NOLOCK)
) AS TitlesThe following script is run on SLA-Data database after 1E is installed and has already populated the inventory repository.
/*SLA query to get number of distinct software titles from SLA-Data database*/
USE [SLA-Data]
CREATE TABLE #Software
(
[SoftwareID] INT IDENTITY(1,1) PRIMARY KEY,
[DataSource] NVARCHAR(255) COLLATE DATABASE_DEFAULT,
[SoftwareIdent] NVARCHAR(MAX) COLLATE DATABASE_DEFAULT,
[SoftwareIdent_Hash] VARBINARY(128),
[Vendor] NVARCHAR(MAX) COLLATE DATABASE_DEFAULT,
[Vendor_Hash] VARBINARY(128),
[Title] NVARCHAR(MAX) COLLATE DATABASE_DEFAULT,
[Title_Hash] VARBINARY(128),
[Version] NVARCHAR(MAX) COLLATE DATABASE_DEFAULT,
[Version_Hash] VARBINARY(128),
[ColloquialVersion] NVARCHAR(MAX) COLLATE DATABASE_DEFAULT,
[ColloquialVersion_Hash] VARBINARY(128),
[Edition] NVARCHAR(MAX) COLLATE DATABASE_DEFAULT,
[Edition_Hash] VARBINARY(128),
[NormalizedProductID] INT
);
SELECT * FROM #Software
EXEC [usp_ReportDataEx_se] 1, N'Software', N'#Software';
SELECT count(*) AS 'Distinct Software Titles'
FROM
(
SELECT DISTINCT [Title] FROM #Software
) a
DROP TABLE #SoftwareModifying 1E Catalog AI Powered Auto-curation file download post-installation
The following script can be used to modify the EnableAIPackageSync setting in the Settings table of the 1ECatalog database, and is used on the Configuring 1E Catalog connection to 1E Cloud page. Click below to view the SQL script to configure the this setting.
/* Script to change 1ECatalog setting */ USE [1ECatalog] GO DECLARE @setting nvarchar(max), @oldvalue nvarchar(max), @newvalue nvarchar(max);; SET @setting = 'EnableAIPackageSync' SET @newvalue = 'True' SET @oldvalue = (SELECT [Value] FROM [dbo].[Settings] WHERE [Key]= @setting) UPDATE [dbo].[Settings] SET [Value]=@newvalue WHERE [Key]=@setting SELECT @setting AS 'Setting', @oldvalue AS 'Before', [Value] AS 'After' FROM [dbo].[Settings] WHERE [Key]=@setting GO
Modifying the AI Package Deployment Path
The Script to change SLA Deployment Path for AI Package script can be used to modify the AIModelDeploymentPath setting in the Settings tables of the SLA-Shared and SLA-Data databases and is used on the AI Powered Auto-curation page. It is not possible to modify the download path use by the 1E Catalog Update service. Click below to view the SQL script to configure this setting.
/* Script to change SLA Deployment Path for AI Package */ /* The value is copied from SLA-Shared to instance in SLA-Data when a new inventory repository is created */ /* Updating SLA-Shared means script is not required for each new repository */ /* Updating all values in SLA-Data means all repositories use the same value */ DECLARE @setting nvarchar(max), @oldvalue nvarchar(max), @newvalue nvarchar(max) SELECT @newvalue = 'C:\ProgramData\1E\SLA Platform\AI' SELECT @setting = 'AIModelDeploymentPath' USE [SLA-Shared] SELECT @oldvalue = [VALUE] FROM [dbo].[ProjectSetting] WHERE [Group] = 'ProcessAIEngine' AND [Name] = @setting UPDATE [dbo].[ProjectSetting] SET [Value]=@newvalue WHERE [Name]=@setting SELECT DB_NAME() + '.dbo.ProjectSetting' AS 'Table', @setting AS 'Setting', @oldvalue AS 'Before', [Value] AS 'After' FROM [dbo].[ProjectSetting] WHERE [Name]=@setting USE [SLA-Data] SELECT @oldvalue = [VALUE] FROM [dbo].InstanceSetting WHERE [Group] = 'ProcessAIEngine' AND [Name] = @setting AND [InstanceID] = 1 UPDATE [dbo].[InstanceSetting] SET [Value]=@newvalue WHERE [Name]=@setting SELECT DISTINCT DB_NAME() + '.dbo.InstanceSetting' AS 'Table', @setting AS 'Setting', @oldvalue AS 'Before', [Value] AS 'After' FROM [dbo].[InstanceSetting] WHERE [Name]=@setting GO
Modifying 1E Client Cloud synchronization behavior post-installation
The Script to change 1ECatalog setting script is used to configure the 1E Catalog SubscriptionType setting and is used on the Configuring 1E Catalog connection to 1E Cloud page. Click below to view the SQL script to configure this setting.
/* Script to change 1ECatalog setting */ USE [1ECatalog] GO DECLARE @setting nvarchar(max), @oldvalue nvarchar(max), @newvalue nvarchar(max);; SET @setting = 'SubscriptionType' SET @newvalue = '2' SET @oldvalue = (SELECT [Value] FROM [dbo].[Settings] WHERE [Key]= @setting) UPDATE [dbo].[Settings] SET [Value]=@newvalue WHERE [Key]=@setting SELECT @setting AS 'Setting', @oldvalue AS 'Before', [Value] AS 'After' FROM [dbo].[Settings] WHERE [Key]=@setting GO
Operational scripts
Create Direct-based management groups from a file
The following PowerShell script is used on the Management Groups page.
Note
This script has changed to support an API change in Platform 8.1
Minimum of 1E Powershell Toolkit v1.2.5 installed
Kid to Upn mapping should exist in the 1E Platform.
<#
.SYNOPSIS
1E Platform – Create Direct Management Group using Powershell Toolkit
Copyright © 1e Ltd 2023 - all rights reserved
http://www.1e.com
Version 4.0
Disclaimer
Your use of this software is at your sole risk. All software is provided "as-is", without any warranty, whether express or implied, of accuracy, completeness,
fitness for a particular purpose, title or non-infringement, and none of the software is supported or guaranteed by 1E. 1E shall not be liable for any damages
you may sustain by using this software, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such
damages.
If you have an issue using this software, please ensure you are using an unmodified version before contacting 1E Support.
.DESCRIPTION
This script reads the Device FQDN list in files and creates Direct Membership Management Groups through SLA/Tachyon APIs. If the group does not exist
it is created and devices are added. If the group exists, the existing devices are deleted and new devices are added.
.PARAMETER TachyonApiBaseUrl
Mandatory parameter. The header name used to access the Tachyon web service. Should match the header listed in the IIS bindings on the Tachyon server.
.PARAMETER FolderPath
Mandatory parameter. The name of the input folder which contains the files to be used in creating Direct Membership Management Groups. These files contain
the list of devices to be used in creating Management Groups. The File name should be same as the name of Name of Management Group. Also, the first line
of the file should contain the description of Management Group to be created, from next line onwards every line contains the Device FQDN.
.PARAMETER ClientAssertionAppId
Optional parameter. The AppId of the Client Assertion application created inside the cloud which is used for Authentication. Certificate should be present in same application.
.PARAMETER CertificateSubject
Optional parameter. Subject name of the Certificate used for Authentication.
.PARAMETER CertificateThumbprint
Optional parameter. Thumbprint of the Certificate used for Authentication.
.PARAMETER Upn
Optional parameter. UPN of the user for which mapping is targeted. Required only for Add and Update operations
.EXAMPLE
.\Create-DirectManagementGroupUsingFile.ps1 -TachyonApiBaseUrl https://tachyon.lab.local -FolderPath C:\Temp\MgFolder -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>"
or
.\Create-DirectManagementGroupUsingFile.ps1 -TachyonApiBaseUrl tachyon.lab.local -FolderPath C:\Temp\MgFolder -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>"
Scenario:
A Tachyon server is accessed using URL https://tachyon.lab.local/Tachyon using default credentials. The files containing the Device FQDN list
is in the directory C:\Temp\MyFolder. AppId and CertSubject are passed for authentication without browser. KidUpnMapping should be present in TachyonMaster table. This UPN is used for authentication.
.EXAMPLE
.\Create-DirectManagementGroupUsingFile.ps1 -TachyonApiBaseUrl https://tachyon.lab.local -FolderPath C:\Temp\MgFolder -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>" -Upn "<<TachyonPrinciple>>"
or
.\Create-DirectManagementGroupUsingFile.ps1 -TachyonApiBaseUrl tachyon.lab.local -FolderPath C:\Temp\MgFolder -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>" -Upn "<<TachyonPrinciple>>"
Scenario:
A Tachyon server is accessed using URL https://tachyon.lab.local/Tachyon using default credentials. The files containing the Device FQDN list
is in the directory C:\Temp\MyFolder. AppId and CertSubject are passed for authentication without browser. KidUpnMapping should be present in TachyonMaster table. This UPN is used for authentication.
.EXAMPLE
.\Create-DirectManagementGroupUsingFile.ps1 -TachyonApiBaseUrl https://tachyon.lab.local -FolderPath C:\Temp\MgFolder
or
.\Create-DirectManagementGroupUsingFile.ps1 -TachyonApiBaseUrl tachyon.lab.local -FolderPath C:\Temp\MgFolder
Scenario:
A Tachyon server is accessed using URL https://tachyon.lab.local/Tachyon using default credentials. The files containing the Device FQDN list
is in the directory C:\Temp\MyFolder. AppId and CertSubject are not passed. Authentication will happen through browser.
#>
param(
[Parameter(Mandatory=$true, HelpMessage="Enter Tachyon API Base URL e.g. : tachyon.testlab.com or https://tachyon.testlab.com")]
[ValidateNotNullOrEmpty()]
[string] $TachyonApiBaseUrl,
[Parameter(Mandatory=$true, HelpMessage="Enter Folder Path which contains files containing Device FQDN list")]
[ValidateNotNullOrEmpty()]
[string] $FolderPath,
[Parameter(Mandatory=$true, HelpMessage="Enter 1E Powershell Module Path e.g. : C:\Program Files\WindowsPowerShell\Modules")]
[ValidateNotNullOrEmpty()]
[string] $Ps1eModulePath,
[Parameter(Mandatory=$false, HelpMessage="Enter AppId of Cloud Tenant. If it passed then KidUpnMapping should be present in TachyonMaster table. This UPN is used for authentication. If it is not passed, Authentication will happen through browser")]
[string] $ClientAssertionAppId,
[Parameter(Mandatory=$false, HelpMessage="Enter Certificate Subject. If ClientAssertionAppId is not passed, it is not required")]
[string] $CertificateSubject,
[Parameter(Mandatory=$false, HelpMessage="Enter Certificate Thumbprint. If CertificateThumbprint is not passed, it is not required")]
[string] $CertificateThumbprint,
[Parameter(Mandatory=$false, HelpMessage="Enter Upn which will be used for making API call.")]
[string] $Upn
)
function Write-Log {
[CmdletBinding()]
Param(
[parameter(Mandatory=$true)]
[String]$Path,
[parameter(Mandatory=$true)]
[String]$Message,
[parameter(Mandatory=$true)]
[String]$Component,
[Parameter(Mandatory=$true)]
[ValidateSet("Debug", "Info", "Message", "Warning", "Error")]
[String]$Type
)
switch ($Type) {
"Info" { [int]$Type = 1 }
"Warning" { [int]$Type = 2 }
"Error" { [int]$Type = 3 }
"Debug" { [int]$Type = 4 }
"Message" { [int]$Type = 5 }
}
# Create a log entry
$Content = "<![LOG[$Message]LOG]!>" +`
"<time=`"$(Get-Date -Format "HH:mm:ss.ffffff")`" " +`
"date=`"$(Get-Date -Format "M-d-yyyy")`" " +`
"component=`"$Component`" " +`
"context=`"$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)`" " +`
"type=`"$Type`" " +`
"thread=`"$([Threading.Thread]::CurrentThread.ManagedThreadId)`" " +`
"file=`"`">"
try {
# Write the line to the log file
Add-Content -Path $Path -Value $Content
}
catch {
Write-Host "User does not have access to write log at path $Path"
Write-Host $Message
}
if ($Type -eq 1) {
Write-Host $Message -ForegroundColor Yellow
}
if ($Type -eq 3) {
Write-Host $Message -ForegroundColor Red
}
if ($Type -eq 5) {
Write-Host $Message
}
}
function quotify($s, $dataType){
$value = $s
if ($value -eq "True")
{
$value = "true"
}
elseif($value -eq "False")
{
$value = "false"
}
$value = $value.Replace("\", "\\") # escape backslash
if ($dataType -eq "string" -or $dataType -eq "" -or $null -eq $dataType)
{
$q = '"' + $value + '"'
}
else
{
$q = $value
}
return $q
}
# Get the Current Directory
$CurrentDirectory = $PSScriptRoot
$LogFilePath = Join-Path $PSScriptRoot "$($MyInvocation.MyCommand.Name)_$(Get-Date -Format ddMMyyyyHHmmss).log"
$Content = "************************************************"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Starting script"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "************************************************"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Running Script Version: " + $ScriptVersion
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
# Get the Current Directory
$Content = "Current Directory is " + $CurrentDirectory
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Tachyon API Base URL: " + $TachyonApiBaseUrl
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = "1E Powershell toolkit module path: " + $Ps1eModulePath
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
if (!(Test-Path $Ps1eModulePath)) {
$Content = "Folder path for 1E PS Module does not exist : $Ps1eModulePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$psModule = Get-ChildItem -Path $Ps1eModulePath -Filter 'ps1etoolkit.psd1'
if(-not ($psModule.Count -eq 1)) {
$Content = "There is no 1E Powershell module found in path : $Ps1eModulePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$PsModuleFullPath = $Ps1eModulePath + "\ps1etoolkit.psd1"
$Content = "Importing Powershell toolkit "
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
import-module $PsModuleFullPath
$Content = "set-1Eserver for base url: " + $TachyonApiBaseUrl
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
set-1Eserver -Server $TachyonApiBaseUrl -AppId $ClientAssertionAppId -CertSubject $CertificateSubject -CertThumbprint $CertificateThumbprint -Principal $Upn
$Content = "Folder Path containing Device list files: " + $FolderPath
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = "Verifying Folder Path : $FolderPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
if (!(Test-Path $FolderPath)) {
$Content = "Folder path does not exist : $FolderPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$Content = "Fetching all supported files from folder"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$files = Get-ChildItem -Path $FolderPath -Filter *.txt
$Content = "Total Files in folder $FolderPath : $($files.Count)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
foreach($file in $files)
{
$fileFullPath = $file.FullName
$Content = "Reading File : $fileFullPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$stopwatchFileRead = [system.diagnostics.stopwatch]::StartNew()
$fqdnList = New-Object System.Collections.Generic.List[System.Object]
foreach($line in [System.IO.File]::ReadLines($fileFullPath))
{
if($line -ne ""){
$fqdnList.Add($line.Trim())
}
}
if($fqdnList.Count -lt 2){
$Content = "No device present in file"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
Continue
}
#File name is Management Group Name
$MGName = $file.BaseName
#First line of the file is the Management Group Description
$MgDescription = $fqdnList[0]
#Removing the first line from collection as it's MG description
$fqdnList.RemoveAt(0)
$quotedMgString = ""
$quotedMgString = '{0}' -f ($fqdnList -join ',')
$stopwatchFileRead.Stop()
$Content = "Time taken in reading file : $($stopwatchFileRead.Elapsed.TotalSeconds) seconds"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Creating Management Group -"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Info
$Content = " Name : $MGName"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = " Description : $MgDescription"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = " Total Devices in file : $($fqdnList.Count)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
Set-1ESLADirectManagementGroup -Name $MGName -Description $MgDescription -FqdnList @($quotedMgString)
}
$Content = "Complete"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type MessageCreate Direct-based management groups from a Configuration Manager collection
The following PowerShell script is used on the Management Groups page.
Note
This script has changed to support an API change in Platform 8.1
Minimum of 1E Powershell Toolkit v1.2.5 installed
Kid to Upn mapping should exist in the 1E Platform.
<#
.SYNOPSIS
1E Platform - Create Direct Management Group
Copyright © 1e Ltd 2023 - all rights reserved
http://www.1e.com
Version 4.0
Disclaimer
Your use of this software is at your sole risk. All software is provided "as-is", without any warranty, whether express or implied, of accuracy, completeness,
fitness for a particular purpose, title or non-infringement, and none of the software is supported or guaranteed by 1E. 1E shall not be liable for any damages
you may sustain by using this software, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such
damages.
If you have an issue using this software, please ensure you are using an unmodified version before contacting 1E Support.
.DESCRIPTION
This script reads the memberships of ConfigMgr Collections (Both Direct and Query based membership) and creates Direct Membership Management
Groups through SLA/Tachyon APIs. If the group does not exist it is created, and devices are added. If the group exists, the existing devices
are deleted and new devices are added. The script is designed to run from the ConfigMgr server It queries WMI and gets the information
necessary to connect to the ConfigMgr's site namespace.
.PARAMETER TachyonApiBaseUrl
Mandatory parameter. The header name used to access the Tachyon web service. Should match the header listed in the IIS bindings on the Tachyon server.
.PARAMETER FilePath
Mandatory parameter. The name of the input file which contains the list of collections to be used in creating Direct Membership Management Groups. Each line in file
contains Collection Name, Management Group Name, and Management Group Description; all three values separated by a comma.
e.g.
Collection 1,Management Group 1, Management Group 1 Description
Collection 2,Management Group 2, Management Group 2 Description
.PARAMETER ClientAssertionAppId
Optional parameter. The AppId of the Client Assertion application created inside the cloud which is used for Authentication. Certificate should be present in same application.
.PARAMETER CertificateSubject
Optional parameter. Subject name of the Certificate used for Authentication.
.PARAMETER Upn
Optional parameter. UPN of the user for which mapping is targeted. Required only for Add and Update operations
.EXAMPLE
.\Create-DirectManagementGroupUsingCmCollection.ps1 -TachyonApiBaseUrl https://tachyon.lab.local -FilePath C:\Temp\Collection.txt -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>"
or
.\Create-DirectManagementGroupUsingCmCollection.ps1 -TachyonApiBaseUrl tachyon.lab.local -FilePath C:\Temp\Collection.txt -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>"
Scenario:
A Tachyon server is accessed using URL https://tachyon.lab.local/Tachyon using default credentials. The file containing the list of collections to process
is in the location C:\Temp\Collection.txt. AppId and CertSubject are passed for authentication without browser. KidUpnMapping should be present in TachyonMaster table. This UPN is used for authentication.
.EXAMPLE
.\Create-DirectManagementGroupUsingCmCollection.ps1 -TachyonApiBaseUrl https://tachyon.lab.local -FilePath C:\Temp\Collection.txt -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>" -Upn "<<TachyonPrinciple>>"
or
.\Create-DirectManagementGroupUsingCmCollection.ps1 -TachyonApiBaseUrl tachyon.lab.local -FilePath C:\Temp\Collection.txt -ClientAssertionAppId <<AppId>> -CertificateSubject "<<CertSubject>>" -Upn "<<TachyonPrinciple>>"
Scenario:
A Tachyon server is accessed using URL https://tachyon.lab.local/Tachyon using default credentials. The file containing the list of collections to process
is in the location C:\Temp\Collection.txt. AppId and CertSubject are passed for authentication without browser. KidUpnMapping should be present in TachyonMaster table. This UPN is used for authentication.
.EXAMPLE
.\Create-DirectManagementGroupUsingCmCollection.ps1 -TachyonApiBaseUrl https://tachyon.lab.local -FilePath C:\Temp\Collection.txt
or
.\Create-DirectManagementGroupUsingCmCollection.ps1 -TachyonApiBaseUrl tachyon.lab.local -FilePath C:\Temp\Collection.txt
Scenario:
A Tachyon server is accessed using URL https://tachyon.lab.local/Tachyon using different credentials other than current default credentials.
The file containing the list of collections to process is in the location C:\Temp\Collection.txt.
#>
param(
[Parameter(Mandatory=$true, HelpMessage="Enter Tachyon API Base URL e.g. : tachyon.testlab.com or https://tachyon.testlab.com")]
[ValidateNotNullOrEmpty()]
[string] $TachyonApiBaseUrl,
[Parameter(Mandatory=$true, HelpMessage="Enter Folder Path which contains files containing Device FQDN list")]
[ValidateNotNullOrEmpty()]
[string] $FilePath,
[Parameter(Mandatory=$true, HelpMessage="Enter 1E Powershell Module Path e.g. : C:\Program Files\WindowsPowerShell\Modules")]
[ValidateNotNullOrEmpty()]
[string] $Ps1eModulePath,
[Parameter(Mandatory=$false, HelpMessage="Enter AppId of Cloud Tenant. If it passed then KidUpnMapping should be present in TachyonMaster table. This UPN is used for authentication. If it is not passed, Authentication will happen through browser")]
[string] $ClientAssertionAppId,
[Parameter(Mandatory=$false, HelpMessage="Enter Certificate Subject. If ClientAssertionAppId is not passed, it is not required")]
[string] $CertificateSubject,
[Parameter(Mandatory=$false, HelpMessage="Enter Certificate Thumbpring. If CertificateThumbprint is not passed, it is not required")]
[string] $CertificateThumbprint,
[Parameter(Mandatory=$false, HelpMessage="Enter Upn which will be used for making API call.")]
[string] $Upn
)
function Connect-ConfigMgrModule{
try {
# Load CM PowerShell Module
if (-Not (Test-Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\ConfigMgr10\AdminUI")) {
$Content = "Configuration Manager not found. Pleaes verify if the machine has Configuration Manager installed."
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$CMConsolePath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\ConfigMgr10\AdminUI" | Select-Object -expandProperty AdminUILog).Replace("\AdminUILog\", "")
if ($CMConsolePath) {
Import-Module -Name "$CMConsolePath\bin\ConfigurationManager.psd1"
$Content = "Loaded ConfigurationManager.psd1 module"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
}
else {
$Content = "ConfigurationManager.psd1 module not found. Pleaes verify if the machine has Configuration Manager installed."
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
# Get the CM Site Code
$CMSiteCode = Get-WMIObject -Namespace root\sms -Class SMS_ProviderLocation | Select-Object -expandproperty SiteCode
if ($CMSiteCode -eq "") {
$Content = "Sitecode could not be determined."
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$Content = "ConfigMgr Site Code: " + $CMSiteCode
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$CMLocation = $CMSiteCode + ":"
$Content = "Setting Location : $CMLocation"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
Push-Location
Set-Location -Path $CMLocation
}
catch {
$err = $_
$Content = "An error occured while loading configuration manager module : $($err.Exception)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
$Content = "Error Details : $($err)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
exit
}
}
function Read-ConfigMgrDeviceCollection{
[CmdletBinding()]
Param(
[parameter(Mandatory=$true)]
[hashtable]$collectionList
)
try {
$stopwatch = [system.diagnostics.stopwatch]::StartNew()
$arrCMCollList = Get-CMDeviceCollection | Select-Object -Property CollectionID,Name
ForEach ($CMCollection IN $arrCMCollList){
$CMCollName = $CMCollection.Name
$Content = "Retrieved Collection Name from ConfigMgr: " + $CMCollName
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$CMCollId = $CMCollection.CollectionID
If ($collectionList.ContainsKey($CMCollName)){
$arrCollectionMembership = Get-CMCollectionMember -CollectionId $CMCollID | Select-Object -expandProperty Name
If ($null -ne $arrCollectionMembership){
$Content = "Fetching Devices from Collection : " + $CMCollName
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$MgName = $collectionList["$CMCollName"][0]
$MgDescription = $collectionList["$CMCollName"][1]
#Create File to save the device list, which later will be used to create MG
$MGFileName = $MgName + ".txt"
$MGFilePath = Join-Path $FolderPath $MGFileName
if (!(Test-Path $MGFilePath)){
$Content = New-Item -itemType File -Path $FolderPath -Name $MGFileName
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Created file $MGFilePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
Add-Content -Path "$MGFilePath" -Value "$MgDescription"
$Content = "Added description $MgDescription"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
}
ForEach ($CollMember IN $arrCollectionMembership){
$deviceDetails = Get-CMDevice -Name $CollMember -Resource | Select-Object Name,FullDomainName
$deviceFullName = $deviceDetails.Name + "." + $deviceDetails.FullDomainName
$Content = "Adding device: " + $deviceFullName + " to MG File: " + $MgName
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
Add-Content -Path "$MGFilePath" -Value "$deviceFullName"
}
}
}
}
$stopwatch.Stop()
$Content = "Time taken in reading devices from Collection : $($stopwatch.Elapsed.TotalSeconds) seconds"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
Pop-Location
$Content = "Update location back to local drive"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "**********************"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
}
catch {
$err = $_
$Content = "An error occured while fetching devices : $($err.Exception)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
$Content = "Error Details : $($err)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
exit
}
}
function Write-Log {
[CmdletBinding()]
Param(
[parameter(Mandatory=$true)]
[String]$Path,
[parameter(Mandatory=$true)]
[String]$Message,
[parameter(Mandatory=$true)]
[String]$Component,
[Parameter(Mandatory=$true)]
[ValidateSet("Debug", "Info", "Message", "Warning", "Error")]
[String]$Type
)
switch ($Type) {
"Info" { [int]$Type = 1 }
"Warning" { [int]$Type = 2 }
"Error" { [int]$Type = 3 }
"Debug" { [int]$Type = 4 }
"Message" { [int]$Type = 5 }
}
# Create a log entry
$Content = "<![LOG[$Message]LOG]!>" +`
"<time=`"$(Get-Date -Format "HH:mm:ss.ffffff")`" " +`
"date=`"$(Get-Date -Format "M-d-yyyy")`" " +`
"component=`"$Component`" " +`
"context=`"$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)`" " +`
"type=`"$Type`" " +`
"thread=`"$([Threading.Thread]::CurrentThread.ManagedThreadId)`" " +`
"file=`"`">"
try {
# Write the line to the log file
Add-Content -Path $Path -Value $Content
}
catch {
Write-Host "User does not have access to write log at path $Path"
Write-Host $Message
}
if ($Type -eq 1) {
Write-Host $Message -ForegroundColor Yellow
}
if ($Type -eq 3) {
Write-Host $Message -ForegroundColor Red
}
if ($Type -eq 5) {
Write-Host $Message
}
}
function quotify($s, $dataType)
{
$value = $s
if ($value -eq "True")
{
$value = "true"
}
else
{
if ($value -eq "False")
{
$value = "false"
}
}
$value = $value.Replace("\", "\\") # escape backslash
if ($dataType -eq "string" -or $dataType -eq "" -or $null -eq $dataType)
{
$q = '"' + $value + '"'
}
else
{
$q = $value
}
return $q
}
# Get the Current Directory
$CurrentDirectory = $PSScriptRoot
#Temp Folder to contain list of devices which will be used to create MG
$MGFolderName = "MGDeviceList"
$FolderPath = Join-Path $CurrentDirectory $MGFolderName
$LogFilePath = Join-Path $CurrentDirectory "$($MyInvocation.MyCommand.Name)_$(Get-Date -Format ddMMyyyyHHmmss).log"
$Content = "************************************************"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Starting Script"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "************************************************"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Running Script Version: " + $ScriptVersion
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Current Directory is: " + $CurrentDirectory
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Tachyon API Base URL: " + $TachyonApiBaseUrl
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = "1E Powershell toolkit module path: " + $Ps1eModulePath
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = "1E Powershell toolkit module name: " + $Ps1eModuleName
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
if (!(Test-Path $Ps1eModulePath)) {
$Content = "Folder path for 1E PS Module does not exist : $Ps1eModulePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$psModule = Get-ChildItem -Path $Ps1eModulePath -Filter 'ps1etoolkit.psd1'
if(-not ($psModule.Count -eq 1)) {
$Content = "There is no 1E Powershell module found in path : $Ps1eModulePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$PsModuleFullPath = $Ps1eModulePath + "\ps1etoolkit.psd1"
$Content = "Importing Powershell toolkit "
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
import-module $PsModuleFullPath
$Content = "set-1Eserver for base url: " + $TachyonApiBaseUrl
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
set-1Eserver -Server $TachyonApiBaseUrl -AppId $ClientAssertionAppId -CertSubject $CertificateSubject -CertThumbprint $CertificateThumbprint -Principal $Upn
$Content = "File Path containing CM Collection list: " + $FilePath
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = "Temp Folder Path to save Device List: " + $FolderPath
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = "Verifying File Path : $FilePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
if (!(Test-Path $FilePath)) {
$Content = "File path does not exist : $FilePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
try {
if (!(Test-Path $FolderPath)) {
$Content = "Creating temp folder : $FolderPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = New-Item -Path "$CurrentDirectory" -Name $MGFolderName -ItemType "directory"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
}
else{
$Content = "Deleting existing MG files from $FolderPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
# Delete Collection Membership List Files
Remove-Item -Path "$FolderPath\*.*"
}
}
catch {
$Content = "User does not have permission to create : $FolderPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
# Read Collection Details from File
$Content = "Reading collection details from $FilePath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$collectionList = @{}
foreach($line in [System.IO.File]::ReadLines($FilePath))
{
if($line -ne ""){
$collectionDetails = $line.Split(",")
if($collectionDetails.Count -lt 3){
$Content = "Input data not in correct format. It should contain <CollectionName>,<MGName>,<MGDescription>"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$collectionList.Add($collectionDetails[0],@($collectionDetails[1],$collectionDetails[2]))
}
}
if($collectionList.Count -lt 1){
$Content = "No collection details present in file"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
#Loading Config Manager Powershell Modules
Connect-ConfigMgrModule
#Read Device Collections from Config Manager and prepare device list in temp folder which will be used to create
Read-ConfigMgrDeviceCollection -collectionList $collectionList
$Content = "Verifying Folder Path : $FolderPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
if (!(Test-Path $FolderPath)) {
$Content = "Folder path does not exist : $FolderPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
exit
}
$Content = "Fetching all files from folder"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$files = Get-ChildItem -Path $FolderPath -Filter *.txt
$Content = "Total Files in folder $FolderPath : $($files.Count)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
foreach($file in $files)
{
$fileFullPath = $file.FullName
$Content = "Reading File : $fileFullPath"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$stopwatchFileRead = [system.diagnostics.stopwatch]::StartNew()
$fqdnList = New-Object System.Collections.Generic.List[System.Object]
foreach($line in [System.IO.File]::ReadLines($fileFullPath))
{
if($line -ne ""){
$fqdnList.Add($line)
}
}
if($fqdnList.Count -lt 2){
$Content = "No device present in file"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Error
Continue
}
#File name is Management Group Name
$MGName = $file.BaseName
#First line of the file is the Management Group Description
$MgDescription = $fqdnList[0]
#Removing the first line from collection as it's MG description
$fqdnList.RemoveAt(0)
$quotedMgString = ""
$quotedMgString = '{0}' -f ($fqdnList -join ',')
$stopwatchFileRead.Stop()
$Content = "Time taken in reading file : $($stopwatchFileRead.Elapsed.TotalSeconds) seconds"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Debug
$Content = "Creating Management Group -"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Info
$Content = " Name : $MGName"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = " Description : $MgDescription"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
$Content = " Total Devices in file : $($fqdnList.Count)"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message
Set-1ESLADirectManagementGroup -Name $MGName -Description $MgDescription -FqdnList @($quotedMgString)
}
$Content = "Complete"
Write-Log -Path $LogFilePath -Message $Content -Component $MyInvocation.MyCommand.Name -Type Message