Sunday, August 18, 2019

Many Things Go Well with PowerShell 7 - But Not All!

Introduction 

I am currently working on a PowerShell 7 book for those nice people at Wiley (https://blackwells.co.uk/bookshop/product/PowerShell-7-and-NET-Core-3-0-for-IT-Pros-by-Thomas-Lee-author/9781119644729). I'm meant to finish up by December and have the book out in 2020. For me, one of the key questions around PowerShell 7, in the Window space at least, is how well PowerShell 7 can be used to manage an enterprise. If it can, then IT Pros can move towards it with increasing confidence.

PowerShell 7, at the time this is written is at Preview-3 stage with an expectation of shipping sometime in September. If you find I am wrong on any of the points below please let me know. This is a work in progress as we approach PowerShell 7 RTW.

What Works and What Doesn't

In working on the book, I have found that commands, modules, and .NET classes fall into 3 distinct bands:
  • Stuff that works. 
  • Stuff that can work using WindowsCompatibility module
  • Stuff that does not work and probably won't work 

Stuff that works is, well, stuff that works. Most of the basic day to day commands/modules work fine. Which I think is to be expected. A big win here is that the ActiveDirectory module works great (although sadly, the AD Deployment module does not work natively).

The Windows Compatibility module (WCM) is a solution to some compatibility issues. WCM uses a combination of implicit remoting and PowerShell remoting sessions. For each module you want to use - you use Import-WinModule to load the commands in the current PowerShell session. WCM, under the cover, creates a remoting session into PowerShell 5.x on the local machine and imports the underlying module using implicit remoting. Thus you can import the ADDeployment module using this technique and access the commands natively.

Stuff that doesn't work and never well should be a small subset of overall PowerShell functionality. An example is workflows - these depend on Windows Workflow Foundation and thus far at least, there is no interest in porting the necessary .NET classes to .NET Core.

While I never say never, I think if you want to use anything that doesn't work today - you either stick with PowerShell 5.x for the foreseeable future or evaluation alternatives.

Stuff that Doesn't Work and May Never Work

Here is my list of PowerShell 5.x features that are not ever going to work in PowerShell 7:
  • Workflows - Workflows in PowerShell depends on the Windows Workflow Foundation and that is not in .NET Core. As far as can be told at the time of writing, this is not going to happen. See the release notes at https://docs.microsoft.com/en-us/powershell/scripting/whats-new/breaking-changes-ps6?view=powershell-6.
  • PowerShell Snap-ins - Snap-ins was an earlier approach to add-ins, but were replaced by Modules in Windows PowerShell 2.  Some Microsoft modules were affected but these are being updated. In many cases, you can convert the Snap-in into a module - but many modules delivered by third parties may not be so easy to modify. If you are still using an old snap-in, consider migrating it to a module. 
  • Desired State Configuration - this too is not going forward. At least not yet. Having said that, there might be some updates on this shortly. We'll see.
In my testing, there are also some modules that do not currently work with Preview 2. The final status is unknown - but the following do not work currently even using WCM. 
  • Best Practices Module - I would have expected this module to have been fully supported.

Things That Work Using the Windows Compatibility Module

There are a number of Windows PowerShell cmdlets and modules which are not supported natively in PowerShell 7 but can be used by way of the WindowsCompatibility module. You load these modules using the Import-WinModule command.

The modules I've found that do not work natively in PowerShell 7 but do work with Windows Compatibility Module include:

  • ADDSDeployment module 
  • AppLocker module
  • ConfigCI module
  • DFSR module
  • DHCP Server module 
  • GroupPolicy module
  • IscsiTarget module
  • MsDtc  module
  • NetworkController module
  • NetworkControllerDiagnostics module
  • NetworkLoadBalancingClusters module
  • ServerManager module
  • Storage Replica module
  • The *-Computer commands in Microsoft.PowerShell.Management specifically Add‑Computer, Checkpoint-Computer, Remove‑Computer, and Restore-Computer commands.
  • The *-Counter commands - these are used to obtain Windows performance counters.
  • The WMI cmdlets. I was kind of surprised at this - but still, the CIM cmdlets are just a better way to leverage WMI.
NB: this is a work in progress. I'll update it as I find out more:
1. (20/8) Removed Print Management from a list of modules that do not work - all seems fine with Preview.2.
2. (21/8) Updated modules that need remediation. 
3. (21/08) Retested and updated based on Preview 3.









Wednesday, July 31, 2019

The End Of My Era

For me, today is a sad day. At least in some ways. Today is the end of my career as a Microsoft Certified Trainer. My MCT  credentials lapse today and as I stopped taking/passing exams, I am no longer able to renew.  So this is the end of my 26+year road as an MCT and the end of my 51 year career in IT, largely focused on Microsoft technologies.

I first got excited about computers in 1968, when I found that the job of an operator paid something like $.50/hour more than washing dishes – and was way cleaner. After a degree from CMU in Computer Problem Solving (what the hip call AI today). I spent three years working both in tee US and the UK for Comshare. They were a time-sharing company and were, in effect, at the beginnings of the Cloud.

After several years as an OS developer, I took a sabbatical for 7 months. The high point of that trip was seeing Mt Everest over my shoulder from 18192 feet.


After returning, I joined Arthur Andersen and a few weeks later was in Chicago at a training event when I read about the launch of the PC and was blown away by the potential of the PC. Within days of the announcement, I was bugging our partners to invest. Looking back, I became one of the first DOS trainers in the world!  During the 1980s I taught to a variety of audiences on subjects including PCs, DOS, dBase, Word and Windows. I left Andersen in 1988 and launched my own company specialising in a combination of training and direct mail. in 1992 – One cool client was The Savoy Hotel Group which was a lot of fun.

I discovered NT (as an early beta tester) and began doing NT training in 1993 – teaching at SkyTech in London. Happy memories. I also taught for Learning Tree. Their NT and TCP/IP courses were awesome.  I then became an MCT at the very opening of the programme and until today I have remained an active MCT (MCT ID 6851).

Those early days were very different from today. To become an MCT you had to attend an evaluation session (often dubbed Shelia York’s Day Of Hell). I failed my first attempt – I was just told to turn up and was never given the relevant information. Second time around I nailed it and became both one of the first MCTs and one of the first MCSEs in the world.  You also had to attend a trainer prep course for any course you wanted to re-deliver – I taught a lot of these in my time which was a real honour to do this training.

I was also active first on MSN, then in the newsgroups, when there were newsgroups. I loved being able to help other MCTs. Like many MCTs, I  spent time working for Microsoft both in Redmond and Europe. As an MCT I served on a couple of advisory boards too. Perhaps the most meaningful for me was the Certified Learning Consultant initiative – requiring learning partners to have suitably qualified learning consultants on staff.

As many of the early MCTs will know, I have a passion for quality. Even when I all too often fall short myself. Such is life. When Lutz Ziob joined Microsoft Learning, he quickly outsourced much of the work that had previously been done internally (with LOTS of contractors – some of them awesome). The outsourcing made sense, unfortunately, the quality of what was produced was incredibly bad. I started a discussion about quality which I am glad to say had a major effect. One thing the discussion surfaced was that students often rated the courseware far better than the MCTs did. If nothing else, this proved the value of the MCT.

I have many happy memories: meeting Bill Gates and getting him to sign my Windows 95 Gold CD.


I also met Steve Ballmer on a couple of occasions. Here’s one:


In my travels, I have had many adventures, lost luggage, horrible rooms, cancelled/rerouted flights. I even spent an evening in jail in Turkey during a military revolution. I have also had the good times – flying Concorde, staying in the Savoy, and eating at the Tuna House. A precious memory was being in the room when Jeffrey Snover launched PowerShell and waving a $20 at him saying I’ll buy it.

So now it's over. I enter full retirement with a mixture of relief(I made it!) and sadness.







What a long strange trip it's been. Thanks for all the fish

Monday, July 22, 2019

Windows Forms - working in PowerShell 7 Preview2

One long-anticipated feature of what is soon to be PowerShell 7 is the use of Windows Forms. Technically, it is the .NET Framework that provides for form handling, but with the latest Preview.2 of PowerShell 7, this is now possible!

Here is a Windows Forms-based script that now works just fine in Powershell 7 Preview 2.

First, here is the code:

# Get-Shares Windows Forms Script
#

# Load System.Windows.Forms
Add-Type -Assembly System.Windows.Forms

# Get PowerShell version details
$Maj = $PSVersionTable.PSVersion.Major
$Min = $PSVersionTable.PSVersion.Minor
$Ver = "$Maj.$Min"

# Create form
$Form = New-Object Windows.Forms.Form 
$Form.Width = 750
$Form.Height = 650
$Form.Text = "My First Windows Forms Application - PowerShell Version $ver"

# Create a "computer name" label control and add it to the form
# Set label location, text, size, etc
$Label1 = New-Object Windows.Forms.Label
$Label1.Location = New-Object Drawing.Point 50, 50
$Label1.Text     = "Computer Name:"
$Label1.Visible  = $true
$Form.Controls.Add($Label1)

# Create a text box to get computer name and add to form
$Text1 = new-object windows.forms.textbox
$Text1.Location = New-Object system.drawing.point 150, 50
$Text1.Text     = "Localhost"
$Text1.Visible  = $true
$Form.Controls.add($text1)

# Create a label to output stuff
$Label2 = New-Object Windows.Forms.Label
$Label2.Location = New-Object Drawing.Point 50, 100
$Label2.Width    = 750
$Label2.Height   = 360
$Label2.Text     = ""
$Label2.Visible  = $true
$Form.Controls.Add($Label2)

# Create a button to get the shares
$Button1          = New-Object Windows.Forms.Button
$Button1.text     = "Push To Get Shares"
$Button1.width    = 150
$Button1.location = new-object drawing.point 350, 50

# Define Getting Shares Button Click handler
$Button1_OnClick = {
  $Label1.Text = "Getting Shares"
  $Shares = Get-CimInstance WIn32_Share -ComputerName $Text1.Text
  $Label2.Font = [System.Drawing.Font]::new('Courier New', 10)

  $Label2.Text = "Shares on $($Text1.Text):`n"
  Foreach ($Share in $Shares) {
    $Name = $Share.Name
    If ($Name.Length -gt 17) {
      $name = $($Name.substring(0,17)+'...').padright(16)
      }

    $Path = $share.Path
    $Label2.Text += "{0,-20}  {1}`n" -f $Name, $Path
  }
  $Label1.Text = "Computer Name:"
}
    
# Add the script block handler
$Button1.Add_Click($Button1_Onclick)    
$Form.Controls.Add($button1)

# Now create a button to close window
$Button2          = New-Object Windows.Forms.Button
$Button2.Text     = "Push To Close Form"
$Button2.Width    = 150
$Button2.Location = New-Object drawing.point 160, 550

# Define Button Click handler
$Button2_OnClick = {
  $Form.Close() 
}

# Add the script block handler
$Button2.Add_Click($Button2_Onclick)    
$Form.Controls.add($Button2)

# finally, show the form as dialog box.
$Form.ShowDialog()


And here you can see the results. First against first a work station:


And here a DC



Success. A working Windows.Forms script that runs great on Windows PowerShell now runs fine in PowerShell 7 Preview.2 (and beyond into infinity....)



Monday, July 01, 2019

Windows Terminal Is Here (in Preview) and it Rocks!

In Windows, PowerShell and CMD.Exe both run inside a console. That console is where programs interact with a command line application. The console inside Windows has not had a lot of love, although has improved of late. But that has just changed big time with the release of the Windows Terminal. If you are running Windows 10 - just go the Windows Store and get it - it's free.

The installation does a great job of detecting the console applications including CMD.EXE, PowerShell.Exe, PWSH.EXE, and the versions of WSL you are running, By default, the terminal installer does not find PowerShell 7.

Configuration of the WIndows terminal is, today, done via hand editing the Profiles.JSON file. On My workstation, this file is: C:\Users\tfl\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\RoamingState\profiles.json - You may need to adjust it based on your userid.

To enable PowerShell 7,. I just added a new section at the top of the profiles section inside the profiles.json file. I added the following:

 {
   "acrylicOpacity": 0.75,
   "closeOnExit": true,
   "colorScheme": "Solarized Light",
   "commandline": "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
   "cursorColor": "#FFFFFF",
   "cursorShape": "bar",
   "fontFace": "Consolas",
   "fontSize": 12,
   "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff4442}",
   "historySize": 9001,
   "icon": "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png",
   "name": "Windows PowerShell 7.0.0 Core Preview",
   "padding": "5, 5, 5, 5",
   "snapOnInput": true,
   "startingDirectory": "%USERPROFILE%",
  "useAcrylic": false
  },

If you wish to add PowerShell 7, as I do, you need to do is to ensure the guid value inside this profile block is unique. You can just munge an existing GUID, as I did, or use New-Guid cmdllet create one know to be unique.

Since I plan to use Pwsh 7 mainly, I also wanted the default shell to default to using PowerShell 7. That also involves updating the Profiles.JSON file. At the top of the file, in the Globals section, change the value of the defaultProfile to the GUID of Pwsh 7. Mine looks like this


{
 "globals" : 
  {
   "alwaysShowTabs" : true,
   "defaultProfile" : "{61c54bbd-c2c6-5271-96e7-009a87ff4442}",
   "initialCols" : 120,
   "initialRows" : 30,
 ... etc


Once I have set these values in the Profile.JSON file, my terminal looks like this:


And away we go. There is far more you can do with the JSON file and I sure hope that by release later this year, there is a GUI to help configure the settings. I don't mind in the least having to deal with the JSON file and can live with this for now. 

Friday, June 14, 2019

What PowerShell Commands Do You Use Most Often?

I saw this question in a twitter thread. And a good one it is too. My immediate reaction was: Get-Module, Get-Command, Get-Help, and Get-Member. But those are my most  valuable. So what were my most USED? Turns out there’s a script for that:

PS [C:\foo> ]> Get-MostUsedCommands
Count Name
----- ----
1442 git
832 cd
578 ls
353 f
243 cc

I have uploaded this script to the Spiceworks script library at https://community.spiceworks.com/scripts/show/4644-get-the-most-used-powershell-commands - Feel free to download that code and play with it!

But if you are impatient and want the code, here it is:

Function Get-MostUsedCommands {
   <#
.Synopsis
    Gets most used PowerShell commands
.DESCRIPTION
    Uses the parser and command history to construct a list of the
    most used commands and returns a sorted list. The -TOP parameter
    tells how many results to return.
    Based on a tweet by @stevenjudd
.EXAMPLE
    PS [C:\foo> ]> Get-MostUsedCommands

  Count Name
   ----- ----
    1442 git
     832 cd
     578 ls
     353 f
     243 cc
.EXAMPLE
    Another example of how to use this cmdlet
.INPUTS
    Inputs to this cmdlet (if any)
.OUTPUTS
    Output from this cmdlet (if any)
.NOTES
    Works in Windows PowerShell 5.1, PowerShell 7 Preview 1.
#>
   # Define the parameters
   [CmdletBinding()]
   Param (
     [Alias("GMUC")]
     $Top = 5 # top number of results to return 
   )

  $ERR = $null
   [System.Management.Automation.PSParser]::Tokenize((Get-Content (Get-PSReadLineOption).HistorySavePath), [ref]$ERR) |
     Where-Object { $_.Type -eq 'command' } |
       Select-Object -Property Content |
         Group-Object -Property Content |
           Sort-Object -Property Count, Name -Descending |
             Select-Object -Property Count, Name -first $Top
}         

Saturday, June 08, 2019

Using PSEdit from the PowerShell Console - There's a script for that!

The PowerShell ISE has a neat built-in function, PSEDIT. It brings one or more files up into exit windows. I used to use this a lot (in the days before I discovered VS Code!). The PSEDIT function used the $PSISE object to add the files into edit windows. But this does not work from withing the PowerShell console.

But as ever, there's a script for that!  Just add the following function definition into your PowerShell 5.x console:

Function PSEDIT {
[CmdletBInding()]
param([Parameter(Mandatory=$true)]$filenames)
foreach ($filename in $filenames) {
dir $filename | 
  where {!$_.PSIsContainer} |
     %{
        start-process $Pshome\powershell_Ise.exe $filename
      }
   }
}

Once you restart PowerShell, you now have that command avaialble in the console.

Of course, in PowerShell 7, this function does not work since there is no PowerShell ISE V7. In PowerShell 7, you should be using VS Code. Once you install VS Code, typing Code $FileName just works as you would expect - and either start a new instance of VS Code with that file or add the file to an open instance. Very civilised - and another reason I love VS Code.

Tuesday, June 04, 2019

First Steps with With PowerShell 7

In my last blog post, PowerShell 7 Is Here - Getting Started, I showed how you can install PWSH 7 and associated tools. I have had some good feedback on that post so it all seems to work (today!).

Having run the Install-PWSH7 script, which you can find at: https://community.spiceworks.com/scripts/show/4638-install-powershell7, you should now have PowerShell 7 Preview 1 on your system.

The first thing to notice is that the executable is now pwsh.exe. Also, the home directory, aka $PSHOME is now: C:\Program Files\PowerShell\7-preview. What this provides is a mechanism to run multiple versions of PowerShell side by side. On my system, I have PowerShell 5.1, 6.2 and 7.0. If you use the MSI to install PowerShell 7, the installer updates your environment path so you can easily find PWSH.EXE.

The next thing to notice is that there are a lot fewer modules immediately available. Since PowerShell 7 is installed in a different folder, modules you may have with PowerShell 5.1 are not visible. One thing I had to do was copy over my personal modules.

The next thing I found is that some modules do not load naturally. So for example, you can not just use Import-Module to import the ServerManager module. The solution to this is to use the Windowscompatibility module you can find on the PowerShell Gallery. Amongst other things, this module uses implicit remoting to access Windows PowerShell cmdlets from PowerShell7, for those modules that are not compatible directly. 

An example of using this is the Get-EventLog cmdlet. By default, running this cmdlet results in errors. But by using the Import-WinModule Microsoft.PowerShell. Management command you can access the windows event log, like this:


For more information on this module, see https://github.com/powershell/windowscompatibility.

So far, the following modules need to be loaded using Import-WinModule:
  • ServerManager
  • ADDSDeployment (and even then some cmdlets do not work properly).
  • DHCPServer
And finally, there are some things that are either not going to be supported or, arguably, are broken. So far, Windows.Forms, DSC and Workflows are not supported. Support for forms should come in later releases, prior to RTM.

With respect to Active Directory, I've worked out how to promote a server to be the first DC in a new forest but so far, I can not add a second DC to that initial domain. Still playing with that.

Another thing I found that is, arguably broken, is that the System.IO.DirectoryInfo object, returned from Get-ChildItem/GetItem cmdlets has changed the way it returns the parent directory. In PowerShell 5.1, the Parent property is just a relative path,, whereas in later versions this property returns the full path.  This will break some scripts. 



Sunday, June 02, 2019

PowerShell 7 Is Here - Getting Started

Like many IT Pros, I’ve been waiting on the first builds of PowerShell 7. Late last week Steve Lee on the PowerShell team inside Microsoft published a road map to PowerShell 7. And on Friday I finally got access to the code! YEAH. It took some playing around with the code, but I now have a basic development environment up and working. And of course, there’s a script for that.
The environment for PowerShell 7, at least for me, includes downloading the latest build and installing Visual Studio Code as my main IDE. Here’s my getting started script:
# 1. Fix Execution polity then install latest versions of Nuget and PowershellGet
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process -Force
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force
Install-PackageProvider Nuget -Force |
  Out-Null
Install-Module -Name PowerShellGet -Force –AllowClobber 


# 2. Setup C:\Foo to hold stuff. ignore the error if it already exists,

#    then set location to C:\Foo
New-Item -Path C:\Foo -ItemType Directory –EA 0
Set-Location -Path C:\Foo

# 3. Install PowerShell 7

#    For now, load the preview version.
#    Based on: https://www.thomasmaurer.ch/2019/03/how-to-install-and-update-powershell-6/
$URI = "https://aka.ms/install-powershell.ps1"
Invoke-RestMethod -Uri $URI |
  Out-File -FilePath C:\Foo\Install-PWSH7.ps1
C:\Foo\Install-PWSH7.ps1 -UseMSI -Preview -Quiet


# 4. Download and save Install-Vscode script from PSGallery

Save-Script -Name Install-VSCode -Path C:\Foo

# 5. Now run it and add in some popular VSCode Extensions

$Extensions = "Streetsidesoftware.code-spell-checker",
              "yzhang.markdown-all-in-one",
              "davidanson.vscode-markdownlint"
$InstallHT = @{
   BuildEdition = 'Stable'
   AdditionalExtensions = $Extensions
   LaunchWhenDone = $true}
.\Install-VSCode.ps1 @InstallHT 

# 6. Install WindowsCompatibility module

# the ServerManager module
Install-Module -Name WindowsCompatibility -Force

In step 1, I update the execution policy, and install the latest versions of the NuGet package provider and get the latest PowerShellGet module. Windows 10 ships with older versions of these tools so updating them is a good thing to do. With step 2, I create a  local folder, C:\Foo, which is where I put stuff.  Feel free to change either or both of these steps to suit your needs. IN step 3, I download and run a script to install PowerShell 7 on the local system.  The code here is a slightly more long-winded (but possibly more easily understandable) version of a one-liner posted by @Steve_MSFT. 
In step 4, I download a script that installs VSCode and in step 5, I run the script. This installs the latest stable version of VSCode along with three useful extensions. Adjust this list as appropriate.                 
Lastly, in step 6, I install the WIndowsCompatibility module – which is going to be useful in order to use PowerShell 7 to support a wider set of modules/cmdlets.  I plan to highlight this in coming blog articles.
Now that you have PowerShell 7 land VSCode, you need to set VSCode to a) use your colour scheme of choice then set the version of PowerShell you want VS Code. On my workstation, here’s how that looks:
So now that you have both PowerShell 7 and VS Code installed, it’s time to start getting used to the new version. My next post is going to be around installing AD, and the issues arising (along with solutions where possible).














Monday, April 08, 2019

PowerShell 7 - Coming Soon!

The news is now out and to me clear - the next release of PowerShell, to be known as PowerShell 7 is going to be a huge deal. Steve Lee released the news in a blog post about the next release. There are several big points in Steve's blog post:

First, the next release is going to be called PowerShell 7. They are dropping 'core'  from the product name, and the team are taking the opportunity to up the major build number to 7.0. I think many folks confused the term "core", so this is a great time to tune the product name. The change in version number was also surprising but most welcome - by calling it PowerShell 7, it signals to Windows IT Pros that what you now know as PowerShell Core 6.2 is pretty good on windows (all my Grateful Dead curation scrips work - so that's good!). But it's not 'there' enough: no WPF, no WinForms, and a bunch of cmdlets that are not supported.

Since PowerShell is based on .NET, its hard to do things that are not in the version of .NET in use. So since there is no WinForms support in the .NET version integrated with PowerShell Core 6.2, that version of PowerShell core cannot run Winforms programs. All you need to do is to add the necessary classes and hey presto.

.NET Core 3.0 is the big game changer here. It is intended to have all the underlying APIs that should allow upwards of 90% compatibility with Windows PowerShell 5.1. In my discussions with Steve Lee, I came away with the distinct view that .NET Core would both solve a lot of problems many Windows IT Pros had in adopting the newer versions and be an awesome tool in the Linux world. Imagine being able to write mini-GUIs that work in Linux. Utterly cool in my view and potentially very very useful for the Linux users. When we chatted, the view was to call it PowerShell Core 6.3, but I really like the name PowerShell 7. It not only makes the name easier but it calls out for adoption.

I am looking forward to updating my PowerShell books for PowerShell 7. Can't wait to get stuck in! I'll certainly be blogging more about this new release!

Monday, March 18, 2019

Moving from PowerShell Journeyman to PowerShell Master

I’ve just finished writing another book on PowerShell. The book looked at a number of core Windows Features and components, from AD, DHCP/DNS, SMB file sharing and FSRM, Hyper, V and more. Having gone through the process of witting over 125 scripts using a dozen Windows Server 2018 features  has given me a perspective on both PowerShell in use today, and in what it takes, what you really need to know in order to progress from a Google-Engineering assisted journey man into a PowerShell  master.  There is absolutely NOTHING wrong with using Google to get the job done. And for many their career path possibly precludes acquiring deep skills. But if you are the folks who aspire, please read on!

So what do you need to know, to know how to do? The following list is not in any order:

  • Understand and be able to use the PowerShell language - the starting point, for me, is that you know the PowerShell language and can use it. You should understand the core concepts of cmdlets, objects, and the pipeline. You should be familiar the the core approach to discovery (Get-Module, Get-Command, Get-Help, and Get-Member). You need to know how each language feature works and be able to leverage it in your scripts. Knowing the internal architecture of PowerShell is also almost expected.
  • Understand the .NET Framework. Powershell is built on top of .NET – cmdlets work by using .NET. Get-Process, for example, just calls [System.Diagnostics.Process]::GetProcesses(). Also known as a static method (GetProcesses()) on a .NET Class (Systems.Diagnostics.Process). You should understand the architecture of .NET (CLR, BCL, IL and JIT Compilation, .NET Security, and more).  The .NET Framework can often provide functionality for which there are no cmdlets. For example, there a number of .NET classes useful for localisation. Time zones, clock types, DST/ST, etc are all a method call away and therefore easy to use if you know how.
  • Understand how to read C# and be able to convert C# to PowerShell. There is a feast of wonderful examples of more obscure tasks often written in C#. You should be able to read the C# sufficiently to see how the code is doing things, and be able to concert simple snippets into working PowerShell Code. Knowing enough VB.NET to convert it into PowerShell is also a useful skill.
  • Understand COM and COM objects. There are a number of features that make use of COM. The Microsoft Office products can each be automated using PowerShell’s COM interop features. The Performance Logging and Alerting subsystem makes use of COM. You use PowerShell's New-Object cmdlet to instantiate a COM object to specify PLA data collector sets.
  • Know how to use XML. Some features such as the Task Scheduler make use of XML. You should know how to use the XML emitted by windows as well as knowing how to manage XML documents and the DOM. XPath is also a useful skill. The FSRM, for example, produces reports. The report format is fixed and cannot be modified. But the FSRM also produces XML files containing the report’s raw data for you to format to your own needs. PowerShell also makes use of XML for default object formatting. which you can customise to change how PowerShell formats objects.
  • Know how PowerShell modules work. Cmdlets are delivered in modules and you can write your own. Both DSC and JEA leverage modules. You should know where modules are stores, how PowerShell finds them and how it builds the Module cache, what a manifest is.
  • JEA – Also known as Just Enough Administration. It’s a neat feature that enables you to provide delegated permissions to do just those things necessary for a person’s job and nothing more. This is a very useful security feature of Powershell that appeals to large and distributed organisations.
  • Understand how to implement DSC. DSC is a great way to configure hosts and to ensure they stay configured. You should know about DSC resources, setting up DSC pull servers (SMB and Web), and DSC reporting and error logging. You should also know how DSC resources work as well as how to write your own DSC resources)
  • Master Remoting – This is a rich topic area. You should understand the PowerShell Remoting stack (including PSRM, SOAP, WinRM), how end points work and how to create a constrained end point. 
  • Understand at Depth Core Windows Features. I suppose it’s obvious, but to be a PowerShell master you have to be able to apply your skills  to Windows. You should really understand AD (and GPO), SMB (SMB3 SOFS, Clustering and Hyper-Converged S2D), Containers and Docker, Hyper-V (and maybe VMware too!), TCP/IP Networking, Disk/FIle Storage, PLA, Task Scheduler, and probably more.
  • Leverage Azure – Organisations are increasingly moving to the cloud and knowing Azure (or AWS) is also an important skill. With Azure, you should be able to build IAAS objects in cloud including web sites, VMs, and Virtual networks. You should also be able to manage Azure Storage and Content distribution.
  • Be competent at Windows Troubleshooting – There are a variety of good PowerShell tools that assist in Troubleshooting, particularly network troubleshooting. These tools really are second nature to a PowerShell master. And to be a good trouble-shooter you really need to understand what you are troubleshooting. Knowing how to leverage the information in the event logs is also critical. You should become very familiar with docs.Microsoft.com.  And if you ever work out how to fully automate the Windows trouble-shooters – let me know.
  • Use PowerShell Core and VSCode – PowerShell Core 6 is almost a re-invention of PowerShell. Cross-platform, open source, based on .NET Core which is also open source along with a totally new development tool (VS Code). Arguably,6.1 and 6.2 are not quite ready for hard core usage across all features. But it’s close – I am now using the developing 6.2 and VS Code in preference to the ISE and Windows PowerShell. My Grateful Dead scripts even work in PS Core! There are a number of features that do not work with PowerShell Core. Today, for example, DSC and Windows Forms are not supported (although 6.3 should support Windows.Forms!).

So a baker’s dozen of things you really need to know, and know how to do with PowerShell.

PowerShell Core and Experimental Features

In testing any new feature, one technique for getting users to use (and test) the feature is known as feature flags.  Essentially these are settings (flags) that signal you should gain access to those experimental features. The allows a user to opt-in to testing the new features. Thus is a big application, such as PowerShell, most users just use the published feature set. But if you know how, you can turn on some interesting new features. And of course if you do not like a given experimental feature, you can turn it off.  Let’s look at hot oaccess these features. In this blog post, I am using PowerShell Core 6.2.0-rc.1. If you install different versions, your mileage is going to vary!

Finding Experimental features

Finding experimental features is pretty easy. Hey – this is PowerShell and you should know what to do). Like this:

PS [C:\foo> ]> Get-ExperimentalFeature
Name                        Enabled Source   Description
----                        ------- ------   -----------
PSCommandNotFoundSuggestion   False PSEngine Recommend potential commands based on fuzzy search on a CommandNotFoundException
PSImplicitRemotingBatching    False PSEngine Batch implicit remoting proxy commands to improve performance
PSTempDrive                   False PSEngine Create TEMP: PS Drive mapped to user's temporary directory path
PSUseAbbreviationExpansion    False PSEngine Allow tab completion of cmdlets and functions by abbreviation

So these four experimental feature present on 6.2.0-rc.1 all look pretty cool to me.  The usefulness of these may vary. For me tablcompletion of cmdlet names using abbreviations could interesting. Potentialy really useful so worth looking at even though as I think about it – if the alias is any good, it’s wired into my fingers thus it may be a feature that I never use. Creating the TEMP: folder is something I would take advantage of. The PSCommandNotFoundSuggestion just helps IT Admins to find what they need. And the batching of implicit remoting commands could be very useful if you are, for example, managing Exchange On-Line  using local PowerShell and importing the session.

Enabling Experimental Features

Again – this is PowerShell so: Simples:

PS [C:\foo> ]> Get-ExperimentalFeature | Enable-ExperimentalFeature
WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell.
WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell.
WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell.
WARNING: Enabling and disabling experimental features do not take effect until next start of PowerShell.

So very simple to add in. Due to how these features are implemented you nee to restart Pwsh before you can access the fetures:


Using Experimental Features:

The Command not found suggestion feature is nice. After enabling, PowerShell does a better job of handling typos, like this:

PS [C:\foo> ]> get-chliditem
get-chliditem : The term 'get-chliditem' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ get-chliditem
+ ~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (get-chliditem:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException


Suggestion [4,General]: The most similar commands are: Get-ChildItem, Get-ChildItem2.

Nice.  It could be really useful where you have long cmdlet names


Disabling Experimental Features

Needless to say, disabling them is simple too: Use the DisableExperimentalFeature cmdlet to disable the commands.


Summary

Experimental features are PowerShell Core features that may or may not be added to future versions of PowerShell Core. They are easy to enable, consume, and disable as you wish but use at your own risk.

Thursday, March 07, 2019

Setting up DHCP in Windows Server 2019

In my recently published book (Windows Server 2019 Automation with PowerShell), I included several recipes to show how to install DHCP, how to setup a scope, and how to setup DHCP load balancing and fail over. This blog post looks at the steps involved.

In this walk through, I assume you have two servers on which you wish to run DHCP. Many smaller organisations install DHCP on their DCs for simplicity – I’m assuming the two servers are DC1.Reskit.Org and DC2.Reskit.Org. You also need to determine what IP addresses to hand out (this sample uses the rante 10.10.10.150-10.10.10.199), the subnet mask (255.255.255.0), and an IP address for the DHCP Server.

Hree are the steps:

1. Install DHCP Service on DC1, DC2

# Create a script block
$SB1 = {
  # Install the service
  Install-WindowsFeature -Name DHCP -IncludeManagementTools
  # Add the DHCP server's security groups
  Add-DHCPServerSecurityGroup
  # Let DHCP know it's all configured
  $RegHT = @{
    Path  = 'HKLM:\SOFTWARE\Microsoft\ServerManager\Roles\12'
    Name  = 'ConfigurationState'
    Value = 2  }
  Set-ItemProperty @RegHT
}
# Run the script block on DC1, DC2
Invoke-Command –Computer DC1 –ScriptBlock $SB1
Invoke-Command –Computer DC2 –ScriptBlock $SB1
# Authorise the DHCP server in AD
Add-DhcpServerInDC -DnsName DC1.Reskit.Org
Add-DhcpServerInDC -DnsName DC2.Reskit.Org
# Restart the DHCP Service on DC1, DC2
$SB2 = {
  Restart-Service -Name DHCPServer –Force
}
Invoke-Command –Computer DC1 –ScriptBlock $SB2
Invoke-Command –Computer DC2 –ScriptBlock $SB2


2. Create a DHCP Scope

# Add new DHCP Scope to DC1
$SHT = @{
  Name = 'Reskit'
  StartRange   = '10.10.10.150'
  EndRange     = '10.10.10.199'
  SubnetMask   = '255.255.255.0'
  ComputerName = 'DC1.Reskit.Org'
}
Add-DhcpServerV4Scope @SHT

3. Configure DHCP Server Options

# Set DHCP V4 Server Option Values
$OHT = @{
  ComputerName = 'DC1.Reskit.Org'
  DnsDomain    = 'Reskit.Org'
  DnsServer    = '10.10.10.10'
}
Set-DhcpServerV4OptionValue @OHT

4. Configure Fail Over/Load Balancing between DC1 and DC2

$FHT = @{
  ComputerName       = 'DC1.Reskit.Org'
  PartnerServer      = 'DC2.Reskit.Org'
  Name               = 'DC1-DC2'
  ScopeID            = '10.10.10.0'
  LoadBalancePercent = 60
  SharedSecret       = 'j3RryIsG0d!'
  Force              = $true
}
Add-DhcpServerv4Failover @FHT

After you complete these two steps, both DC1 and DC2 are able to satisfy DHCP configuration requests on the 10.10.10/0 subnet, can provide some options to DHCP clients along with offered IP address and the subnet mask, and is in a fail over/load balancing relationship.

Monday, March 04, 2019

It's Done! So now what??

Last week, I completed work on a new PowerShell book. The book is entitled Windows Server 2019 Automation with PowerShell. The front cover looks like this:

One of the more interesting features the more eagle-eyed viewers might notice is that Jeffrey Snover wrote a forward. I've known Jeffrey for a very long time and admire him enormously. The draft of his foreword reads like this:


Of course, I love it and am immensely flattered. I just hope the book lives up to his introduction.

The book contains over 120 'recipes' - small scripts that do useful things. I have tried to strip out some of the overhead normally associated with production scripting (error handling, logging, firewalls, etc.) to concentrate on the essence of the various features and how to manage them with PowerShell. The scripts are also published on Github at: https://github.com/doctordns/PowerShellCookBook2019. 

I learned a lot in the process of writing this book. It was fun delving down into various aspects of Windows Server 2019 and playing with various features. Being able to log performance data then graph it all in a few lines of code was neat.

A major resource in writing this latest book was https://docs.microsoft.com. This is Microsoft's (largely) open source documentation platform. The documents are, in the main, of an excellent standard. And when I found a page that has an error or was unclear - I just fixed it. Highly satisfying to improve the documentation.

The book is now available on Amazon: https://smile.amazon.com/Windows-Server-Automation-PowerShell-Cookbook/dp/1789808537/ref=sr_1_3?ie=UTF8&qid=1551353410&sr=8-3

Enjoy!!

Saturday, December 15, 2018

Calling a PowerShell Function like a Method is a Bad Idea

As a frequent contributor to Spiceworks’s PowerShell forum (come visit at: https://community.spiceworks.com/programming/powershell), from time to time I see a post that contains bad practice and sometimes worse.  One thing I see more often than I’d like, is post that calls a PowerShell function like a .NET method. This is, as the subject to this article suggests, is a very bad idea. Let me explain why.

First, let’s create a really simple function Add, like this:

Function ADD {
   Param ($A, $B)
   $A + $B
}

This function just adds two values and returns the results. Now, go and use the function both as a proper function call, then as a method call:

Psh[C:\foo]> Add 1 2
3

Psh[C:\foo]> Add(1,2)
1
2

As Iyou can see, the two uses of the Add function return decidedly different results. But why? At first sight, it appears quite illogical. To work out what is going on, under the covers,  create a richer Add function that describes what is being passed to and from the function, like this:

Function WhatAdd {
   Param ($A, $B)
   # Set to avoid error if GetType fails
   $ErrorActionPreference = 'SilentlyContinue'
   # Display the type and value of the first parameter
   "A is type   : [$($A.GetType())]"
   "A has value : [$A]"
   # Display the type and value of the second parameter
   "B is type   : [$($B.GetType())]"
   "B has value : [$B]"
   # Now calculate and describe the sum:
   $Result = $A + $B
   "Result has type  : [$($Result.GetType())]"
   "Result has value : [$Result]"
   # Return the result
   Return $Result
}

This function performs the same calculations and returns a results. It also displays the type and value of each input parameter and the result.  First try it out with calling a function like a cmdlet:

Psh[Cookham24:C:\foo]> WhatAdd 1 2
A is type   : [int]
A has value : [1]
B is type   : [int]
B has value : [2]
Result has type  : [int]
Result has value : [3]
3

Next, try calling Add like a method:

Psh[Cookham24:C:\foo]> WhatAdd(1,2)
A is type   : [System.Object[]]
A has value : [1 2]
B is type   : []
B has value : []
Result has type  : [System.Object[]]
Result has value : [1 2 ]
1
2

As you can see, the results of calling a function like a method are not what you might have expected.  What is happening is that when you call the Add function like a method, PowerShell assumes that the stuff inside the parentheses is an array of values that is passed to the first parameter of the function, not two separate parameter values. This means PowerShell passes an untyped array of two numbers as the value of the first parameter, and Null as the value of the second. The result of Adding Null to an array of values is an just an array of values (the input values) and not the addition of anything.

Of course, had you indicated in the function’s parameter block of the exact type of the parameters, PowerShell would have indicated the mismatch (passing an array into an parameter with a type of Int.

So two best practices in a single article.

  • First, avoid calling functions like a method. The results can be very much not what you were expecting or wanting.  As a user of someone else’s function, you don;t know how well, or badly they wrote it. Just call a function like a cmdlet.
  • Second, if you are writing functions, assume ALL user input is evil until you prove other wise and that your user will pass you data you were not expecting (like an array not a single integer). At a minimum, ensure that ALL parameters in your functions are explicitly typed. And consider ensuring that what you return is properly typed.  It all cuts down on bad input ruining your day.

Saturday, October 27, 2018

There's always a way...

I teach my classes that there's always more than one way to do almost anything. This week was a nice reminder of how true that is.

The client had a legacy script that scans their client's AD attached computers. One small thing this script does is to determine the Domain Functional Level and whether the domain is still in mixed mode.

The 'old' script we were looking at was a very long, comment-free VBS script using LDAP to get the Domain Object in the AD to get the values for those two attributes to determine the domain level. You might think you could convert that horrid VB code into a simple Get-ADDomain, pick out the two properties.

Sadly, that does not work. The Get-ADDomain cmdlet returns a number of properties that represent the attribute values inside the AD.  But not all!

Here is a screenshot of LDP.EXE showing the domain object and two properties inside my AD:



As you can see, the domain object has two attributes/properties (msDS-Behavior-Version and ntMixedDomain. But Get-ADDomain does not return those properties, as you can see here:


The solution, however, is pretty easy. Get-ADDomain returns an object with the Distinguished Name of the domainDNS object. That object represents the domain inside the AD and it is that object which has the needed properties. So, use Get-ADDomain to get the Domain object's Distinguished Name (DN), then use Get-ADObject to get the object whose identity is that DN. It looks like this:



An interesting thing is that some AD objects, like the domainDNS object, have attribute names that contain a hyphen, eg the msDS-Behavior-Version attribute. This can confuse PowersShell's parser. So the solution is to enclose the property names in braces, as you can see in the final line of the snippet above. An obscure feature of PowerShell that in this case is useful.

All in all, I'd like to think Jimmy Andersen would be impressed. Well, a bit anyway!




Thursday, October 04, 2018

Installing RSAT tools

I've been installing 1803 (and for reasons I can not explain, 1709)  on a bunch of workstations - but needed the RSAT tools. I HATE having to use the GUI - so here's a PowerShell script:

# Install-RSATTools.PS1 # Thomas Lee - doctordns@gmail.com # 1. Get Windows Client Version and Hardware platform $Key = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' $CliVer = (Get-ItemProperty -Path $Key).ReleaseId $Platform = $ENV:PROCESSOR_ARCHITECTURE "Windows Client Version : $CliVer" "Hardware Platform : $Platform" # 2. Create URL for download file # NB: only works with 1709 and 1803. $LP1 = 'https://download.microsoft.com/download/1/D/8/'+ '1D8B5022-5477-4B9A-8104-6A71FF9D98AB/' $Lp180364 = 'WindowsTH-RSAT_WS_1803-x64.msu' $Lp170964 = 'WindowsTH-RSAT_WS_1709-x64.msu' $Lp180332 = 'WindowsTH-RSAT_WS_1803-x86.msu' $Lp170932 = 'WindowsTH-RSAT_WS_1709-x86.msu' If ($CliVer -eq 1803 -and $Platform -eq 'AMD64') { $DLPath = $Lp1 + $lp180364} ELSEIf ($CliVer -eq 1709 -and $Platform -eq 'AMD64') { $DLPath = $Lp1 + $lp170964} ElseIf ($CliVer -eq 1803 -and $Platform -eq 'X86') { $DLPath = $Lp1 + $lp180332} ElseIf ($CliVer -eq 1709 -and $platform -eq 'x86') { $DLPath = $Lp1 + $lp170932} Else {"Version $cliver - unknown"; return} # 3. Display the download details "RSAT MSU file to be downloaded:" $DLPath # 4. Use BITS to download the file $DLFile = 'C:\foo\Rsat.msu' Start-BitsTransfer -Source $DLPath -Destination $DLFile # 5. Check Authenticode signature $Authenticatefile = Get-AuthenticodeSignature $DLFile If ($Authenticatefile.status -NE "Valid") {'File downloaded fails Authenticode check'} Else {'Downloaded file passes Authenticode check'} # 6. Install the RSAT tools $WusaArguments = $DLFile + " /quiet" 'Installing RSAT for Windows 10 - Please Wait...' $Path = 'C:\Windows\System32\wusa.exe' Start-Process -FilePath $Path -ArgumentList $WusaArguments -Wait # 7. Get RSAT Modules Get-Module -Listavailable | Where-Object Name -like '*rsat*'

Friday, August 03, 2018

PSReadline V2 Can Break PowerShell Profile Scripts in Latest Insider's RS5 Preview

The Windows Insider programme provides access to the latest pre-release versions of Windows 10 and Windows Server 2019. Earlier this week, Microsoft released a new preview of what is coming with Windows 10 RS5. This build, 17728.1000, contains an updated version of the module PSReadLine. PowerShell uses this module to enable you to, inter alia, set the colours of tokens in the PowerShell command console.

I love this feature, as it allows me to override the default colours that PowerShell uses. One particular issue for me is that the colours can look less good when displaying on some of the lower quality projectors I get stuck with!  One example - the parameter name token is set, by default, to gray. Displaying from my laptop, this has been hard to see at some sites. So I just added this line to my profile:
Set-PSReadlineOption -TokenKind Parameter -ForegroundColor Cyan
Simple and this is highly useful for me ainsome places I teach. However, with the latest release of the RS5 preview, Microsoft has  now included an updated version of PSReadline (2.0 vs 1.2). Taking the new version was done to enable a fix to an accessibility issue. If you run PowerShell after the upgrade, you might see this error when you run PowerShell:


This is both a breaking change and one not described in the release notes. And, if you use Bing or Google to search for Set-PSReadLine, the page at docs.microsoft.com shows the old version of the cmdlet - you need to dive into the actual PSReadLine repository (ie the modules 'source' code!) to discover the answer.

The solution is simple - change that line to:
Set-PSReadLineOption -Colors @{"parameter" = [ConsoleColor]::Cyan}
I've opened issues at docs.microsoft.com, the PsRadline repo and with the Insider's programme, so hopefully going forward this issue is no longer an issue.


Friday, July 20, 2018

My Book Scripts

I saw a comment over on Amazon relating to my last PowerShell book (see Amazon Book Listing) from Long Time Programmer. His comment is that he couldn’t download the code. I wish there was a way I could comment on his comment as there is a way. I’ve put all my scripts up on GitHub!  If you go to https://github.com/doctordns/PowerShellCookbook you can see the scripts there.  Although I have to say some of them – getting the scripts onto GitHub is a work in progress.

As an aside and for background: the publication process at the time this book was published had a few issues.  The web site used to enter content  was literally eating content so we switched over to word at last hour. In doing so, the longer-line scripts ended up broken in the final PDF and thus the published version.

To be helpful to my readers, I’ve slowly retrieved the scripts from inside the VMs and have published them to GitHub, Which is less simple than it sounds and it not yet complete. At present, I’ve ported over the recipe scripts for chapters 1-4, 8, 10, 11 (7 out  of 13). And I hope to have the updates soon.
Please download any of the scripts – and file issues if you find problems.

[Later]
The conversion has been completed and I've created a version 1.0 of the scripts on the GitHub repository. . You can consume the scripts by navigating the GitHub UI or you can take a fork and clone to a local machine. The release mechanism in GitHub provides a zipped up Go to the GitHub Release page and download a .gz or .zip file depending on your tastes. I may do some further tidying up.