Powershell: Prevent Users To View and Change Input or Config Files That Are Used by a Script
Often, I use .xml or .ini files to store settings that a PowerShell script uses. When I distribute my scripts to end users, sometimes, I want to make sure users cannot manually view or change the content of these config files.
Below is what I did to achieve the goal:
- Create a password protected zip file that contains the config file (.xml or .ini).
- rename the zip file from xxxxxx.zip to xxxxxx.bin
- In powershell script, use ICSharpCode.SharpZipLib.dll to unzip renamed zip file
- compile powershell script to exe so users cannot view the script to figure out the zip file password.
- read the content of the extracted config file
- delete extracted config file
To compile the powershell script, I can use one of these tools:
Below is a sample Powershell script (Zip-Test.PS1) I have written to read a xml file inside a renamed zip file:
param ([string]$FilePath)
$ziplib = Join-Path $FilePath "ICSharpCode.SharpZipLib.dll"
[System.Reflection.Assembly]::LoadFrom("$ziplib") | Out-Null
$ZipName = "Health-Check.bin"
$XmlName = "Health-Check.xml"
$xmlPath = Join-Path $FilePath $XmlName
$ZipPath = Join-Path $FilePath $ZipName
$objZip = New-Object ICSharpCode.SharpZipLib.Zip.FastZip
$objZip.Password = "password"
$objzip.ExtractZip($ZipPath, $FilePath, $XmlName)
if ((Test-Path $xmlPath))
{
$xml = (get-content $xmlPath)
Remove-Item $xmlPath -Force
}
$xml.configuration
The script extracts and reads the health-check.xml file and deletes health-check.xml straightaway, it happens so fast, it won’t be possible for end users to access the file. Below is the output from above sample code (content of my XML file):
One thing to keep in mind: in most of my scripts, I use
$thisScript = Split-Path $myInvocation.MyCommand.Path -Leaf $scriptRoot = Split-Path (Resolve-Path $myInvocation.MyCommand.Path)
To determine the script name and location. $MyInvocation does not work anymore after I converted the Powershell script to EXE. Therefore, from my above example, I’m actually passing the directory location into the script as a parameter.
Using SCOM PowerShell Snap-in and SDK client with a PowerShell Remote Session
Recently, I’ve been working on a utility based on PowerShell scripts using WinForms GUI to perform some SCOM tasks (i.e. create maintenance window, approve manually installed agents, adding network devices, etc.). Since this script is going to be widely used in the organisation when it’s completed, I’ve always kept in mind that when users run this utility, the utility should only connect to SCOM SDK service when required and disconnect as soon as the task is done. In another word, I don’t want this utility to remain connected to the SDK service because Microsoft recommends the concurrent connections should not exceed 50 per management group.
So I did some testing to make sure my scripts disconnects from the RMS SDK service. I opened perfmon on RMS watching the “Client Connections” counter under OpsMgr SDK Service:
and want to make sure the performance counter drops when the script is supposed to disconnect from SCOM management group. In my script, I use both the SCOM PowerShell Snap-in and the SCOM SDK, below is what the code looks like:
SCOM PowerShell Snap-in:
Connect to management group:
$RMS = "<RMS Server Name>" Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client New-PSDrive -Name:Monitoring -PSProvider:OperationsManagerMonitoring -Root:\ Set-Location "OperationsManagerMonitoring::" new-managementGroupConnection -ConnectionString:$RMS | Out-Null Set-Location $RMS
Disconnect from management group:
$CurrentMG = get-managementGroupConnection
if ($CurrentMG -ne $null)
{
$CurrentMG | Remove-ManagementGroupConnection | Out-Null
}
SCOM SDK:
Firstly, Load Assembly:
[System.Reflection.Assembly]::LoadFrom("$sdkDir\Microsoft.EnterpriseManagement.OperationsManager.Common.dll") | Out-Null
[System.Reflection.Assembly]::LoadFrom("$sdkDir\Microsoft.EnterpriseManagement.OperationsManager.dll") | Out-Null
Connect to management group:
$UserName = "<user name>" $UserDomain = "<user domain>" $password = "<password>" $securePassword = ConvertTo-SecureString $password –AsPlainText -Force $MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings($RootMS) $MGConnSetting.UserName = $UserName $MGConnSetting.Domain = $UserDomain $MGConnSetting.Password = $SecurePassword $ManagementGroup = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)
Disconnect from management group:
I couldn’t find a “disconnect” method for the Microsoft.EnterpriseManagement.ManagementGroup object. So I tried to simply remove the variable:
Remove-Variable ManagementGroup
I couldn’t unload the SDK DLLs as I read it’s a limitation in .NET, the only way to unload a loaded DLL is to close the app.
Test Results:
Regardless which way I use to connect to SCOM (PowerShell Snap-in or SDK), the perf counter does not drop when I tried to disconnect using methods above. In fact, I could only get the counter drop when I close the Powershell console (or exit my GUI app which is just a pure Powershell script).
As shown above, notice that as soon as I exit PowerShell, the counter has dropped by 1.
Therefore, I thought I had 2 options to work around this issue.
1. Getting the script to launch another powershell.exe instance when trying to connect to SCOM every time but by doing so, I can’t really pass data / variable back to my script.
2. Use PowerShell Remoting to create a PS Session on local computer, run whatever needs to run against SCOM and remove the PS Session when it’s done. By doing so, I can still pass variables back to my script.
So I’ve decided to go with PowerShell Remoting. I’ve used “Enable-PSremoting –force” cmdlet to enable PS Remoting with all default settings.
I’ll use a simple get-agent cmdlet via PS Remoting as example, I’ve written something like this:
$RMS = "<RMS Server Name>"
$AgentName = "<Agent Computer Name>"
$NewSession = new-pssession
$agent = invoke-command -session $NewSession -ScriptBlock {
param($RMS,$AgentName)
Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client
New-PSDrive -Name:Monitoring -PSProvider:OperationsManagerMonitoring -Root:\
Set-Location "OperationsManagerMonitoring::"
new-managementGroupConnection -ConnectionString:$RMS | Out-Null
Set-Location $RMS
$Agent = Get-Agent | Where-Object {$_.PrincipalName -imatch $AgentName}
$Agent
} -ArgumentList $RMS, $AgentName
Remove-PSSession $NewSession
I ran above code using an account that is a domain admin in my test environment and it’s also a SCOM administrator in my management group. But somehow I get this error:
The user does not have sufficient permission to perform the operation.
After some research, I realised that I have to use the CredSSP (Credential Security Support Provider) authentication to pass my credential from the local Powershell session to the PS Remoting session (in this case, also on my local machine). So I modified my script to use Credssp when creating the new PS Session:
$me = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name $NewSession = new-pssession -ComputerName $env:COMPUTERNAME -Authentication Credssp -Credential (Get-Credential $me)
It turned out, after the modification, the code still would not work:
I then found that I will also have to configure the remote session to pass my credential to the remote server again – in this case, the SDK service in SCOM RMS (second hop). so my credential will be passed from Local PowerShell session –> PS remote session on the local computer –> SCOM RMS SDK Service.
In addition to “Enable-PSRemoting –force”, I had perform the following to make it work:
1. Enable WinRM CredSSP to allow the second hop:
Via PowerShell:
- Set-Item WSMAN:\localhost\client\auth\credssp –value $true
- Set-Item WSMAN:\localhost\service\auth\credssp –value $true
Or Via Group Policy:
- Computer Configuration\Administrative Templates\Windows Components\Windows Remote Management (WinRM)\WinRM Client\Allow CredSSP authentication – Set to “Enabled”
- Computer Configuration\Administrative Templates\Windows Components\Windows Remote Management (WinRM)\WinRM Service\Allow CredSSP authentication – Set to “Enabled”
2. Configure Credentials Delagations
in Group Policy (either domain GPO or local policy), under
Computer Configuration\Administrative Templtes\System\Credential Delegation\Allow Delegating Fresh Credentials
- Set to Enabled
- Add “WSMAN/<local computer name>” to the server list
Now, after I updated the group policy (gpupdate /force), the code should just work. As shown below, I have retrieved the agent information using SCOM Powershell Snap-in via a PS remote session.
And now if I take a look at the OpsMgr SDK Service “Client Connections” perf counter:
My script has connected to the SDK service for few seconds then disconnected!
Conclusion:
My code ended up like this:
$me = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$RMS = "<RMS Server Name>"
$AgentName = "<Agent Computer Name>"
$NewSession = new-pssession -ComputerName $env:COMPUTERNAME -Authentication Credssp -Credential (Get-Credential $me)
$agent = invoke-command -session $NewSession -ScriptBlock {
param($RMS,$AgentName)
Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client
New-PSDrive -Name:Monitoring -PSProvider:OperationsManagerMonitoring -Root:\
Set-Location "OperationsManagerMonitoring::"
new-managementGroupConnection -ConnectionString:$RMS | Out-Null
Set-Location $RMS
$Agent = Get-Agent | Where-Object {$_.PrincipalName -imatch $AgentName}
$Agent
} -ArgumentList $RMS, $AgentName
Remove-PSSession $NewSession
I could not use “localhost” as computer name when creating new PS session (and adding “WSMAN/localhost” in “Allow Delegating Fresh Credentials policy”. It doesn’t work.
More Reading:
On OpsMgr SDK service client connections counter:
http://thoughtsonopsmgr.blogspot.com.au/2010/12/how-to-get-alert-when-too-many-scom.html
On CredSSP , PS Remoting and SCOM PowerShell Cmdlets:
Additionally, I ran into this free ebook about a week ago, Even though I’m still reading it, it’s a pretty good book: Secrets of PowerShell Remoting.
My Observation on SCCM Clients BITS Settings
Yesterday, while we were reviewing the SCCM (2007 R3) client BITS settings at work, we (my team) have some interesting findings with SCCM client’s BITS settings.
We found when the BITS bandwidth throttling settings are configured for a SCCM primary site. SCCM clients get the policy and write the settings into Windows local policy:
SCCM Computer Client Agent BITS Settings:
BITS Settings from SCCM Client’s Windows local policy (Local Policy –>Computer Configuration –>Administrative Templates –>Network –>Background Intelligent Transfer Service (BITS) –>Limit the maximum network bandwidth for BITS background transfers):
As you can see, the SCCM site setting is identical to SCCM client’s local policy. SCCM 2007 Unleashed has explained the client BITS settings. You can read about it on Google Books HERE.
The book did not state and explain the SCCM client actually WRITES the SCCM site’s BITS policy into SCCM client’s Windows local group policy object (GPO). So I did below tests IN ORDER in my home SCCM 2007 R3 AND SCCM 2012 RTM test environments to work out the behaviours of SCCM client and compare SCCM Client’s BITS setting against the above mentioned setting in local policy:
1. SCCM Client BITS setting left as default in SCCM (Not configured).
- SCCM 2007 Client Computers: BITS policy in local GPO is set to DISABLED!
- SCCM 2012 Client Computers: Same as SCCM 2007 client computers
2. Enable BITS in SCCM Computer Client Agent setting (In 2007, apply to both clients and BDPs, in 2012, just enable it since there is no BDPs in 2012 anymore.), and define some throttling settings. Then trigger machine policy retrieval on SCCM client computers.
- SCCM 2007 Client Computers: BITS policy in local GPO is ENABLED in throttling settings are set to as same as SCCM policy.
- SCCM 2012 Client Computers: Same as SCCM 2007 client computers
3. Change BITS throttling settings in SCCM. Then trigger machine policy retrieval on SCCM client computers
- SCCM 2007 Client Computers: BITS policy in local GPO updated accordingly.
- SCCM 2012 Client Computers: Same as SCCM 2007 client computers
4. Change BITS throttling settings in SCCM client’s Windows local policy. Then trigger machine policy retrieval on SCCM client computers.
- SCCM 2007 Client Computers: local policy remained the same after machine policy retrieval.
- SCCM 2012 Client Computers: Same as SCCM 2007 client computers
5. Change BITS throttling settings in SCCM again. Then trigger machine policy retrieval on SCCM client computers.
- SCCM 2007 Client Computers: local policy was updated again according to SCCM client’s BITS policy.
- SCCM 2012 Client Computers: Same as SCCM 2007 client computers
Conclusions:
Based on the tests I have performed. I have come to below conclusions:
- When the SCCM client’s BITS policy is not configured, the BITS throttling settings OS local policy is set to DISABLED, so effectively no BITS throttling is allowed for ALL the apps that uses BITS on the SCCM client computer. (i.e. in our case, VMM agent)
- Upon SCCM policy change, SCCM client changes local policy with updated settings once it has retrieved the updated policy via SCCM client’s machine policy retrieval (by default runs every 60 minutes).
- The SCCM client’s BITS settings are NOT enforced in local policy. i.e. when local policy is manually updated to be different than SCCM client’s policy, SCCM client does not enforce and update local policy. SCCM clients ONLY write to local policy when the SCCM BITS policy is CHANGED on the primary site.
- SCCM 2007 clients and SCCM 2012 clients exhibit same behaviour.
So, please look out if you have other apps that uses BITS and the bandwidth is throttled. SCCM client would update the local policy without you knowing it.
Alternatively, using a domain GPO to set BITS throttling settings seems like a good idea. By doing so, you can target different SCCM clients more granularly (targeting different OUs, using WMI filters and AD groups to set GPO scopes) whereas in SCCM 2007, this setting is unique across all clients in the primary site. Additionally, domain GPO will override local policy so local policy can be ignored.
Changing Display Language on Windows 7 Home and Professional Editions
I bought a laptop for other family members yesterday, it comes with Windows 7 Home Premium. I needed to change the display language from English to Chinese because the main user of this laptop does not speak English.
I thought it was a no brainer as I’ve done it before, all I had to do was to load another language pack in “Regional and Language” in Control Panel. However, I was wrong. apparently this function is available in Windows 7 Ultimate and Enterprise editions.
I didn’t really want to use Windows Anytime Upgrade to upgrade it to Ultimate just so I can change the language. Lucky I found this post: http://mark.ossdl.de/2009/08/change-mui-language-pack-in-windows-7-home-and-professional/
So below is what I’ve done:
- Download Windows 7 Service Pack 1 language pack (Because the laptop comes with Windows 7 SP1, I had RTM version of the language pack but it didn’t work.) – I downloaded the entire ISO from my TechNet subscription, but there are many blog posts around with the direct link to Windows Update for each individual language (such as this one: http://www.technize.net/windows-7-sp1-language-packs-direct-download-links-kb2483139/)
- Extracted the downloaded ISO (from TechNet subscription) to C:\Apps\langpacks
- in Command prompt:
- dism /online /add-package /packagepath:C:\Apps\langpacks\zh-cn\lp.cab
- bcdedit /set {current} locale zh-cn
- bcdboot %WinDir% /l zh-cn
- Backed up and deleted HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\MUI\UILanguages\en-US
- Reboot
Note: if there were any windows updates that were pending to be installed, the install may fail after the language was changed. I had to run wuauclt /detectnow so Windows Update agent detects the updates for different language.
SCCM 2012 Log Parser: cmtrace.exe
In my opinion, THE most used utility (other than SCCM console) for any SCCM administrators / engineers would have to be trace32.exe. Back in SMS and SCCM 2007 days, trace32.exe comes with the SCCM Toolkit, which contains a bunch of other tools.
Speaking of my own experience, out of all the tools provided by the toolkit, trace32.exe is the one I used the most.
Now with SCCM 2012, trace32.exe has been replaced by a new tool called cmtrace.exe.
Unlike trace32.exe, cmtrace.exe is actually built-in in SCCM, there is no need to download separate toolkits for it. cmtrace.32 can be found on the SCCM site server, under “<SCCM Install Dir>\tools\” folder. Same as it’s predecessor trace32.exe, cmtrace.exe can be copied / redistributed to other locations / computers alone and use as a log parser.
I have also found that trace32.exe actually does not correct parse SCCM 2012 logs. For example, I’m using both trace32.exe and cmtrace.exe to open execmgr.log from a SCCM 2012 client:
trace32.exe:
cmtrace.exe:
So, if you are working with SCCM 2012, make sure you use cmtrace.exe rather than the good old trace32.exe. And maybe like me, copy cmtrace32.exe to your local machine and use it from there rather than using it on the server.
Installing SCCM 2012 RTM Secondary Site using A Pre-Installed SQL Express 2008 R2 Instance
Since System Center 2012 was RTM’d few days ago, I have started updating / migrating my home environment. After I migrated my 2 Hyper-V servers from VMM 2008 R2 to VMM 2012, I have started building a brand new SCCM 2012 environment so I can migrate SCCM 2007 to it. My plan is to install a Central Admin site, a child primary site and a Secondary site so I have a simple 3-tier hierarchy like my existing 2007 and 2012 Beta 2 environments.
The Central Admin site and the child primary site installation all went pretty smoothly. But I had some issues when installing the secondary site.
When installing Secondary Site from it’s parent primary, There are two options available for the database:
- Install and Configure a local copy of SQL Server Express on the secondary site computer
- Use an existing SQL Server instance.
I wanted to install SQL Express myself so I can control where it’s installed to and locations for data, log and backup files. – This is pretty common and most of SQL DBAs would configure to install SQL on a volume other than C:\ and place data / logs / backups on dedicated and separate disks. By using SCCM to install SQL express for you, you don’t get to choose any of this, which can be pretty annoying.
According to Supported Configurations for Configuration Manager, secondary sites supports SQL Server Express 2008 R2 with SP1 and Cumulative Update 4. So I downloaded SQL Server 2008 R2 Express With SP1 with Tools (SQLEXPRWT_x64_ENU.exe) and SQL 2008 R2 Service Pack 1 Cumulative Update 4 and installed them in order on my secondary site site server.
Below is what I have customised during the SQL express install:
- I configured the location for SQL, SQL instance, data files, log files and backup files the way I wanted it.
- I selected the SQL instance to use the collation “SQL_Latin1_General_CP1_CI_AS” because it is the only collation that SCCM supports.
- I kept the default secondary site SQL instance name “CONFIGMGRSEC” (this name is what’s used if you choose SCCM to install SQL Express for you).
- I have given a pre-configured AD group called “ConfigMgr2012 Servers” which contains all SCCM 2012 site servers sysadmin rights in SQL Express.
After the install, I applied CU4 and all went pretty smoothly.
Now, I tried to push Secondary Site install from the primary site. Under SQL Server setting step, I selected “Use an existing SQL Server instance” option and enter the secondary site server’s FQDN under “SQL server fully qualified domain name” and “CONFIGMGRSEC” under “SQL server instance name, if applicable”. After finishing the wizard, the secondary site install failed during prerequisite checks. I got few errors in regards to the SQL collation is not set to SQL_Latin1_General_CP1_CI-AS:
This is very strange because all my SQL instances in this hierarchy are set to this collation, and because of this, the setup did not even get kicked off.
Additionally, I also found the following:
- On the primary site server, in the ConfigMgrSetup.log under System root, I get the following errors:
- CSql Error: Cannot find type data, cannot get a connection.
- *** [08001][17][Microsoft][ODBC SQL Server Driver][DBNETLIB]SQL Server does not exist or access denied.
- I could use the SQL management studio from Secondary site server to connect to the SQL express instance, but I couldn’t use the SQL management studio from a remote machine to connect to it:
After spending some time troubleshooting, I got it going. Below is what I have done on the SQL Express instance:
1. I’ve assign “ConfigMgr2012 Servers” group (which I created myself and it contains the primary site server’s computer account) “dbcreator” role on top of sysadmin role it already had.
2. I realised by default, after I installed SQL express, TCP/IP protocol is disabled. So I went to SQL Server Configuration Manager, under SQL Server Network Connection —> Protocols for CONFIGMGRSEC—>TCP/IP, enabled it. I also had to configure the ports for this connection:
I removed 0 from “TCP Dynamic Ports” for each IP and added static port 1433 under “TCP Port”
After you enabled TCP/IP and changed the port, you will be prompted that you have to restart SQL server service for the change to take effect, so I restarted the SQL service.
After these steps, the prerequisite checks were passed and the Secondary site installation finished successfully.
In summary below are the steps I took to pre-configure a SQL Express instance for SCCM 2012 secondary site:
- Install SQL Express 2008 R2 with SP1 with Tools
- Configure SQL express install directory as per my standard (not on C:\ drive)
- Configure SQL Express instance name as “CONFIGMGRSEC” as it is default to SCCM secondary site and there’s no reason to change it.
- Select “SQL_Latin1_General_CP1_CI_AS” as SQL server collation.
- Configure data/logs/backups directory
- add primary site server’s computer account (or a group containing primary site server’s computer account) as administrator during install
- Apply SQL Server 2008 R2 Service Pack 1 Cumulative Update 4 after SQL Express install
- Set a limit for amount of memory SQL express can use.
- Reboot secondary site server (just to be safe)
- give the parent primary site server’s computer account dbcreator access in SQL Express instance.
- Enable TCP/IP for the SQL express instance.
- Configure TCP/IP connection port settings.
- Restart SQL service.
- Initiate Secondary Site install from Primary site (via SCCM console). – Unlike SCCM 2007, secondary site install can no longer be performed by running SCCM setup from secondary site servers.
- During setup wizard, choose “Use an existing SQL Server instance”, enter secondary site server’s FQDN and SQL instance name (“CONFIGMGRSEC”). leave site database name and SQL broker port as default.
- monitor install status using the SCCM console:
You can also check:
- C:\ConfigMgrSetup.log on Primary Site server (contains details for Secondary Site install’s prerequisite checks).
- C:\ConfigMgrSetup.log on Secondary Site server (contains details for the actual setup).
Now, instead of having SQL Express installed and configured by SCCM, I have more control of it so I can align the configuration with my organisation’s standard (if it’s in a real production environment
).
In this case, I have my SQL data file located under F:\SQL_Data\Microsoft SQL Server\MSSQL10_50.CONFIGMGRSEC\MSSQL\DATA:
And log files under G:\SQL_Logs\Microsoft SQL Server\MSSQL10_50.CONFIGMGRSEC\MSSQL\Data:
Reports not updated in SCOM SQL Reporting Service When the Management Pack was Updated
I ran into an issue today. I have updated a report in a management pack. After I updated the version number, sealed it and imported the updated management pack into SCOM, the report that I have modified did not get updated in SQL Reporting Service (SRS).
Generally, once a new MP is imported into a management group, within few minutes, the reports within the MP should be deployed to SRS. This was the case when I updated the very same MP in Development environment, but in Production, I waited few hours and nothing has happened.
After few hours, I finally fix the issue.
For any reports that have been deployed as part of a MP, there should be a .mp file in the SRS folder, like this one:
In the production environment, the .mp file name from my management pack folder in SRS is different than the one in development environment. I checked other management packs in both Prod and Dev and they all have the same .mp file name.
To fix the issue: I deleted the .mp file from SRS, restarted the SRS service. Within one minute, the updated report got deployed to SCOM SQL Reporting Service and the .mp file got recreated as well.
This Blog Has Been Hacked! But Should Be OK Now…
Over the last few days, it seems my blog has been hacked. some suspicious malware codes have been injected into the WordPress PHP pages.
I have just reinstalled WordPress, changed all the passwords and ran another scan. it came out clean. I have manually checked infected pages, the suspicious codes have bee removed.
If you are using Google Chrome and saw the warning page when trying to access my blog:
I have requested Google to review my site again. Hopefully I’ll get my site removed from the blacklist within few days.
