Tag Archives: MimbolovePowershell

My First Impression on PowerShell Web Access

Written by Tao Yang
PSWA

I ran up an instance of Windows Server 2012 in my test lab last night so I can play with various new features such as IPAM and PowerShell Web Access, etc.

Today I configured this box as the PowerShell Web Access (PSWA) gateway. I have to say, I am very very impressed! The implementation is easy, took me less than an hour (including time spent reading TechNet articles) and having ability to access PowerShell console on virtually any web browser for all Windows machines in my lab is just fantastic!

Now I can probably get away from using RDP most of the times since I’m pretty comfortable with PowerShell Smile

So, here are the steps I took to setup PSWA:

1. Add the PSWA feature in Server Manager

2. Install PSWA web application using PowerShell:

Install-PswaWebApplication

3. Requested and installed a SSL certificate for the PSWA gateway machine from my Enterprise CA

4. In IIS, configured HTTPS for the default web site and used the SSL certificate I just installed from previous step.

5. Created an AD group called PSWA_Users and added few user IDs into this group.

6. Create PSWA Authorization Rule:

Add-PSWAAuthorizationRule -UserGroupName Corp\PSWA_Users -Computername * -ConfigurationName *

image

7. Since I can’t guarantee that WinRM has been enabled and configured on every machine, I’ve created a GPO to enable WinRM and linked it to the domain root.

Now, PSWA is pretty much ready to go. I launch the web access console on Google Chrome and entered my credential and the computer that I wish to connect to:

image

And I’m in!

image

It’s great to see that Microsoft releases a web-based product that runs on browsers other than IE. I don’t think I’ve seen anything like this before!

Additional Configurations:

I started testing by connecting to a SCOM management server and tried to retrieve all SCOM agents in my management group (Only 11 in total so I’d assume not huge amount of data is returned). I used:

Import-Module OperationsManager

$a = Get-SCOMAgent

Interestingly, it failed and the connection to the management server was closed:

image

Error:

Processing data for a remote command failed with the following error message: The WSMan provider host process did not return a proper response. A provider in the host process may have behaved improperly.

This reminded me the default setting for “Maximum amount of memory in MB per Shell” for WinRM, which I blogged previously in this post. The default setting on Windows Server 2008 R2 and Windows 7 is 150MB. This default setting has increased to 1024MB on Windows Server 2012 and Windows 8.

So to test, since I have 3 management servers in the OM12 management group, I’ve increased this setting to 1024 on another management server. It fixed the error:

image

To further prove this error is actually caused by not having enough memory for the remote shell, I’ve connected PSWA to a Windows 8 machine, which has OM12 console and command shell installed. I used the following commands to connect to the OM12 management group:

Import-Module OperationsManager

New-SCManagementGroupConnection OpsMgrMS03

It prompted an error saying I don’t have sufficient permission:

image

This is by design, when using second hop in CredSSP, the credential has to be explicitly specified. so I changed the command to:

New-SCManagementGroupConnection OpsMgrMS03 –Credential (Get-Credential domain\MyID)

after entering the password, I was successfully connected and I managed to retrieve all SCOM agents by using Get-SCOMAgent Cmdlet without issues.

image

So to fix this issue once for all, I’ve modified the GPO I’ve just created and changed the “Maximum amount of memory in MB per Shell” setting to 1024.

Click here to see settings defined in my WinRM GPO.

I also configured another port forwarding rule on my ADSL router to forward port 443 to the PSWA gateway computer so I can connect when I’m not home.

PSWA on Mobile Devices:

I am able to launch and use PSWA on both my Android tablet (Samsung Galaxy Tab 10.1v running ICS) and my wife’s iPad 3 (running iOS 6) using both built-in browsers and Google Chrome on both devices.

Below are few screenshots from my Galaxy Tab:

image

image

Maybe it’s just me being an Apple noob, when I’m on the iPad, I could not find the Tab key on the keyboard, so I couldn’t use the PowerShell auto completion feature. – One more reason that I’m staying away from that product!

