Category Archives: Powershell

Desired State Configuration – A very basic intro

I’ve seen this topic before when reviewing the 70-410 exam books, though for that exam very little is mentioned or indeed required. So until now I’ve given it scant notice. However, having read more about it I can see it will continue to have a growing use in the years ahead, as my requirement to provision multiple standardised servers grows. DSC really looks like it can replace the need for storage-heavy VM templates and multiple GPOs and can quickly deploy standardised servers and maintain the initial settings in the event they are changed by well-meaning, but meddling System Administrators.

As I have only just dipped my toe into the DSC ocean, this post is really as much for my benefit as anyone eases (though I’m always glad to see you of course). It’s just a basic step-through to create a MOF file and apply that to the target. I don’t mention any of the underlying concepts and terminology because there’s a plethora of information out there on Technet and beyond.

Task: I need to ensure a new directory is created on my domain controller (CIV-DC1)

Create the Configuration file:

Configuration AccountingDir {

Import-DscResource –ModuleName ‘PSDesiredStateConfiguration’

Node CIV-DC1 {
#create a new directory in the C drive called accounting

File Accounting { 
Type = “Directory”
Ensure = “Present”
DestinationPath = “C:\Accounting” }

} #Node-complete

} #configuration-complete

#run this to create the MOF file
#the name of the configuration file
AccountingDir -OutputPath c:\temp

#run this to apply the MOF file to the target
Start-DscConfiguration -path C:\temp -Wait -Verbose -Force

NB: The image below will be used during the next section, I used the PowerShell ISE:

DSC-steps

NB: Please note line 3, when I did not have this I got the following error:

DSC-warning

Step 1: Load the Configuration Function Into Memory

Select the Configuration text and run this in ISE

Step 2: Generate the MOF file

Highlight the command (the name of the Configuration and specify a location where therMOF file will be stored) and run this in ISE, you should get the following output:

dsc-step2-output

Step 3: Apply the MOF settings to the target

Highlight the Start-DscConfiguration line, which includes the location of the MOF file (you don’t stipulate the actual MOF file) and run in ISE. The target for the MOF file is stipulated in the first lines of the MOF file so PowerShell and LCM know what the target is. If it is successfully applied you will see the following:

dsc-step3-output

Visually checking on CIV-DC1 shows the new directory:

dsc-results

You can also run a test to confirm if the settings in the MOF file are still active/applied on the target using the Test-DscConfiguration command:

DSC-testing

 

Disclaimer: provided “AS IS” with no warranties and confer no rights

Advertisements

PS Tip: PowerShell Catches

In this example I want to check if a list of users samAccountNames returns a list of matching displaynames. In the event that the user is not found I don’t want a system error displayed, I want a custom error message. This is done using a Try & Catch statement, the Try element being the test and the Catch element what I want the custom error to be.

$users = get-content C:\team-members.txt
Foreach ($user in $users) {
try {
(get-aduser $user).Name
}
catch {“$user not found in AD!” #this text will replace the system error output
}
}

PS Tip: Is Hyper-Threading enabled on my computer

Open an elevated PS session and enter the following:

get-wmiobject -Class win32_processor | ft -Property NumberOfCores, NumberOfLogicalProcessors -auto

If Hyper-threading is enabled you’ll have twice the number of logical processors that you have of physical cores. In my case Hyper-threading is enabled due to the 4-8 ratio.

ht-enabled

 

Disclaimer: provided “AS IS” with no warranties and confer no rights

PS Script: Form to get MAC Address (v1.0)

Description: Enter the name of a computer and get it’s MAC address. The form is still a work in progress, with some of the functions and results needing tightened up and the form is ugly.

IMPORTANT: Remember if you copy and paste this text I often find the format of the quotation marks gets changed so the script becomes corrupt and fails.

Code:

#region start declare .NET FUNCTIONALITY
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Drawing”)
[void] [System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”)
Add-Type -AssemblyName System.Windows.Forms
[System.Reflection.Assembly]::LoadWithPartialName(‘Microsoft.VisualBasic’) | Out-Null
$psexec = ‘\\<computer name>\share$\PSTools\PsExec.exe’
#endregion end declare .NET FUNCTIONALITY

#region FUNCTIONS

