PowerShell PSObjects – Think outside the box!

Working with PSObjects is essential to anyone using PowerShell. Especially, when there’s a need to extend the object to provide useful information.

Remember, the best way to get familiar with the PowerShell object(‘s)… (AKA PSOBject, .NET Object) is to use the ‘Get-Member’ Cmdlet.

Learn-By-Example

Creating custom properties on an existing PSObject:

1. Add a row property using Custom Expression: @{ label = ‘xxcolnamexx’; expression = { “xxxany code logic here..xxx” }; }

2. Add two new columns using the Add-Member command: “SystemName” and UserName – using existing PowerShell session environment variables: $env:COMPUTERNAME, and $env:USERNAME.

Building our Sample PSObject

Let’s create a PSObject using the following *command:

$myWindowsfilesList = Get-ChildItem C:\Windows -Recurse -ErrorVariable flerr;
$myWindowsfilesList.count

*Note: Please ignore the errors and we’ll get back to it later. Errors are saved in the $flerr variable for later viewing.

## – List a small range of rows without the “Row” Property:
$myWindowsfilesList[0..5]
$myWindowsfilesList[1000..1005]

Now, with the help of PowerShell Custom Expressions, we’ll be providing a ‘row’ property to the existing output object. The new ‘Row’ property will be included in the “Select-Object” command and at the end of the process, will be saved as a new PSObject: “$myNewWindowsfilesList”.

Remember to initialize a global variable counter used for the row property.

To build the Custom Expression for the ‘Row’ property, see the line below:

@{ l = 'row'; e = { "[" + $global:row.tostring("D") + "]"; ++$global:row }; }

Try the new Code below:

## - 
## - To preserve the new 'row' column as part of the PSobject. by saving it as a new object.
## -
$myWindowsFilesList = dir c:\Windows -Recurse -ErrorVariable flerr -ErrorAction SilentlyContinue;
$row = 0; $myNewWindowsfilesList = $null;
$myNewWindowsfilesList = $myWindowsfilesList `
| Select-Object @{ l = 'row'; e = { "[" + $global:row.tostring("D") + "]"; ++$global:row }; }, `
name, mode, lastwritetime, length;

## - verify the 'Row' column has been added:
$myNewWindowsfilesList | Get-Member -MemberType Properties;

## - List a small range of rows:
$myNewWindowsfilesList[1633.. 1645] | Format-Table;

Add custom property using Add-Member

Add columns “SystemName” and “UserName“, to an existing new object using the “Add-Member” cmdlet within the ‘foreach{..}’ statement.

## - 
## - Using "Foreach" command to Add two new columns: "SystemName" and "Username"
## - to an existing PSObject.
## - 
[int32]$i = 0
foreach ($r in $myNewWindowsfilesList)
{ 
   ## - The following Add-member adds a property to an existing PSObject:
   $r | Add-Member -NotePropertyName "Systemname" -NotePropertyValue $env:COMPUTERNAME;
   $r | Add-Member -NotePropertyName "username" -NotePropertyValue $env:UserNAME;

   ## - Below code will show the progress bar:
   $percomplete = ($i / $myNewWindowsfilesList.count) * 100
   Write-Progress -Activity "Counting $i times" -PercentComplete $percomplete;
   ++$i
};

Displaying member properties and sample data with added columns:

## - verify the 'Row', 'computername', and 'username' columns has been added:
$myNewWindowsfilesList | Get-Member -MemberType Properties;

## - List a small range of rows with the new columns:
$myNewWindowsfilesList[1633.. 1645] | Format-Table;

Here’s where the PowerShell magic happened, as you would think that the Add-Member command would only affect only the row “$r” variable. The main PSObject “$myNewWindowsfilesList” has been updated. There’s no need to save the PSObject with a different name.

Recap

The combination of using ‘Custom Expressions’ with the Add-Member cmdlet gives great flexibility for building customized PSObjects. Go ahead and copy/paste this code, make your own properties, extend your PSObjects, and start thinking outside of the box.  Make it fun!