Console Size:

by default, the console size is 120×35, which seems like a waste of space when I’m on a big screen.

So I wrote a simple PowerShell script called Resize-Console.ps1 to resize the window:

$bufferSize = $Host.UI.RawUI.BufferSize
$buffersize.Width = 180
$host.UI.RawUI.BufferSize = $buffersize

$WindowSize = $host.UI.RawUI.WindowSize
$WindowSize.Width = 180
$WindowSize.Height = 40
$host.UI.RawUI.WindowSize = $WindowSize

After I ran this script, the console fits perfectly on my Galaxy tab (resolution 1280×800):

image

This console size also works great on my laptop, which has the resolution of 1366×768. For different resolutions, the width and height need to be adjusted in the script. the only catch is the buffersize cannot be less than the window size (I set the width for both sizes to be the same).

I haven’t managed to work out a automated way to resize the console as when in a PS remote session, there is no $profile so I can’t add scripts into $profile like we normally do on a local console. If I find a way in the future, I’ll post it here.

This is what I found so far. I’ll continue to blog on this topic if I find any other interesting stuff!

By the way, I followed this TechNet article to configure the PSWA: Deploy Windows PowerShell Web Access

PowerShell Script To Enable SCOM Agent Proxy in a More Efficient Way

Written by Tao Yang

if you search on how to enable SCOM agent proxy for all your SCOM agents using PowerShell, you’ll get lots of posts and scripts that shows you how to do it in SCOM 2007 or 2012. In fact, I have written few back in the days.

However, no matter if the script uses SCOM 2007 PowerShell Snap-in, or SCOM 2012 PowerShell module, or even SCOM SDK, there is one limitation: the “ProxyingEnabled” property of the agent class is not one of the search criteria that you can use when retrieving the agent:

image

If you use the SCOM SDK, there are only 4 property names that can be used in the search criteria expression:

  • Id
  • Name
  • LastModified
  • DisplayName

ref: http://msdn.microsoft.com/en-us/library/microsoft.enterprisemanagement.administration.agentmanagedcomputercriteria.aspx

So in order to retrieve the agents that are not ProxyingEnabled, we can only client-side filtering, which retrieves ALL agents in the management group and then filter-out the ones that ProxyingEnabled is set to False.

i.e.

Using SCOM 2007 PowerShell Snap-in:

Get-agent | where-object {$_.ProxyingEnabled -match "false"}| foreach {$_.ProxyingEnabled = $true; $_.applyChanges()}

Using SCOM 2012 PowerShell Module:

Get-SCOMAgent | where-object {$_.ProxyingEnabled –match “false”} | Enable-SCOMAgentProxy

Using SCOM SDK in PowerShell:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager.Common") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager") | Out-Null

$RMS = “RMS SERVER NAME”

$MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings(RMS )
$MG = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)

#Get MG Admin
$Admin = $MG.GetAdministration()

#Search agents
$Agents = $Admin.GetAllAgentManagedComputers()
Foreach ($Agent in $Agents)
{
If (!($Agent.ProxyingEnabled.Value))
{
Write-Host "Enabling Agent Proxy for $($Agent.Name)`..."
$Agent.ProxyingEnabled = $true
$Agent.ApplyChanges()

}
}

Imagine in a large management group with few thousands agents or more and there are only couple of agents that don’t have Agent Proxy enabled. the script / cmdlet will take a long time and a lot of system resources to run because it needs to retrieve information of ALL agents first!

So I wrote a PowerShell script to perform this task a bit differently:

  1. Firstly run a SQL query against SCOM operational database to retrieve a list of agents that do not have agent proxy enabled
  2. connect to SCOM SDK and for each agent retrieved from the database, turn on agent proxy.

This is much more efficient as it only retrieves agents that do not have agent proxy enabled, not the whole lot!

The script uses SCOM SDK, it works on both 2007 and 2012 environments.

Syntax:

To run it on a SCOM management server, no other parameters need to be specified.

image