#region FUNCTION #0 Get computer MAC from ARP table – not used in this script
Function arpcheck () {
$IP = [System.Net.Dns]::GetHostByName($computer).AddressList[0].IpAddressToString #the IP of the computer I need to get the MAC for.
$mac = arp -a
($mac | ? { $_ -match $ip } ) -match “([0-9A-F]{2}([:-][0-9A-F]{2}){5})” | out-null;

if ( $matches ) {

$matches[0];

} else {

“Not Found”

}
}
#endregion FUNCTION #0 Get computer MAC from ARP table

#region FUNCTION #1 Display Form
Function showform () {
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
}
#endregion FUNCTION #1 Display Form

#region FUNCTION #2 Conversion and Display

function get-mac2 ()
{
$computer = $ECN.text

#test if WinRM is enabled on the remote computer, this script requires WinRM is enabled.
$winrm = test-wsman -ComputerName $computer -ErrorAction SilentlyContinue
if ($winrm)
{

$VPNTC = Test-Connection $computer -Count 1 -ErrorAction SilentlyContinue
$VPNIP = ($VPNTC.IPV4Address).IPAddressToString
If ($VPNIP -match “92.*”){$MAC.text=”ERROR:VPN Reply”} #a test/result if I’m on my VPN and the computer is offline, an active ping response is still returned by the VPN starting with 92.
Else
{
$TC = Test-Connection -count 1 $computer -Quiet
# If the PC is online then get the InterfaceIndex ID of the active NIC
If ($TC -eq “True”){
$result1 = Invoke-Command -ComputerName $computer {(get-wmiobject win32_networkadapter -filter “netconnectionstatus = 2″).InterfaceIndex | out-string}
$result1 = $result1.Trim()
# If multiple NICs are active then this script won’t work – it still a work in progress 🙂
If ($result1.length -lt 3) {
$result2 = Invoke-Command -ComputerName $computer {(gwmi Win32_NetworkAdapter | where {$_.InterfaceIndex -eq $using:result1}).MACAddress}
$MAC.text=$result2
}
Else {$MAC.text=”ERROR:Multi MACs”}
}
Else {$MAC.text=”ERROR:PC Offline”}
}
}
Else {$MAC.text= “WinRM Disabled”}
}
#endregion FUNCTION #2 Conversion and Display

#region FUNCTION #3 Copy to clipboard
Function ClipMe() {
[System.Windows.Forms.Clipboard]::SetText($MAC.Text.Trim())
}
#endregion FUNCTION Copy to clipboard

#endregion FUNCTIONS

#region FORM

#region FORM ELEMENT #1 THE BASIC FORM
$Form = New-Object System.Windows.Forms.Form
$form.text = “MAC Address Finder”
$Form.Size = New-Object System.Drawing.Size(400,300)
$Form.Font = “Segoe UI,12”
$Form.ForeColor = [System.Drawing.Color]::White
$Form.BackColor = [System.Drawing.Color]::LightSteelBlue
#endregion FORM ELEMENT #1 The basic form

#region FORM ELEMENT #2 GROUP BOXES

$groupConvert = New-Object System.Windows.Forms.GroupBox
$groupConvert.Location = New-Object System.Drawing.Size(10,20)
$groupConvert.size = New-Object System.Drawing.Size(360,170)
$groupConvert.text = “Enter Computer Name:”
$Form.Controls.Add($groupConvert)
#endregion FORM ELEMENT #2 group boxes

#region FORM ELEMENT #3 LABELS and TEXTBOXES

$cnlabel = New-Object System.Windows.Forms.Label
$cnlabel.Text = “Enter Computer Name:”
$cnlabel.AutoSize = $True
$cnlabel.Font = “Segoe UI,16”
$cnlabel.Location = “20,20”
$groupConvert.Controls.Add($cnlabel)

$ECN = New-Object System.Windows.Forms.Textbox
$ECN.Size = New-Object System.Drawing.Size(135,100)
$ECN.Font = “Segoe UI,16”
$ECN.Location = “20,60”
$ECN.TextAlign = “Center”
$groupConvert.Controls.Add($ECN)

$malabel = New-Object System.Windows.Forms.Label
$malabel.Text = “MAC Address:”
$malabel.AutoSize = $True
$malabel.Font = “Segoe UI,16”
$malabel.Location = “20,120”
$groupConvert.Controls.Add($malabel)

