A little bit about me
All of early career spent in the Windows world doing automation using DOS, then VBScript and eventually Powershell
For the most part worked in the build, source control and deployment automation space – developer enablement.
Windows apps very GUI focused but hard to automate and manage at scale. Proliferation of scripting tools - VBS, DOS, bespoke CLI tooling, sysprepping servers was labour-intensive and error-prone. We had to write a lot of code to make sure our customers’ machines were ready to have software installed on them. Similarly our QA teams wasted hours building test images.
Off the shelf 'solutions’ typically costly. No consistency of CLI or API between different vendors, assuming they even offer any kind of automation option. High TCO vs Unix and its CLI apps.
Windows has no built-in remoting capability. It was either RDP (which isn't automatable in a remote session other than via gui drivers) or PSExec. No equivalent of SSH. MS realised the limitation here, even for their own internal product development teams, as they couldn't do agile without being able to automate all of their testing. Exchange admin console for example, although it's a GUI, under the hood it uses PowerShell so everything is properly tested.
It's a Shell - command shell for 1 off commands, history, pipelines. However, the built in commands are not discrete executables that fork processes, but functional objects called cmdlets (command-lets) that run in-proc.
Core objects are written in C# - either built in in the shell or developed by vendors and released as PowerShell modules that can be loaded on demand.
If a vendor or developer hasn't written cmdlets, you can even instantiate .net classes directly and script them yourself. You can also create your own cmdlets and modules in powershell code if you don't want to write C# or VB.Net.
It's a scripting language - owes a massive debt to Bash. Less resemblance to other high level scripting languages like VBS, Python and Ruby
Cmdlets (command-lets) are essentially built in powershell functions named as <verb>-<noun>, such as Get-ChildItem (for enumerating a tree, such as a Directory) or Get-Content (equivalent of cat, for printing out a file). Typically only a limited range of verbs. The Noun part is always singular, even if it returns a collection. Helps avoid ambiguity. Built in cmdlets typically have a number of aliases to make it easier for people adapt to using it. So gci and dir map are both aliases of get-childitem.
If like me you can’t remember the name of the command, you can use a process of elimination using get-command and filters, like get-command –noun JSON*
You can write your own cmdlets and package them as re-usable modules or libraries. To avoid naming collision, provide your own noun prefix to make it easier to identify your own cmdlets. AWS don’t do this (tut tut)
Built in documentation and tab-completion. Even if you haven't written any docstrings or comment blocks, any cmdlets you write will support tab completion and basic docs.
Unlike unix shells the output of cmdlets isn't just a raw strings or numbers that you have to wrangle with cut, grep, sed and awk. When a cmdlet returns something, what you get is an object, or a stream / collection of objects. So get-childitem doesn't give you a listing, but a collection of file system objects. Because they are objects, you can inspect their properties and invoke their methods using get-member
Pipes and filters. Any bulk operation like listing objects in the file system, reading lines from a file or machines running in the cloud will yield a collection. Rather than storing the results in memory you can use pipelining to process the results one at a time, discarding things you don't care about and performing deeper processing or transformation on the rest and even generate new output objects. Built-ins for reading and writing common textual serialization formats - XML, CSV and JSON.
Although the output looks like a table or list (not unlike a docker PS output, for example)What's actually going on under the hood is that every command in the shell terminates in an implicit formatting object, which is predefined for most built in object types with the default properties to render. If you want to override this, just explicitly output to table, list or a custom format, telling it which properties of the object you want to include in the output. If an object has many properties, you can shave off the ones you don't care about and pass down the pipeline.
Let’s put this together and run a query to get some JSON data from a web service and pull some specific data off it.
In this case we’re going to interrogate stack exchange for powershell topics
Once we have the raw request returning data, we can drill in and identify specific data fields, in this case just the Question title and the link
All we’re doing here is querying the AWS London AMI store for base Windows machines, sorting them by date and selecting the most recent one.
Windows machines are not generally enabled for allowing inbound connections other than over RDP.
Fortunately there are some pretty solid scripts that you can include in your user data to allow the system to configure the WSMan services and install SSL certificates locally so that the traffic can be properly encrypted.
Once that’s done it’s a pretty straightforward move to connect to the remote Windows machine and run some commands in the remote session.
All we’re doing here is connecting to a remote AWS instance and interrogating the instance metadata, but of course there is no limit to what you can do. It’s worth pointing out that this is the same mechanism that Ansible uses for configuring Windows machines.
Because the client isn’t domain-joined we have to use basic authentication, though it’s over an SSL channel.