To run it on a SCOM agent, you will need to specify a management server in the management group that you wish to connect to (does not have to be RMS or RMSE)

image

Additionally, the default SQL query timeout is set to 120 seconds, you can specify a different value by using the –SQLQueryTimeout parameter

image

DOWNLOAD Enable-AgentProxy.ps1

By the way, I also tried to run below SQL command to directly change the ProxyingEnabled attribute in the database (similar to Kevin Holman’s query to change all agents to remote manageable):

Update MT_HealthService Set ProxyingEnabled = 1 where ProxyingEnabled = 0

After I ran this SQL command, the agent proxy setting did get updated in the SCOM console, but I’m not sure if this is supported or not, thus I wrote this script instead.

PowerShell Function: Get-WeekDayInMonth

Written by Tao Yang

Often, IT admins need to workout the first/second/third/fourth Mon/Tue/Wed/Thur/Fri/Sat/Sun of any given month. some good examples are:

  • Prepare themselves for Microsoft’s patching Tuesday of each month
  • Planning for any admin tasks caused by Day Light Saving time change

So I wrote this simple function today to calculate the date for any given month & year.

Here’s the function:

Function Get-WeekDayInMonth ([int]$Month, [int]$year, [int]$WeekNumber, [int]$WeekDay)
{

$FirstDayOfMonth = Get-Date -Year $year -Month $Month -Day 1 -Hour 0 -Minute 0 -Second 0
#First week day of the month (i.e. first monday of the month)
[int]$FirstDayofMonthDay = $FirstDayOfMonth.DayOfWeek
$Difference = $WeekDay - $FirstDayofMonthDay
If ($Difference -lt 0)
{
$DaysToAdd = 7 - ($FirstDayofMonthDay - $WeekDay)
} elseif ($difference -eq 0 )
{
$DaysToAdd = 0
}else {
$DaysToAdd = $Difference
}
$FirstWeekDayofMonth = $FirstDayOfMonth.AddDays($DaysToAdd)
Remove-Variable DaysToAdd
#Add Weeks
$DaysToAdd = ($WeekNumber -1)*7
$TheDay = $FirstWeekDayofMonth.AddDays($DaysToAdd)
If (!($TheDay.Month -eq $Month -and $TheDay.Year -eq $Year))
{
$TheDay = $null
}
$TheDay
}

the $weekday variable represents the week day you after:

0 Sunday
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
Usage:
Example #1: to query the 2nd Tuesday of October 2012:
Get-WeekDayInMonth –month 10 –year 2012 –Weeknumber 2 –Weeday 2
OR
Get-WeekDayInMonth 10 2012 2 2
image
Example #2: to query the 1st Sunday of May 2013:
Get-WeekDayInMonth –month 5 –year 2013 –Weeknumber 1 –Weeday 0
OR
Get-WeekDayInMonth 5 2013 1 0

image

SCOM Enhanced Email Notification Script Version 2

Written by Tao Yang

Few years ago, I posted the SCOM Enhanced Email Notification Script in this blog and became well adopted by the community. Over the last week or so, I have spent most of my time at night re-writing this script and I have completed the new version (2.0) now.

There are few reasons why I have decided to rewrite this script:

  • Make it to work in SCOM 2012
  • To be able to include Company Knowledge articles in the email (someone asked me about this a long time ago)
  • Ability to use an external / public SMTP server (i.e. gmail) to send emails so I can decommission the Exchange server in my test lab. – since all I use this Exchange server for is to send out alert notifications and it can’t email out to the real world!
  • Improve the email HTML body layout.