Bonus

In the script code you’ll find a few techniques you’ll want to pay attention to:

1. String “.NET formatting” for the ‘row’ column.

2. Use of the “-ErrorVariable” with the “-ErrorAction” parameters – prevents the errors or exceptions to be displayed in the console. For more information, use the command:
  Get-Help about_CommonParameters -ShowWindow;

3. Using the “Write-Progress” Cmdlet to display a progress bar while going thru adding the new columns. For more information, use the command:
Get-Help Write-Progress -ShowWindow;

4. “Get-Member” Cmdlet information, use the command:
Get-Help Get-Member -ShowWindow;

5. Displaying the saved errors from the $flerr variable;

## - 
## - BONUS - Check how many errors were saved, list a few of the messages
## -
$flerr.count
$flerr[50 .. 54].exception.message

PowerShell is a powerful scripting language. Enjoy and Keep Learning!
Own your future!

PowerShell Working with SQL Logins Name

This blog post is following “Changing a SQL Server Login name with T-SQL“. Let’s take advantage of the .NET SMO framework assembly object model Namespaces with PowerShell to change a Windows account in a SQL Server Logins.

Remember to download the latest version of PowerShell.

There’s nothing wrong in using SSMS (SQL Server Management Studio) as our GUI application to manage our SQL Server engine. But soon, you will have the need to use PowerShell scripting for automating some daily tasks. And, Trust me! It will save you time.

Both Microsoft and the SQL Server Community provide you with some of the Awesome tools, such as the following PowerShell modules: SQLPS, SqlServer, Secretmanagement, and DBATools.

Let’s begin with creating  a list all SQL users on our SQL Server using the DBATools module  “Get-DBAuser” command:

Get-DBADBUser -SqlInstance 'localhost,1433'

As you can see, this command returns a lot of information you can export and dissect in many way.

Now, let’s take this a little further using SMO Object Model Namespaces.

Don’t be scare! in order to start using these SMO Classes. To start, all you need to have installed any of the following PowerShell Modules: SQLPS, SQLServer or DBATools, then execute the “import-Module” command:

## This will load SMO assemblies:
Import-Module SqlServer

Then all necessary SMO Assemblies are loaded and ready to be consumed during your PowerShell session. You can start building your own PowerShell one-liners or scripts/functions command to interact with the SQL Server engine.

Let’s cut to chase, and create a simple PowerShell function “Get-SqlLogins‘ to simply list all my SQL logins in my SQL Server:

## - function_Get-SqlLogins.ps1:

function Get-Sqllogins
{
param
(
[parameter(Mandatory = $true)]
[string]$sqlname,
[string]$uname,
[string]$upwd
)

## - Prepare connection to SQL Server:
$SQLSrvConn = `
new-object Microsoft.SqlServer.Management.Common.SqlConnectionInfo($sqlname, $uname, $upwd);
$SQLSrvObj = new-object Microsoft.SqlServer.Management.Smo.Server($SQLSrvConn);

## - Get SQL SERVER list of database names:
$global:itm = 0
$SQLSrvObj.logins | Select-Object @{ l = 'itm'; e = { $global:itm; ++$global:itm }; }, name, logintype;

}; 
$sqlname = 'localhost,1433';
$uname = 'sa';
$upwd = '$SqlPwd01!';

Get-Sqllogins -sqlname $sqlname -uname $uname -upwd $upwd

## - End-of-File

Note: Save this code as function_Get-Sqllogins.ps1. 

You can edit this file to run one liner at the time and explore the $SQLSrObj PowerShell object.

Use the following GET-Member(alias gm)command to explore the object content:

## - exploring .NET Objects:
$SQLSrvObj | gm | Out-GridView

This is a good way to learn about your PowerShell objects.  You’ll be surprised by the ton of information you can find for documentation.

Now, try listing all SQL logins names by typing the following:

## - shorthand  to list all values in a object proprety:
$SQLSrvObj.logins.name

So, with a few lines of code, you can quickly get results.

Now, proceeding with looking for the Windows account I want to change the name in the SQL Login.

For this, I need to add line numbers to the PSObject result. This way I can Isolate the Login ID:

$global:cnt = 0
$SQLSrvObj.logins | Select-Object @{ l = ‘cnt’; e = { $global:cnt; ++$global:cnt }; }, name, logintype

For the finale: Changing the SQL Login Name. I’m going to manually do this using SMO PowerShell One-liner:
I found that element #5 is the SQL login I need to change:

## - verify before making the changeto the SQL Login object;
$SQLSrvObj.logins[5]

So far we’ve been working with SMO .NET Objects properties. Here’s where we use SMO .NET methods which affect the object (element#5) I have manually selected using “$SQLSrvObj.logins[5]“:

Last steps for updating the SQL Login name:

Note: Keeping in mind, the actual change starts at the Windows Account level. 

1. The *.Alter() method sets the object ready to be changed:

$SQLSrvObj.logins[5].alter()

2. followup by the *.rename(**string**) method which will affect the name object.

$SQLSrvObj.logins[5].rename('MXTLPT01\Dev01')

3. And, finally we use the *.refresh() to update all logins list with the name change.

$SQLSrvObj.logins.refresh()

AS you can see,  this open to some automation opportunities that can involve Windows Domain with SQL Server Accounts administration.

Don’t forget! always test your scripts. Practice makes a good scripter, and never be afraid of trying new stuff.

SQL PowerShell! It is the way!

Updating ActiveDirectory module in Windows 10

Do you want to use “ActiveDirectory” module in PowerShell 7 RC.1 in Windows 10? For those who haven’t notice yet, seem like one of the recent updates to Windows 10 RTM Build 1909 will includes the latest version of this module.

“ActiveDirectory” module version 1.0.0.0 will not work in PowerShell 7 RC.1. It will give the following error during the import module process:

ActiveDirectory module imports, but the PSDrive AD: is not created.
Set-Location: Cannot find drive. A drive with the name ‘AD’ does not exist.

To correct the issue, you will need to update this module to version 1.0.1.0.

How to install the updated version?

First, make sure you have installed all of the latest Windows updates. Previous ActiveDirectory module will be on version 1.0.0.0.

To install, look in the “App or remove programs | Optional Features” then look under “Add a feature” for the “RSAT: Active Directory Domain Services and Lightweight Directory Services Tools.

It will replace the previous version with the newer one and will work with PowerShell 7 RC.1.

Remember

To use this module the system need to be a member of a domain, or you’ll get the following error message:

WARNING: Error initializing default drive: ‘Unable to find a default server with Active Directory Web Services
running.’.”

Also, it’s only available for Windows 10 RTM Build 1909, Windows 10 Insider Edition, and Windows Server 2019.

Powerhell Core Ubuntu 18.04 – PSRemoting to an Active Directory Machine

Sometime there’s the need to do PowerShell remoting from Linux to a Windows System. In my lab environment, I was able to install, configure, and established a PowerShell Remote connection from a Linux Ubuntu 18.04 system to *Active Directory joined Windows System.

*Note: Before trying to following steps, if you’re in a corporate domain, consult with your security team. I would recommend that you try this scenario in virtual machine environment.

I’ve been struggling trying to OpenSSH in both Windows 10 (Build 1803) and Windows Server 2019 with no success connecting from Linux. So, I decided to try install Kerberos component on my Ubuntu system and it works!  And, with no need to joined my Linux system to my virtual Active Directory domain.

Install and configuring Kerberos Client

  • I need to install and configure the Kerberos Client application on my system:
$ sudo apt-get install krb5-user
  • Customizing *krb5.conf file settings for my domain:
$ sudo vim /etc/krb5.conf
  • The following are my custom settings in the krb5.conf file for “DOMAINNAME” Kerberos:
[libdefaults]
default_realm = DOMAINNAME.COM

# The following are custom settings for "DOMAINNAME" Kerberos:
dns_lookup_realm = true
dns_lookup_kdc = true
default_tgs_enctypes = arcfour-hmac-md5 des-cbc-crc des-cbc-md5
default_tkt_enctypes = arcfour-hmac-md5 des-cbc-crc des-cbc-md5
permitted_enctypes = arcfour-hmac-md5 des-cbc-crc des-cbc-md5

[realms]
TRINITY.COM = {
kdc = DOMAINMACHINENAME
admin_server = DOMAINMACHINENAME
}

[domain_realm]
.com = DOMAINNAME

*Note: Make a copy of the krb5.conf file before any changes.

One thing to point out! Both DOMAINNAME and DOMAINMACHINENAME, must be in uppercase.

Configuring ssh

Next step involves in configuring the ssh for Kerberos negotiation. This is the ssh_config file (not sshd_config).

$ sudo vim /etc/ssh/ssh_config

Make sure the following parameters are set at the end of the *ssh_config file:

SendEnv LANG LC_*
HashKnownHosts yes
GSSAPIAuthentication yes
GSSAPIDelegateCredentials no
GSSAPIKeyExchange yes

*Note: If there are missing ones, don’t touch the commented ones. Just copy/paste and set the values.

After completing the changes, I would recommend a reboot.

Testing and working Kerberos Client

Here are a few linux commands to work with Kerberos client.  If the krb5.conf setting are set correctly, then the following commands should work without any issues.

1. This command will verify user domain, asking for password.

$ kinit username@domainname

2. Shows the list of Kerberos Cached tickets and credential.

$ klist

3. To delete\clear all Kerberos Cache entries:

$ kdestroy

What about setting in Windows Systems?

I’m will cover the whole PowerShell remoting setup. But, I will highlight what’s needed to make Linux connect to a Active Directory Domain system.

  • Enable PSRemoting

In PowerShell Conscole, run the “Enable-PSRemoting -force” command line on both client and server. This command will add the firewall rule to allow PowerShell remoting to work.

  • Check WinRM Service

Check the Windows Remote Management service is running. By default, in Windows 10 client, this is set to “Manual”.
On the server, just verify that the service running.

Before, connecting Linux to a windows domain system, make sure to test PowerShell remoting between Windows machines. This will guarantee that you got everything working correctly.

Name Resolution Tip

I don’t join my Linux system to my AD domain. So, to resolve my name resolution issues, I manually update the hosts file on my systems. This will include the domain ip-address as well as all other systems

hosts file
:
xxx.xxx.xxx.xxx domainname.com
:

Testing connectivity

Ubuntu 18.04 Connecting to a domain system final test.

1. In Linux, open PowerShell:

$ pwsh

2. Prepare the domain user:

PS /home/user> kinit domainuser

3. Create a *PowerShell Remote interactive session:

PS /home/user> Enter-PSSession -ComputerName wincomputer -Authentication Negotiate -Credential user@domainname.com

*Note: This remote connection will open Windows PowerShell and not PowerShell Core.

Summary

So, in Ubuntu 18.04 installing and configuring Kerberos user client only, you can connect your Linus system to a Active Directory Domain systems. But remember, this will connect to a Windows PowerShell session only.

I’m hoping that in the near future we can have the ability to select a PowerShell versions. Wait!!  There’s a way to open a PowerShell Core session instead of Windows PowerShell!!

How To Connect to PowerShell Core

So, by default you’re going to connect to Windows PowerShell. But, if you use the following parameter ‘-ConfigurationName’ folllowed by either ‘PowerShell.6‘ or ‘PowerShell.6-Preview‘ then you’ll get PowerShell Core session.  Also, you can use an specific version ‘PowerShell.6.1.0‘.

Enter-PSSession -ComputerName venus -Authentication Negotiate -Credential max_t@trinity.com -ConfigurationName PowerShell.6

Thanks to Steve Lee (Microsoft PowerShell Team) for letting me know this is already available.

References

The following links help figured out the needed components to make my lab environment work.