Scripting Game puzzle – – January 2016
Here’s how I’d solve the puzzle
function get-starttime {
[CmdletBinding()]
param(
[parameter(
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[Alias('CN', 'Computer')]
[ValidateNotNullOrEmpty()]
[string[]]$computername = $env:COMPUTERNAME
)
PROCESS {
foreach ($computer in $computername){
$props = [ordered]@{
ComputerName = $computer
StartTime = ''
'UpTime (Days)' = 0.0
Status = 'OFFLINE'
}
if (Test-WSMan -ComputerName $computer -ErrorAction SilentlyContinue) {
$lbt = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computer -ErrorAction SilentlyContinue
if ($lbt) {
$props['StartTime'] = $lbt.LastBootUpTime
$upt = [math]::round(((Get-Date) - $lbt.LastBootUpTime).TotalDays, 1)
$props['UpTime (Days)'] = $upt
$props['Status'] = 'OK'
}
else {
$props['Status'] = 'ERROR'
}
} ## endif
New-Object -TypeName PSObject -Property $props
} ## end foreach
} ## end PROCESS
}
Create an advanced function. Yes I know I’ve used lower case for the function name. I always do to visually separate my code from cmdlets and other functions.
Use the [parameter] decorator to enable pipeline input. Only a single parameter so don’t need to bother woth positional parameters. Function is supposed to default to local machien so can’t make parameter mandatory.
Requirement to process multiple computers at once presumably means the computername parameter has to take an array – sumultaneous processing implies a work flow which negates the initial requirement to create a function
Use the PROCESS block to run a foreach loop that iterates over the collection of computernames.
Create a hash table for the results – I’ve used an ordered hash table to preserve the property order. Set the values to a failed connection.
use Test-Wsman to see if can reach the computer. If can’t the output object is created. If you can reach the machine then run Get-CimInstance - preferred over Get-WmiObject because it returns the date ready formatted
Assuming that works set the start time and status properties. Calculate the uptime in days. I’d prefer to see just an integer here – tenths of days doesn’t mean anything to most people
If the call to Get-CimInstance fails then set the status to ERROR
Output the object.
The requirement to add a proeprty for patching is not clear but I’m assuming it means if the machine has been up for more than 30 days with the 1/10 month as a typo
if you want to add that then
Add a property
MightNeedPatching = $false
to the hash table when you create it
and add this line
if ($upt -ge 30){$props['MightNeedPatching'] = $true}
after
$upt = [math]::round(((Get-Date) - $lbt.LastBootUpTime).TotalDays, 1)
$props['UpTime (Days)'] = $upt
PowerShell Deal of the Day – – 31 January 2016
PowerShell in Action, Third Edition is Manning’s Deal of the Day Sunday 31 January 2016
Deal of the Day January 31: Half off Windows PowerShell in Action, Third Edition. Use code dotd013116au at https://www.manning.com/books/windows-powershell-in-action-third-edition
PowerShell in Depth, Second Edition is also available as part of the deal
Deal of the Day January 31: Half off my book PowerShell in Depth, Second Edition. Use code dotd013116au at https://www.manning.com/books/powershell-in-depth-second-edition
The deal will go live at Midnight US ET and will stay active for about 48 hours to account for time zones.
PowerShell editing options
I’ve used the ISE since it first appeared in PowerShell 2.0 but there are a couple of recent annocements that increase your code editing options
ISE previews will become available out of band rather than being tied to WMF/OS releases
The preview ISE is a module you can download from the PowerShell gallery and runs side-by-side with your existing version of ISE
Currently this is a PowerShell 5.0 only option
The current preview hasn’t changed much – its just to test the delivery mechanism. Look for updates approximately monthly.
The second interesting editing option is Visual Studio Code which has PowerShell support
as well as a host of other languages. if you have to work across multiple languages this may be an option for you
Alternativley, if you use Visual Studio a lot you have the PowerShell Tools for Visual Studio option
Which filter
Get-ADUser has 2 filter parameters.
The –Filter takes a PowerShell syntax filter e.g.
Get-ADUser -Filter {Name -eq 'Richard'}
The –LDAPfilter takes an LDAP search filter e.g.
Get-ADUser -LDAPFilter "(Name=Richard)"
Mixing them up will ensure you don’t get the results you want
Rescuing IE favourites
I received the new Windows Insider Windows 10 build over the wekend and have just discovered that installing it wiped out my IE favourites – or at least those in folders.
I’d copied my favourites to Microsoft Edge when installing Windows 10 so I can copy everything back
IE favourites are stored at
Get-ChildItem -Path 'C:\Users\<user>\Favorites'
You can clean out the favourites:
Get-ChildItem -Path 'C:\Users\<user>\Favorites' | Remove-Item –Force
You will be asked to confirm the action.
Microsft Edge favourites are at
Get-ChildItem -Path 'C:\Users\<user>\AppData\Local\Packages\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\AC\MicrosoftEdge\
User\Default\Favorites' –Recurse
Copy them into the IE favourites folder
Get-ChildItem -Path 'C:\Users\<user>\AppData\Local\Packages\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\AC\MicrosoftEdge\
User\Default\Favorites' | Copy-Item -Destination 'C:\Users\<user>\Favorites' -Force -Recurse
Job done
FQDN
How do you find the FQDN of the machine you’re using.
The simplest way is to combine a couple of environmental variables:
PS> "$env:COMPUTERNAME.$env:USERDNSDOMAIN"
SERVER02.MANTICORE.ORG
If you like using CIM (and who doesn’t) you can try this
PS> Get-CimInstance -ClassName Win32_ComputerSystem |
>> select @{N='FQDN'; E={"$($_.DNSHostName).$($_.Domain)"}}
>>
FQDN
----
server02.Manticore.org
This could easily be used for remote machines as well by adding the –ComputerName parameter to Get-CimInstance
If you want to go down the .NET route you have:
PS> [System.Net.Dns]::GetHostByName('').HostName
server02.Manticore.org
CDXML filter parameters
I was recently asked about adding a filter parameter to a cmdlet created through CDXML. If you’ve not seen it before (see PowerShell and WMI Chapters 18 & 19 from www.manning.com) CDXML allows you to creat ecmdlets by wrapping a WMI class in some simple XML.
The resultaing CDXML (Cmdlet Definition XML) is thn published as a module. Here’s a simple example using the Win32_NetworkAdapterConfiguration class
<?xml version='1.0' encoding='utf-8'?>
<PowerShellMetadata xmlns='http://schemas.microsoft.com/cmdlets-over-objects/2009/11'>
<Class ClassName='ROOT\cimv2\Win32_NetworkAdapterConfiguration'>
<Version>1.0</Version>
<DefaultNoun>NetworkAdapterConfiguration</DefaultNoun>
<InstanceCmdlets>
<GetCmdletParameters DefaultCmdletParameterSet='DefaultSet'>
</GetCmdletParameters>
</InstanceCmdlets>
</Class>
</PowerShellMetadata>
The first 2 lines are boilerplate. The NameSpace and WMI class are defined on line 3, follwoed by a version number (arbitary) and a default noun for you cmdlet to use. Instance cmdlets defines how you’ll pull the data for existing instances of the class – in other words the Get-NetworkAdapterConfiguration cmdlet.
Save as a CDXML file and import as a module
Import-Module .\NetworkAdapterConfiguration.cdxml
Get-Module will sjow it as a Cim module with a single exported command. Use it like any other cmdlet
PS> Get-NetworkAdapterConfiguration | ft -a
ServiceName DHCPEnabled Index Description
----------- ----------- ----- -----------
kdnic True 0 Microsoft Kernel Debug Network Adapter
mwlu97w8 True 1 Marvell AVASTAR Wireless Composite Device
msu64w8 False 2 Surface Ethernet Adapter
mwlu97w8 True 3 Marvell AVASTAR 350N Wireless Network Controller
RFCOMM False 4 Bluetooth Device (RFCOMM Protocol TDI)
BthPan True 5 Bluetooth Device (Personal Area Network)
vwifimp True 6 Microsoft Wi-Fi Direct Virtual Adapter
vwifimp True 7 Microsoft Wi-Fi Direct Virtual Adapter
RasSstp False 8 WAN Miniport (SSTP)
RasAgileVpn False 9 WAN Miniport (IKEv2)
Rasl2tp False 10 WAN Miniport (L2TP)
PptpMiniport False 11 WAN Miniport (PPTP)
RasPppoe False 12 WAN Miniport (PPPOE)
NdisWan False 13 WAN Miniport (IP)
NdisWan False 14 WAN Miniport (IPv6)
NdisWan False 15 WAN Miniport (Network Monitor)
Using the cmdlet is equivalent to
Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration
but is easier and requires less typing.
Very often you’ll want to pick a specific adapter – for instance
Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter 'Index=3'
You can implement the same kind of filters using CDXML. You add a queryable properties section as shown below:
<?xml version='1.0' encoding='utf-8'?>
<PowerShellMetadata xmlns='http://schemas.microsoft.com/cmdlets-over-objects/2009/11'>
<Class ClassName='ROOT\cimv2\Win32_NetworkAdapterConfiguration'>
<Version>1.0</Version>
<DefaultNoun>NetworkAdapterConfiguration</DefaultNoun>
<InstanceCmdlets>
<GetCmdletParameters DefaultCmdletParameterSet='DefaultSet'>
<QueryableProperties>
<Property PropertyName='Index'>
<Type PSType ='UInt32'/>
<RegularQuery AllowGlobbing='true'>
<CmdletParameterMetadata PSName='Index' ValueFromPipelineByPropertyName='true' CmdletParameterSets='DefaultSet' />
</RegularQuery>
</Property>
</QueryableProperties>
</GetCmdletParameters>
</InstanceCmdlets>
</Class>
</PowerShellMetadata>
Set the paraemter name – same as property to use here – and the type (unsigned integer). Decide whether pipeline input and wildcards (globbing) are allowed and save the file.
Re-import the module (use the Force) and your new parameter is available
Get-NetworkAdapterConfiguration -Index 3
Its important to understand CDXMLMI – even if you never create a CDXML module – because 2/3 of the cmdlets in Windows Server 2012 and later are created this way.
WMF 4.0 Updates available downlevel
The WMF 4.0 Updates that were released in November 2014 for Windows 8.1 and Windows Server 2012 R2 are now available for:
Windows Server 2012
Windows Server 2008 R2 SP1
Windows 7 SP1
You need WMF 4.0 installed to install the update
Setting external time source in AD
The PDC emaulator in the root domain of your AD forest should point to an external time source. For some odd reason the PDC emulator in my lab wasn’t doing that. Easily remedied:
##
## set external time source
## set server type to NTP
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters -Name Type -Value 'NTP'
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config -Name AnnounceFlags -Value 5
## Enable NTP server
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer -Name Enabled -Value 1
## Specify Time source
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters -Name NtpServer -Value 'time.windows.com,0x1'
## Set poll interval in seconds - every 30 minutes
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient -Name SpecialPollInterval -Value 1800
## set max +/- time corrections in seconds - 24 hours
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config -Name MaxPosPhaseCorrection -Value 86400
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config -Name MaxnegPhaseCorrection -Value 86400
Stop-Service -Name W32Time
Start-Service -Name W32Time
Wait a minute or so and the time will be set correctly.
Not a job you have to do very often but having the code to do the job reduces the probability of errors
You can view the settings
Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Parameters
Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\Config
Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer
Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpClient
WMF 5.0
If you follow the PowerShell Team blog - http://blogs.msdn.com/b/powershell/ (and if you don’t you should) you’ll know that the WMF 5.0 RTM downloads were pulled just before Christmas. This was due to a bug that reset the module environment.
A comment on the blog - http://blogs.msdn.com/b/powershell/archive/2015/12/23/windows-management-framework-wmf-5-0-currently-removed-from-download-center.aspx – from the team indicates that it’s likely to be a few weeks before the bug is resolved and WMF 5.0 is available again for download.