So what’s changed?

  • Now the script uses SCOM SDK instead of SCOM PowerShell snap-in / module. And because of this, it works on both SCOM 2007 R2 and SCOM 2012. – so far, from my experience playing with the them, the SDK’s in SCOM 2007 and 2012 look pretty similar!
  • Also because of the use of SCOM SDK, I’m able to retrieve Company Knowledge articles.
  • In the original script, it would only retrieve knowledge articles when the language of the article is ENU (“en-US”). Therefore, any knowledge articles stored in other language packs (such as ENA) in the management pack would not be retrieved. The script now retrieve ALL knowledge articles AND ALL company knowledge articles and display ALL of them in the email (as shown in the sample below).
  • I have moved all the customisations out of the script itself to a config.xml to store customised settings. No need to modify the PS1 script anymore. Simply make necessary changes in the config.xml and place it to the same folder as the script.
  • When setting up a native SCOM SMTP notification channel, there are only 2 authentication methods you can choose from: Anonymous and Windows Integrated. This script can be configured to use a separate user name and password to authenticate to SMTP so external SMTP servers such as gmail can be used. This eliminates the needs of having to use Exchange server for mail relay.

image

  • In the original script, the alert resolution state is updated once the script has processed the alert. This feature can now be turned off. – Because we don’t always want to update the resolution state.
  • Additionally, I have made the email body look a bit tidier, the layout now looks more similar to the alert view in the SCOM consoles:

image

How to setup command subscription?

The command subscription setup is very similar to the previous version. However, for the new script, the web console link also needs to be passed into the script. I remove the section to generate web console link for the alert because the web console link can be passed into the script as a parameter natively by SCOM, why generate it again in the script when it’s already available natively?

Assuming the script is located on D:\Script folder of your RMS / MS. here’s how you set it up:

image

From zip file that you can download from the link at the end of this post, you’ll find these files:

image

It contains:

  • The version 2 of the SCOMEnhancedEmailNotification.ps1 script.
  • config.xml that you will need to modify to suit your needs
  • XML explaination.xlsx – explains each tag of the config.xml in details.
  • Command Channel Setup.txt – what to enter when setting up command channel. (assuming the script is located at D:\Script). you can simply change the location of the script and email addresses, then copy & paste each field.
  • XML Sample – contains 3 config.xml samples. one for each SMTP authentication method (Anonymous, Integrated and Credential).

The notifications subscriber and subscriptions are setup exactly the same way as the original version of the script. you can simply refer to the original blog post.

DOWNLOAD HERE.

Find out who is connected to your SCOM 2007 management group

Written by Tao Yang

As we all know, we can see how many concurrent SDK connections have been established on the RMS by looking at the Client Connections counter in OpsMgr SDK Service:

To find out who are actually connected, you can use the SDK:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager.Common") 
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager")
$RootMS = "SCOM01"
$MGConnSetting = New-Object Microsoft.EnterpriseManagement.ManagementGroupConnectionSettings($RootMS)
$ManagementGroup = New-Object Microsoft.EnterpriseManagement.ManagementGroup($MGConnSetting)
$ManagementGroup.GetConnectedUserNames()

Scripting 101 For George

Written by Tao Yang

Yesterday at work, we were having a chat with George, the team leader of our infrastructure 3rd level support team. George told me since he’s a team leader, he doesn’t have to be technical ANYMORE, all he needs to do is to delegate. Then the topic somehow shifted to scripting and George said to me all he can write is “Hello World”.

So I challenged him by opening PowerShell console on my PC and asked him to write “Hello World” for me.

It turned out, he couldn’t write it. that proved he’s NEVER technical in the first place Smile.

So I started an email trail with the whole team: Scripting 101 For George. I initially demonstrated to George how to write Hello World in VBScript, PowerShell, C# and even batch file. then other guys jumped in, extended the list to PHP, Perl, Java, Fortran, etc.

I then showed George how to create a “Hello World” infinite loop in PowerShell:

Do {
Write-Host “Hello World”
}
While ($George.Nationality –eq “Greek”)

and How to Hello World in his favourite colour:

clip_image002

In the end, I’ve been asked to go a bit crazier so I wrote this:

Download the Video HERE.

Here’s the source code for ForGeorge.PS1: Download

George, I hope you’ve now learned how to Hello World in PowerShell at least Smile. And to our boss Craig, when you are talking about organising PowerShell training for the team, I’m totally with you 100%!

SCOM Alert Notification–Using Skype

Written by Tao Yang