$MAC = New-Object System.Windows.Forms.Textbox
$MAC.Size = New-Object System.Drawing.Size(185,100)
$MAC.ReadOnly = $true
$MAC.Font = “Segoe UI,16”
$MAC.Location = “170,120”
$MAC.TextAlign = “Center”
$groupConvert.Controls.Add($MAC)

#endregion LABELS and TEXTBOXES

#region FORM ELEMENT #4 BUTTONS

#region BUTTON #1 Create Convert button
$ConvertButton = New-Object System.Windows.Forms.Button
$ConvertButton.Location = New-Object System.Drawing.Size(20,200)
$ConvertButton.Size = New-Object System.Drawing.Size(80,40)
$ConvertButton.Text = “Get MAC”
$ConvertButton.Font = “Segoe UI,12″
$ConvertButton.BackColor = [System.Drawing.Color]::Green
$ConvertButton.Enabled = $True
$ConvertButton.Add_Click(
{
$MAC.text=”Pending…”
get-mac2
$Closebutton.Text = “Exit”
$buttonCopyToClipboard.Enabled = $true
})
$Form.Controls.Add($ConvertButton)
#endregion BUTTON #1 Create Convert button

#region BUTTON #2 Create CANCEL button
$Closebutton = New-Object System.Windows.Forms.button
$Closebutton.Location = New-Object System.Drawing.Size(280,200)
$Closebutton.Size = New-Object System.Drawing.Size(80,40)
$Closebutton.Text = “Cancel”
$Closebutton.Add_Click(
{
$form.Close()
})
$Form.Controls.Add($Closebutton)
#endregion BUTTON #2 Create CANCEL button

#region BUTTON #3 Create CLIPBOARD button
$buttonCopyToClipboard = New-Object System.Windows.Forms.Button
$buttonCopyToClipboard.Location = New-Object System.Drawing.Size(110,200)
$buttonCopyToClipboard.Size = New-Object System.Drawing.Size(160,40)
$buttonCopyToClipboard.Text = “Copy to clipboard”
#$buttonCopyToClipboard.Font = “Segoe UI,12”
$buttonCopyToClipboard.Enabled = $False
$buttonCopyToClipboard.Add_Click(
{
#call the function Clipme to copy the MAC address to the system clipboard
ClipMe
})
$Form.Controls.Add($buttonCopyToClipboard)
#endregion BUTTON #3 Create CLIPBOARD button

#region BUTTON #4 Create Clear button
$Clearbutton = New-Object System.Windows.Forms.button
$Clearbutton.Location = New-Object System.Drawing.Size(180,59)
$Clearbutton.Size = New-Object System.Drawing.Size(80,40)
$Clearbutton.Text = “Clear”
$Clearbutton.Add_Click(
{
$buttonCopyToClipboard.Enabled = $False
#clears the text from both fields
$ECN.text=””
$MAC.text=””
})
$groupConvert.Controls.Add($Clearbutton)
#endregion BUTTON #4 Create CLEAR button

#endregion BUTTONS

#endregion FORM

#region SHOW FORM
showform
#endregion SHOW FORM

PS Tip – Use local variable in Invoke-Command (remote computer)

I have a locally declared variable $var1

Using it directly doesn’t work, the value of the variable is not carried over within the invoke-command scriptblock.

$result2 = Invoke-Command -ComputerName $computer {(gwmi Win32_NetworkAdapter | where {$_.InterfaceIndex -eq $var1}).MACAddress}

Instead you have to use the “Using” scope modifier followed by a colon, thus $var1 becomes $using:var1

$result2 = Invoke-Command -ComputerName $computer {(gwmi Win32_NetworkAdapter | where {$_.InterfaceIndex -eq $using:var1}).MACAddress}

PS Function – Copy contents of form textbox to clipboard

#region FUNCTION #3 Copy to clipboard
Function ClipMe() {
[System.Windows.Forms.Clipboard]::SetText($ST.Text.Trim())
}
#endregion FUNCTION Copy to clipboard

NB: In this example my form has a textbox called $ST – The function captures the text content and trims it to remove any unwanted white space.

$ST = New-Object System.Windows.Forms.Textbox
$ST.Size = New-Object System.Drawing.Size(135,100)
$ST.ReadOnly = $true
$ST.Font = “Segoe UI,16”
$ST.Location = “150,120”
$ST.TextAlign = “Center”
$groupConvert.Controls.Add($ST)