Recently I’ve been wanting to setup SCOM notifications in my test environment that can reach me even when I’m not home.. However, I had two problem: I don’t have Lync / OCS servers in my lab and my home test lab does not have a registered domain name and so the Exchange server in the lab cannot email outside.

Since I have Skype installed on my PCs, phones and tablets, I thought it would be nice if SCOM can send me a message when a SCOM alert is raised.

It turned out to be relatively easy, with the help from the Skype API Skype4COM, PsExec.exe and some PowerShell scripting, it only took me few hours to achieve this goal.

In a nutshell, I installed SCOM Command Console and Skype on a computer (I’ve tried both 64-bit Windows 7 and Windows Server 2008 R2). I’ll call this computer “Skype Node” in this post. I have also copied and registered the Skype4COM.dll on the Skype Node. I also wrote a PowerShell script that runs on the Skype Node computer to retrieve alert details and send out Skype messages. I then setup SCOM command notification to execute another script on the SCOM 2007 RMS / SCOM 2012 MS, this script will use PsExec.exe to remotely execute the script on Skype Node.

I managed to get this working on both SCOM 2007 R2 and SCOM 2012. The scripts and requirements are very similar, but SCOM 2007 R2 environments requires few additional configurations.

SCOM2007 Diagram

SCOM2012 Diagram

I won’t bother to go through the details on how to set this up for either SCOM 2007 R2 or SCOM 2012. the detailed guide can be downloaded from the link at the bottom of this post.

Now, I receive Skype messages on both my computer and my mobile phone:

PC:

PC Screenshot

Phone:

iPhone ScreenShot

You can download everything you need (except Skype and SCOM itself of course) from below link. Once you’ve unzip the file, you will get:

image

Skype Notification Guide.pdf is the guide that you need to follow to set this up.

Skype4COM-1.0.38.0 is the version of Skype4COM I’ve used in this solution. it is the most recent version as of writing of this post.

Download Link

As always, feel free to contact me if you have questions or need help. However, I’ll try my best but I may not be able to get back to you straightaway.

Have fun Skyping Smile. Bye for now!

Using PowerShell Remote Sessions in a large SCOM environment

Written by Tao Yang

I have previously blogged using SCOM PowerShell snap-in and SDK client in a PowerShell remote session to avoid maintain a consistent SDK connection to RMS server.

I just found out there might be a potential issue when using this technique to run scripts against a reasonably large SCOM management group.

By default, the maximum amount of memory a PowerShell remote session can use is limited to 150MB. You can check this setting on your computer by using

winrm get winrm/config

image

Depending on how much data the remote session has to process, you might running into problems.

For example, compare these 2 scripts:

Sample #1:

$me = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

$RunAsCred = get-Credential ""

$RMS = ""

$AlertID = "8a5e738c-53d5-4a04-91af-cb8bc2d2e5d3"

$NewSession = new-pssession -ComputerName $env:COMPUTERNAME -Authentication Credssp -Credential (Get-Credential $me)

$alert = invoke-command  -session $NewSession -ScriptBlock {

param($RMS,$AlertID,$RunAsCred)

Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client

New-PSDrive -Name:Monitoring -PSProvider:OperationsManagerMonitoring -Root:\

Set-Location "OperationsManagerMonitoring::"

new-managementGroupConnection -ConnectionString:$RMS -credential $RunAsCred | Out-Null

Set-Location $RMS

$Alert = Get-Alert | Where-Object {$_.Id -imatch $AlertID}

$Alert

} -ArgumentList $RMS, $AlertID, $RunAsCred

Remove-PSSession $NewSession

$alert

Sample #2:

$me = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

$RunAsCred = get-Credential ""

$RMS =  ""

$AlertID = "8a5e738c-53d5-4a04-91af-cb8bc2d2e5d3"

$NewSession = new-pssession -ComputerName $env:COMPUTERNAME -Authentication Credssp -Credential (Get-Credential $me)

$alert = invoke-command  -session $NewSession -ScriptBlock {

param($RMS,$AlertID,$RunAsCred)

Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client

New-PSDrive -Name:Monitoring -PSProvider:OperationsManagerMonitoring -Root:\

Set-Location "OperationsManagerMonitoring::"

new-managementGroupConnection -ConnectionString:$RMS -credential $RunAsCred | Out-Null

Set-Location $RMS

$Alert = Get-Alert -Id $AlertID

$Alert

} -ArgumentList $RMS, $AlertID, $RunAsCred

Remove-PSSession $NewSession

$alert

The difference between these 2 scripts is, when using Get-Alert cmdlet,  #1 uses client side filtering and #2 uses server side filtering:

#1: $Alert = Get-Alert | Where-Object {$_.Id -imatch $AlertID}

#2: $Alert = Get-Alert -Id $AlertID

The management group my scripts connect to has just over 22,000 alerts still in the operational database. So when script #1 runs, because it uses client side filtering, it retrieves all 22,000+ alerts to the local remote session, Then filter out all the rest and retrieves ONE alert with that particular Alert ID. On the other hand, script #2 uses server side filtering, only ONE alert is returned from SCOM RMS server.

Here’s what happened when I tried to run both scripts:

#1:

image

#2:

image

As you can see, script #2 worked as expected but script #1 throws an exception:

Processing data for a remote command failed with the following error message: The WSMan provider host process did not return a proper response.  A provider in the host process may have behaved improperly. For more information, see the about_Remote_Troubleshooting Help topic.

    + CategoryInfo          : OperationStopped: (System.Manageme…pressionSyncJob:PSInvokeExpressionSyncJob) [], PSRemotingTransportException

    + FullyQualifiedErrorId : JobFailure

So now we know that we should use server side filtering because it is more efficient and there are less data returned.  However, not all SCOM cmdlets supports server side filtering. this statement is also true when talking about PowerShell cmdlets in general. not all cmdlets supports server side filtering. Sometimes, I have to use client side filering. For example, when working with Get-agent cmdlet, I have to use client side filtering.

Below script is very similar to script #1, I’ve changed a little bit to retrieve information of a particular SCOM agent:

$me = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name

$RunAsCred = get-Credential ""

$RMS = ""

$AgentName = ""

$NewSession = new-pssession -ComputerName $env:COMPUTERNAME -Authentication Credssp -Credential (Get-Credential $me)

$agent = invoke-command  -session $NewSession -ScriptBlock {

param($RMS,$AgentName,$RunAsCred)

Add-PSSnapin Microsoft.EnterpriseManagement.OperationsManager.Client

New-PSDrive -Name:Monitoring -PSProvider:OperationsManagerMonitoring -Root:\

Set-Location "OperationsManagerMonitoring::"

new-managementGroupConnection -ConnectionString:$RMS -credential $RunAsCred | Out-Null

Set-Location $RMS

$Agent = Get-Agent | Where-Object {$_.PrincipalName -imatch $AgentName}

$Agent

} -ArgumentList $RMS, $AgentName, $RunAsCred

Remove-PSSession $NewSession

$agent

The management group I have connected to has around 5000 agents at the moment, so it’s a reasonable size. Below is what happened:

image

PowerShell threw an exception:

Processing data for a remote command failed with the following error message: <f:WSManFault xmlns:f=”http://schemas.microsoft.com/wbem/wsman/1/wsmanfault” Code=”3762507597″ Machine=”computername”><f:Message><f:ProviderFault provider=”microsoft.powershell” path=”C:\Windows\system32\pwrshplugin.dll”></f:ProviderFault></f:Message></f:WSManFault> For more information, see the about_Remote_Troubleshooting Help topic.

    + CategoryInfo          : OperationStopped: (System.Manageme…pressionSyncJob:PSInvokeExpressionSyncJob) [], PSRemotingTransportException

    + FullyQualifiedErrorId : JobFailure

To make the script run, I had to increase the maximum allowed memory for PowerShell Remote session. In this case, I increased from 150MB to 512MB. I used this command to increase it:

winrm set winrm/config/winrs `@`{MaxMemoryPerShellMB=`"512`"`}

After the increase, my get-agent script ran successfully:

Conclusion:

When writing a script, we normally test it against a development or test environment. generally speaking, these environments are much much small than production environments. scripts may run perfectly in dev / test environments but because of the size of environments, we may run into situations like this. Depending on the size of the production environments, we will have to adjust the “MaxMemoryPerShellMB” setting for PowerShell remote sessions accordingly.

Few PowerShell Functions Around Windows Security

Written by Tao Yang

As parts of the PowerShell project that I’m currently working on, with the help with other people’s contribution in various forums and blogs, I have produced few PowerShell functions around Windows security:

Validate Credential

function Validate-Credential($Cred)
{
$UserName = $Cred.Username
$Password = $Cred.GetNetworkCredential().Password
Add-Type -assemblyname System.DirectoryServices.AccountManagement
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine)
Try {
$ValidCredential = $DS.ValidateCredentials($UserName, $Password)
} Catch {
#if the account does not have required logon rights to the local machine, validation failed.
$ValidCredential = $false
}
Return $ValidCredential
}

Usage:

$MyCredential = Get-Credential

$ValidCredential = Validate-Credential $MyCredential

Get Current User Name

function Get-CurrentUser
{
$me = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
Return $me
}

Usage:

$me = Get-CurrentUser

Check If Current User has Local Admin Rights

function AmI-LocalAdmin
{
return ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
}

Usage:

$IAmAdmin = AmI-LocalAdmin

$IAmAdmin

Check if a user is a member of a group

function Check-GroupMembership ([System.Security.Principal.WindowsIdentity]$User, [string]$GroupName)
{
$WindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($User)

if($WindowsPrincipal.IsInRole($GroupName))
{
$bIsMember = $true
} else {
$bIsMember = $false
}
return $bIsMember
}

Usage:

#Current User:

$me = [System.Security.Principal.WindowsIdentity]::GetCurrent()

$group = “\domain admins”

$IsMember = Check-GroupMembership $me $group

#Another User (Using User Principal Name @):

$user = new-object system.security.principal.windowsidentity("tyang@corp.tyang.org")

$group = “\domain admins”

$IsMember = Check-GroupMembership $user $group

Get Local Machine’s SID

function Get-LocalMachineSID
{
$LocalAdmin = Get-WmiObject -query "SELECT * FROM Win32_UserAccount WHERE domain='$env:computername' AND SID LIKE '%-500'"
$MachineSID = $localAdmin.SID.TrimEnd("-500")
Return $MachineSID
}

Usage:

$LocalMachineSID = Get-LocalMachineSID

Check If an account is a domain account (as opposed to local account)

Note: This function also requires the Get-LocalMachineSID function listed above

Function Is-DomainAccount ([System.Security.Principal.WindowsIdentity]$User)
{
$LocalMachineSID = Get-LocalMachineSID
if ($User.user.value -ine $LocalMachineSID)
{
$bIsDomainAccount = $true
} else {
$bIsDomainAccount = $false
}
$bIsDomainAccount
}

Usage:

#Current User:

$me = [System.Security.Principal.WindowsIdentity]::GetCurrent()

$IsDomainAccount = Is-DomainAccount $me

#Another User (Using User Principal Name @):

$user = new-object system.security.principal.windowsidentity(<a href="mailto:tyang@corp.tyang.org">tyang@corp.tyang.org</a>)

$IsDomainAccount = Is-DomainAccount $user

Powershell: Prevent Users To View and Change Input or Config Files That Are Used by a Script

Written by Tao Yang

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:

  1. Create a password protected zip file that contains the config file (.xml or .ini).
  2. rename the zip file from xxxxxx.zip to xxxxxx.bin
  3. In powershell script, use ICSharpCode.SharpZipLib.dll to unzip renamed zip file
  4. compile powershell script to exe so users cannot view the script to figure out the zip file password.
  5. read the content of the extracted config file
  6. 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):

image

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.