Managing Windows Defender / System Center Endpoint Security with PowerShell

I just read a blog post from Ed Wilson (Scripting Guy) about Use PowerShell to Configure Windows Defender Preferences and wondered if there’s more here. And yes there is. If you have a default insallation of Windows 8 and have defender enabled or work in an enterprise environment and use Configuration Manager with the  System Center Endpoint Security agent deployed on your clients then you the below listed cmdlets available.

Windows Defender

To get a list of all available Defender cmdlets just run the following command within a powershell console

Get-command -Module defender

System Center Endpoint Protection

For a list of all available SCEP cmdlets, run the following command within a powershell console.

Get-command -Module MpProvider

If no cmdlets are returned try first loading the module using the following command
Import-Module “$env:ProgramFiles\Microsoft Security Client\MpProvider”

You will notice that the cmdlet names are quite similar, the only difference is that the cmdlets for SCEP have “Prot” within the name.

Windows Defender System Center Endpoint Protection
Cmdlet ModuleName Cmdlet ModuleName
Add-MpPreference Defender Add-MProtPreference MpProvider
Get-MpComputerStatus Defender Get-MProtComputerStatus MpProvider
Get-MpPreference Defender Get-MProtPreference MpProvider
Get-MpThreat Defender Get-MProtThreat MpProvider
Get-MpThreatCatalog Defender Get-MProtThreatCatalog MpProvider
Get-MpThreatDetection Defender Get-MProtThreatDetection MpProvider
Remove-MpPreference Defender Remove-MProtPreference MpProvider
Remove-MpThreat Defender Remove-MProtThreat MpProvider
Set-MpPreference Defender Set-MProtPreference MpProvider
Start-MpScan Defender Start-MProtScan MpProvider
Update-MpSignature Defender Update-MProtSignature MpProvider

So what can we do here?

Update definitions

Antivirus and Spyware definitions can be updates as following:

Update-MProtSignature -UpdateSource MicrosoftUpdateServer

Starting a Scan

To start a scan use the following command. Available Scantypes are QuickScan, FullScan and CustomScan)

Start-MProtScan -ScanType QuickScan

When using the CustomScan option an the path must be provied using the -Scanpath parameter

Computer Protection Status

Computer protection status information is retrieved with the following command

Get-MpComputerStatus

Defender / SCEP Settings

Configuration settings can be gathered using

Get-MProtPreference

Find information about actual threat

To find out information about an actual threat on a client, run

Get-MProtThreat

2014-04-08_15h06_33

Removing Threats

Although there is a Remove-MProtThreat cmdlet, it doesn’t seem to recognize the active threat, as i received the following message when executing it.

2014-04-08_15h13_13

Configuration Changes

For configuratin settings, please refer to Ed Wilson’s blog post Use PowerShell to Configure Windows Defender Preferences

That’s it for today, now it has stopped raining and the sun starts to shine, so let’s get out of here Smile

How to export third-party driver packages using PowerShell

Windows 8.1 Update introduces a new cmdlet that allows you to export third-party drivers that are located within the driver store of a Windows client.

$ExpDrv = Export-WindowsDriver -Online -Destination c:\temp\3rdpartydrivers 

The result, all drivers exported into the provided destination directory

2014-04-04_21h36_47

Now we have a whole bunch of folders, but what drivers did we actually export?

$ExpDrv | Select-Object ClassName, ProviderName, Date, Version | Sort-Object ClassName

2014-04-04_21h40_00

For more information read the What’s new in DISM article here

Good to know: System Center 2012 Configuration Pack for Microsoft User Experience Virtualization

Based on a conversation I had yesterday at the ConfigMgr Community event here , it appears that few people know about the existance of the ConfigMgr pack for Microsoft UE-V. There’s one for UE-V version 1.0 and just a few weeks ago one for UE-V 2.0 was released.

After Microsoft User Experience Virtualization (UE-V) and its required components are installed, UE-V must be configured. This UE-V Configuration Pack provides a way for administrators to use the Compliance Settings feature of System Center Configuration Manager 2012 SP1 to apply consistent configuration across sites where UE-V is installed.

The UE-V Configuration Pack for UE-V 2.0 provides tools to do the following:

The UE-V Configuration Pack provides tools to do the following:
1.    Create UE-V template distribution baselines.
a.    Defines UE-V templates to be registered or unregistered
b.    Updates UE-V template configuration items and baselines as templates are added or updated.
c.    Distribute and register UE-V templates using standard Configuration Item remediation

2.    Create an Agent policy configuration item to manage the following settings:
a.    Max package size
b.    Setting import delay
c.    Settings import notification
d.    Settings storage path
e.    Sync enablement
f.    Sync method
g.    Sync timeout
h.    Enable/disable Windows 8 app sync
i.    Sync unlisted Windows 8 apps
j.    IT contact URL
k.    IT contact descriptive text
l.    Tray icon enabled
m.    First use notification
n.    Wait for sync on application start
o.    Wait for sync on logon
p.    Wait for sync timeout
q.    Settings template catalog path
r.    Start/Stop UE-V agent service
s.    Define which Windows 8 apps will roam settings

PowerShell – Finding ConfigMgr Collections and Members

The below script provides a simple and quick method to find ConfigMgr Collections and its members. The script has a -Name parameter that accepts the exact or part of the collection name. Next all collections that match the name are listed. After selecting a collection, its members are listed.

Function Get-CMColContent()
{
<#
.Synopsis
   Get Configuration Manager Collections and Members
.DESCRIPTION
   This script provides an interactive way to find collections and collection members within 
   Configuration Manager. 
.PARAMETER Name
   The exact or partial collection name. 
.EXAMPLE
   Get-CMColContent -Name All
#>


 Param(
    [Parameter(Mandatory=$true,
    ValueFromPipelineByPropertyName=$true,
    HelpMessage="Enter Collection Name or part of collection Name",
    Position=0)]
    [String]$Name
    )

# Change Site Server Name and Site code so it fits your environment
[string] $SiteServer = "servername"
[string] $SiteCode = "010"
[string] $Namespace = "root\SMS\site_$SiteCode"

$CollectionItem = Get-WmiObject -Namespace $Namespace -ComputerName $SiteServer -Query "SELECT Name,LimitToCollectionName,MemberClassName, MemberCount, CollectionType  FROM SMS_Collection WHERE Name LIKE '%$Name%'" | Select-Object Name,LimitToCollectionName,MemberClassName, MemberCount, CollectionType, ResourceID | Sort-Object CollectionType| Out-GridView -Title "Collections" -OutputMode Single

$CollectionType = $CollectionItem.CollectionType
$CollectionName = $CollectionItem.Name
$MemberClassName = $CollectionItem.MemberClassName


If ($CollectionType -eq 2) # Computer collections
    {
        Write-Output "Please wait, this can take a while..."
        $colcontent = Get-WmiObject -Namespace $Namespace -ComputerName $SiteServer -Query "SELECT Name, Active, OperatingSystemNameandVersion, ResourceID FROM SMS_R_SYSTEM where ResourceID in (Select ResourceID from $MemberClassName)" | Select-Object Name, Active, OperatingSystemNameandVersion -wait | Sort-Object Name 
        $colcontent = $colcontent | Out-GridView -Title "Collection: $CollectionName Member Class: $MemberClassName" -OutputMode Multiple
        #return $colcontent
    }
Elseif ($CollectionType -eq 1) # User Collections 
    {
       Write-Output "Please wait, this can take a while..."
       $colcontent = Get-WmiObject -Namespace $Namespace -ComputerName $SiteServer -Query "SELECT UserName, UserPrincipalName, ResourceID FROM SMS_R_USER  where ResourceID in (Select ResourceID from $MemberClassName)" | Select-Object Username, UserPrincipalName, ResourceID -wait
       $colcontent = $colcontent | Out-GridView -Title "Collection: $CollectionName Member Class: $MemberClassName" -OutputMode Multiple 
       #return $colcontent
    }
Else
    {
      Write-output "No support for other Collection Type $Collectiontype"
    }

return $colcontent
}

Example:

Get-CMColContent -Name “All”

SNAGHTMLa17ffb

SNAGHTMLa30678

PowerShell – Retrieve System Startup Time Information

The below script gathers the following system startup time information from a local or remote client.

  • Computername
  • Last Wakeup time (from Sleep, Hibernate or Fast boot on Windows 8x clients)
    The last wakeup date/time is converted from UTC into the client local time.
  • Last Boot time
  • The Time Zone of the client
  • The system wakeup / sleep message from the Windows event log

Important: the script uses PowerShell remoting, it’s therefore required that the targeted clients have WinRM enabled.

Function Get-SystemStartInfo()
 {
 <#
 .Synopsis
 Get System Boot / Wake-up Time 
 .DESCRIPTION
 This script retrieves system boot and wakeup times from the specified client(s). 
 On Windows 8x clients, the last Wake-up time is the last time the system performed a 
 fast boot. 
 .PARAMETER Computer
 The name of one or multiple clients
 .EXAMPLE
 Get-SystemStartInfo localhost, dev001 | Format-Table -AutoSize

 Computer LastWakeupTime LastBootTime TimeZone 
 -------- -------------- ------------ -------- 
 localhost 1/5/2014 11:55:41 PM 1/5/2014 2:35:44 PM (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm,...
 dev001 1/5/2014 11:55:41 PM 1/5/2014 2:35:44 PM (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm,...

.NOTES
 WinRM must be enabled on remote clients
 #>
 
[CmdletBinding()]
 Param(
    [Parameter(Mandatory=$true,
    ValueFromPipelineByPropertyName=$true,HelpMessage="Enter Computername(s)",
    Position=0)]
    [Alias("ipaddress","host")]
    [String[]]$Computer
    )

Begin
{
    Function Get-LocalTime($UTCTime,$Comp)
    {
        #Credits to Tao Yang for the Get-LocalTime function
        #http://blog.tyang.org/2012/01/11/powershell-script-convert-to-local-time-from-utc/
        #$strCurrentTimeZone = (Get-WmiObject win32_timezone).StandardName
        $strCurrentTimezone = Get-CimInstance -ComputerName $Comp -Namespace root/CIMV2 -ClassName win32_TimeZone | Select-Object -ExpandProperty StandardName -ErrorAction SilentlyContinue
        $TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone) 
        $LocalTime = [System.TimeZoneInfo]::ConvertTimeFromUtc($UTCTime, $TZ)
        Return $LocalTime, $TZ
    }
}
Process
{
    $SystemStartInfo=@()
    foreach ($c in $Computer)
    {
        Write-Output "Processing $c"
        if (Test-Connection -ComputerName $c -Quiet -Count 3 )
        {
            # The last boot date time
            $LBootLocal = Get-CimInstance -ComputerName $c -Namespace root/CIMV2 -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue | Select -ExpandProperty LastBootuptime -ErrorAction SilentlyContinue
            If([string]::IsNullOrEmpty($LBootLocal) -eq $true)
            {
                # No last boot time found
                $LBootLocal=""
            }
            
            $PowerEvent = Invoke-Command -ComputerName $c -ScriptBlock {
            $orgCulture = Get-Culture
            [System.Threading.Thread]::CurrentThread.CurrentCulture = New-Object "System.Globalization.CultureInfo" "en-US"
            $PowerEvent = Get-WinEvent -ProviderName "Microsoft-Windows-Power-Troubleshooter" -MaxEvents 1 -ErrorAction SilentlyContinue | Where-Object { $_.id -eq 1 } | Select-Object -ExpandProperty  Message -ErrorAction SilentlyContinue
            [System.Threading.Thread]::CurrentThread.CurrentCulture = $orgCulture
            return $PowerEvent
            } 
            
            If($PowerEvent.count -gt 0)
            {
                # Extract the Date / Time information when the system woke up
                $wake = ($PowerEvent.Replace("`n","@").split("@")[3]).replace("Wake Time: ","")
                [string]$utcyear = $wake.Substring(1,4)
                [string]$utcmonth = $wake.Substring(8,2)
                [string]$utcday = $wake.Substring(13,2)
                [string]$utchour = $wake.Substring(16,2)
                [string]$utcminute = $wake.Substring(19,2)
                [string]$utcseconds = $wake.Substring(22,2)
                $wakedt = $utcyear + $utcmonth + $utcday + $utchour + $utcminute + $utcseconds

                $Culture = [System.Globalization.CultureInfo]::InvariantCulture 

                #The datetime in UTC format
                $LWUTC = [datetime]::ParseExact($wakedt,"yyyyMMddHHmmss",$Culture)
                # The datetime in Local Time format
                $LWLocal = Get-LocalTime $LWUTC $c
            }
            Else
            {
                #No last wake up event found, so let's just get the TimeZone information
                $TZName = Get-CimInstance -ComputerName $c -Namespace root/CIMV2 -ClassName win32_TimeZone | Select-Object -ExpandProperty StandardName -ErrorAction SilentlyContinue
                $TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($TZName) 
                $LWLocal = "","$TZ"
                $PowerEvent = ""
            }

            $object = New-Object -TypeName PSObject
            $object | Add-Member -MemberType NoteProperty -Name "Computer" -Value $c
            $object | Add-Member -MemberType NoteProperty -Name "LastWakeupTime" -Value $LWLocal[0]
            $object | Add-Member -MemberType NoteProperty -Name "LastBootTime" -Value $LBootLocal
            $object | Add-Member -MemberType NoteProperty -Name "TimeZone" -Value $LWLocal[1]
            $object | Add-Member -MemberType NoteProperty -Name "Message" -Value $PowerEvent
            $SystemStartInfo += $object
        }
Else
        {
        Write-Verbose "Unable to connect to $c"
        }
    }
}
End
{
    return $SystemStartInfo 
}
}

Example:

Get-Systemstartupinfo client1,client2,client3,client4,client5, client6 | format-list

Computer       : client1
LastWakeupTime :
LastBootTime   : 24.12.2013 12:28:41
TimeZone       : (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
Message        :

Computer       : client2
LastWakeupTime : 06.01.2014 08:33:04
LastBootTime   : 06.01.2014 08:29:08
TimeZone       : (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
Message        : The system has resumed from sleep.
                
                 Sleep Time: ?2014?-?01?-?06T07:32:42.550130000Z
                 Wake Time: ?2014?-?01?-?06T07:33:04.780800500Z
                
                 Wake Source: Device -Intel(R) 82579LM Gigabit Network Connection

Computer       : client3
LastWakeupTime : 29.12.2013 19:17:22
LastBootTime   : 06.01.2014 00:55:57
TimeZone       : (UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi
Message        : The system has resumed from sleep.
                
                 Sleep Time: ?2013?-?12?-?29T11:05:17.427355600Z
                 Wake Time: ?2013?-?12?-?29T11:17:22.762004000Z
                
                 Wake Source: Power Button

Computer       : client4
LastWakeupTime : 06.01.2014 10:03:57
LastBootTime   : 06.01.2014 09:55:36
TimeZone       : (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
Message        : The system has resumed from sleep.
                
                 Sleep Time: ?2014?-?01?-?06T09:01:05.727399400Z
                 Wake Time: ?2014?-?01?-?06T09:03:57.248801300Z
                
                 Wake Source: Power Button

Computer       : client5
LastWakeupTime : 13.12.2013 13:27:33
LastBootTime   : 06.01.2014 09:28:39
TimeZone       : (UTC) Dublin, Edinburgh, Lisbon, London
Message        : The system has resumed from sleep.
                
                 Sleep Time: ?2013?-?12?-?13T12:32:42.342018800Z
                 Wake Time: ?2013?-?12?-?13T13:27:33.513115200Z
                
                 Wake Source: Device -USB Root Hub

Computer       : client6
LastWakeupTime :
LastBootTime   : 06.01.2014 01:23:48
TimeZone       : (UTC+10:00) Canberra, Melbourne, Sydney
Message        :

PowerShell – Creating Dummy files

Here’s a function I wrote to create a large dummy file either filled with zero’s or random characters. The -Target parameter must be eitehr a file including the path or just the path. If the path does not exist, it will be created. If only a path is provided a file with a random file name is generated.

Function New-BigFile {
<#
.Synopsis
  Creates a large dummy file with or without random conntent
.DESCRIPTION
  Creates a large dummy file with or without random conntent 
  Credits for the randome content creation logic goes to Robert Robelo
.PARAMETER Target
   The full path to a folder or file. If the target is a folder a random file name is generated
.PARAMETER MegaByte
   The size of the random file to be genrated. Default is one MB
.PARAMETER Filecontent
  Possible values are  or  When  is specified the file is filled with
  random values. The value  fills the file with nulls. 
.PARAMETER ShowProgress
 This parameter is optional and shows the progress of the file creation. 
.EXAMPLE
 New-Bigfile -Target C:\Temp\LF -Megabyte 10 -Filecontent random
.EXAMPLE
 New-Bigfile -Target C:\Temp\LF\bigfile.txt -Megabyte 10 -Filecontent random
.EXAMPLE
 New-Bigfile -Target C:\Temp\LF -Megabyte 10 -Filecontent empty
#>

[CmdletBinding(SupportsShouldProcess=$True)]
param(
    [Parameter(Mandatory = $true, Position = 0)]
    [String]$Target,
    [Parameter(Mandatory = $false, Position = 1)]
    [ValidateRange(1, 5120)]
    [UInt16]$MegaByte = 1,
    [Parameter(Mandatory = $true,position = 2)]
    [ValidateSet("random","empty")]
    [string]$FileContent,
    [Switch]$ShowProgress
)


If ([string]::IsNullOrEmpty([System.IO.Path]::GetDirectoryName("$target")) -eq $True)
{
    Write-Output "Specify a directory or file including path!"
    Throw
}


If([string]::IsNullOrEmpty([System.IO.Path]::GetExtension("$target")))
{
    Write-Verbose "Provided input $target has no file extension, target is a folder"
    $fname = ("" + ([guid]::NewGuid()) + ".LF")
    Write-Verbose "Random generated filename: $fname"
    $Target = Join-path $Target  $fname
    $folder = [System.IO.Path]::GetDirectoryName($target)

    If ((Test-path $folder) -eq $false)
    {If ($PSCmdlet.ShouldProcess("Directory does not exist, creating directory $folder ")) 
            { New-Item -Path $folder -ItemType Directory | Out-Null}}
}
Else
{
   If ((Test-path -Path $target) -eq $true)
        {Write-verbose "File $Target already exists, exiting to prevent overwrite"
        Break}
    Else
        {Write-Verbose "File $target does not exist yet"}

    # Check if the directory actually exists, if not create it
    $folder = [System.IO.Path]::GetDirectoryName($target)
    If ((Test-path $folder) -eq $false)
    {If ($PSCmdlet.ShouldProcess("Creating folder $folder")) 
            {New-Item -Path $folder -ItemType Directory | Out-Null}}
}

$path = $Target
$total = 1mb * $MegaByte
$strings = $bytes = 0

If ($FileContent -eq "random")
{
If ($PSCmdlet.ShouldProcess("Creating random file $path with $Megabyte MB")) 
{

# create the stream writer
$sw = New-Object IO.streamWriter $path

# get a 64 element Char[]; I added the - and _ to have 64 chars
[char[]]$chars = 'azertyuiopqsdfghjklmwxcvbnAZERTYUIOPQSDFGHJKLMWXCVBN0123456789-_'
1..$MegaByte | ForEach-Object {
# get 1MB of chars from 4 256KB strings
1..4 | ForEach-Object {
# randomize all chars and...
$rndChars = $chars | Get-Random -Count $chars.Count
# ...join them in a string
$str = -join $rndChars
# repeat random string 4096 times to get a 256KB string
$str_ = $str * 4kb
# write 256KB string to file
$sw.Write($str_)
# show progress

    if ($ShowProgress) {
    $strings++
    $bytes += $str_.Length
    Write-Progress -Activity "Writing String #$strings" -Status "$bytes Bytes written" -PercentComplete ($bytes / $total * 100)
    }

# release resources by clearing string variables
Clear-Variable str, str_
}
}
$sw.Close()
$sw.Dispose()
# release resources through garbage collection
[GC]::Collect()
}
}

Else 
{
    If ($PSCmdlet.ShouldProcess("Creating empty file $path with $Megabyte MB")) 
    {
    # write 4K worth of data at a time
    $bufSize = 4096
    $bytes = New-Object byte[] $bufSize
    $file = [System.IO.File]::Create("$path")
    # write the first block out to accommodate integer division truncation
    $file.Write($bytes, 0, $bufSize)
    for ($i = 0; $i -lt $Megabyte*1MB; $i = $i + $bufSize) { $file.Write($bytes, 0, $bufSize) 

    if ($ShowProgress) {
        Write-Progress -Activity "Writing String #$strings" -Status "Bytes written" -PercentComplete ($i/($megabyte*1MB)*100 )
    }
    }
    $file.Close()
} 
} 
} 

Run the following command to create a dummy file of 10 MB with random content

New-bigfile c:\temp\lf 10 random  -Verbose 

Run the following command to create a dummy file dummy10mb.txt with random content

New-bigfile c:\temp\lf\dummy10mb.txt 10 random  -Verbose 

Run the following command to create a dummy file of 20 MB filled with zeros

New-bigfile c:\temp\lf 20 empty-Verbose 

If you prefer to see what is going on, add the -Showprogress option. And last but not least the function also supports the -whatif option.

PowerShell – Using the WordPress Rest API

I just found out that meanwhile (since October last year) the Rest API for wordpress now also works on self-hosted wordpress sites. So i can now access the content of my blog through PowerShell.

$posts = Invoke-RestMethod -uri "https://public-api.wordpress.com/rest/v1/sites/www.verboon.info/posts/?number=50"
$posts.posts | Select-Object @{"Name" = "Title";"e"= {($_.Title)-replace "–","-"}},  @{"Name" = "Date"; "Expression" = {get-date ($_.Date) -Format "yyyy-MMM-dd"}} | ft 

2013-12-29_20h05_20

More details abou the WordPress Rest API can be found here

PowerShell – Creating Scheduled Tasks with PowerShell version 3

I am currently working on a script where I need to create a scheduled task that runs a powershell script at a given time. With PowerShell 4.0 a scheduled task can be easily created with the new cmdlets New-ScheduledTaskAction, New-ScheduledTaskTrigger and Register-ScheduledTask, but unfortunately i have to create a solution that works on clients running PowerSshell version 3.0.

When searching the web for examples how others have solved this I found many examples where people invoke the schtasks.exe command but in order to further improve my PowerShell skills I wanted to avoid calling external executables and do as much in PowerShell as possible. So after a bit of further searching I found an interesting post from @letitknowblog who uses the Task Scheduler’s com object for creating a scheduled task.

To simplify sharing my lessons learned I’ve created a simple script that does the following.

  • Define variables for the Schedled Task name, description, command,  command argument to be executed
  • Define the time when the scheduled task must run, the below task will only run once.
  • Create the scheduled task, actions and triggers
# The name of the scheduled task
$TaskName = "MyScheduledTask"
# The description of the task
$TaskDescr = "Run a powershell script through a scheduled task"
# The Task Action command
$TaskCommand = "c:\windows\system32\WindowsPowerShell\v1.0\powershell.exe"
# The PowerShell script to be executed
$TaskScript = "C:\scripts\myscript.ps1"
# The Task Action command argument
$TaskArg = "-WindowStyle Hidden -NonInteractive -Executionpolicy unrestricted -file $TaskScript"

# The time when the task starts, for demonstration purposes we run it 1 minute after we created the task
$TaskStartTime = [datetime]::Now.AddMinutes(1) 

# attach the Task Scheduler com object
$service = new-object -ComObject("Schedule.Service")
# connect to the local machine. 
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa381833(v=vs.85).aspx
$service.Connect()
$rootFolder = $service.GetFolder("\")

$TaskDefinition = $service.NewTask(0) 
$TaskDefinition.RegistrationInfo.Description = "$TaskDescr"
$TaskDefinition.Settings.Enabled = $true
$TaskDefinition.Settings.AllowDemandStart = $true

$triggers = $TaskDefinition.Triggers
#http://msdn.microsoft.com/en-us/library/windows/desktop/aa383915(v=vs.85).aspx
$trigger = $triggers.Create(1) # Creates a "One time" trigger
$trigger.StartBoundary = $TaskStartTime.ToString("yyyy-MM-dd'T'HH:mm:ss")
$trigger.Enabled = $true

# http://msdn.microsoft.com/en-us/library/windows/desktop/aa381841(v=vs.85).aspx
$Action = $TaskDefinition.Actions.Create(0)
$action.Path = "$TaskCommand"
$action.Arguments = "$TaskArg"

#http://msdn.microsoft.com/en-us/library/windows/desktop/aa381365(v=vs.85).aspx
$rootFolder.RegisterTaskDefinition("$TaskName",$TaskDefinition,6,"System",$null,5)

Now if we wanted to run the Task more than just once, let’s say on a monthly basis, we have to change and add a bit of code  The code above uses Create(1) which means that the trigger is set to run once.

$trigger = $triggers.Create(1) # Creates a "One time" trigger

If we want to use another schedule we must use one of the following values as explained in more detail here.

TASK_TRIGGER_EVENT  0
TASK_TRIGGER_TIME 1
TASK_TRIGGER_DAILY  2
TASK_TRIGGER_WEEKLY 3
TASK_TRIGGER_MONTHLY  4
TASK_TRIGGER_MONTHLYDOW 5
TASK_TRIGGER_IDLE 6
TASK_TRIGGER_REGISTRATION 7
TASK_TRIGGER_BOOT 8
TASK_TRIGGER_LOGON  9
TASK_TRIGGER_SESSION_STATE_CHANGE 11

So let’s suppose we want to run the task the first day of every month, we then have to change the code as following.

$trigger = $triggers.Create(4)
$trigger.DaysOfMonth = 1

If we want to run the task when the system is idle we set the value of Create to 6.

$trigger = $triggers.Create(6)

If more than one action is required, just add an action, action path and action argument as in the example below.

$Action = $TaskDefinition.Actions.Create(0)
$action.Path = "$TaskCommand"
$action.Arguments = "$TaskArg"
$Action = $TaskDefinition.Actions.Create(0)
$action.Path = "notepad.exe"
$action.Arguments = ""

The following code registers the scheduled task

$rootFolder.RegisterTaskDefinition("$TaskName",$TaskDefinition,6,"System",$null,5)

If the first parameter is set to $null, a random GUID is assigned as the Task name.

$rootFolder.RegisterTaskDefinition($null,$TaskDefinition,6,"System",$null,5)

The $Taskdefinition holds all the previously defined settings for the scheduled task.

The value 6 referts to the task creation constant which means that Task Scheduler either registers the task as a new task or as an updated version if the task already exists. A complete listing of possible values can be found here.

The next value defines the user context in wich the task runs, in this case we run the scheduled task within the system context.

The last value defines the logon type. The value of 5 Indicates that a Local System, Local Service, or Network Service account is being used as a security context to run the task. A complete listing of possible values can be found here

Much simpler when using PowerShell version 4

With PowerShell version 4 things get a bit simpler, to accomplish the same as in the aboe scrpt only 3 lines of code are needed.

$TaskAction = New-ScheduledTaskAction -Execute "$TaskCommand" -Argument "$TaskArg" 
$TaskTrigger = New-ScheduledTaskTrigger -At $TaskStartTime -Once
Register-ScheduledTask -Action $TaskAction -Trigger $Tasktrigger -TaskName "$TaskName" -User "System" -RunLevel Highest

PowerShell – Get latest Microsoft KB information

I frequently visit kbupdate.info which is a great resource for finding latest KB updates, but still you have to manually click through the various lists. Now that I am learning PowerShell anyway, i thought i’ll give myself another task to further improve my PowerShell skills. So here we go, below you find a script that retrieves the latest KB update information for various products and displays them on screen so that I can quickly browse through them and directly launch the article of interest in Internet Explorer.

<#
.Synopsis
   Retrieve latest Microsoft KB updates for various products
.DESCRIPTION
   Retrieve latest Microsoft KB updates for various products. The script uses the 
   RSS feeds available for the various Microsoft products. For more RSS feeds check
   http://support.microsoft.com/gp/topissuesrss and 

http://support.microsoft.com/select/?target=rss

.EXAMPLE
 Get-LatestKBUpdates.ps1
.LINK

http://support.microsoft.com/gp/topissuesrss


http://support.microsoft.com/select/?target=rss

.NOTES
  Version 1.0, by Alex Verboon
#>

$rss ="http://support.microsoft.com/common/rss.aspx?rssid=16796&ln=en-us", # Most Recent KBs for Windows 8
        "http://support.microsoft.com/common/rss.aspx?rssid=14019&ln=en-us", # Most Recent KBs for Windows 7
        "http://support.microsoft.com/common/rss.aspx?rssid=6527", # Most Recent KBs for Windows Update
        "http://support.microsoft.com/common/rss.aspx?rssid=14134", # Most Recent KBs for Windows Server 2008 R2
        "http://support.microsoft.com/common/rss.aspx?rssid=1060", # Most Recent KBs for System Center Configuration Manager
        "http://support.microsoft.com/common/rss.aspx?rssid=13615", # Most Recent KBs for Office 2010 suites
        "http://support.microsoft.com/common/rss.aspx?rssid=16674&ln=en-us", # Most Recent KBs for Office 2013 suites
        "http://support.microsoft.com/common/rss.aspx?rssid=16526&ln=en-us" # Most Recent KBs for Windows Server 2012


$allkb =@()
ForEach($r in $rss) 
{
    $rssfeed = Invoke-RestMethod -Uri $r | Select-Object title,link,pubDate
    $Title = Invoke-WebRequest -uri $r
    forEach ($kbi in $rssfeed)
        {
        $object = New-Object -TypeName PSObject
        $object | Add-Member -MemberType NoteProperty -Name "Source" -Value $Title.rss.channel.title
        $object | Add-Member -MemberType NoteProperty -Name "Title" -Value $kbi.title 
        $object | Add-Member -MemberType NoteProperty -Name "PubDate" -Value $kbi.pubdate 
        $object | Add-Member -MemberType NoteProperty -Name "Link" -Value $kbi.link
        $allkb += $object
        }
    }
$kbopen = $allkb | Out-GridView -OutputMode Single 
Start-Process $kbopen.link

2013-12-17_01h34_52

PowerShell – Get Stock Quote Information

The below script makes use of a webservice hosted by webserviceX.net and provides stock quote information.

<#
.Synopsis
   Retrieve stock information
.DESCRIPTION
   Retrieve stock information 
.PARAMETER Symbol
  Stock symbol of one or more companies
.EXAMPLE
 Get-StockInfo -Symbol MSFT,INTC
.LINK
   
.NOTES
  Version 1.0, by Alex Verboon
#>
 
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
            ValueFromPipelineByPropertyName=$true,
            HelpMessage= 'Stock Symbol for the company')]
            [String[]]$Symbol
)

begin{}
process{

if ([string]::IsNullOrEmpty($Symbol))
    {Write-Output "You must provide a Symbol"
    Exit}
  
ForEach ($stock in $Symbol)
    {
    $sq = Invoke-WebRequest -uri http://www.webservicex.net/stockquote.asmx/GetQuote?symbol=$stock
    $sqdetail = $sq.DocumentElement.'#text' 
    $sqdetail.StockQuotes.stocK
    }
}

End{}

2013-12-15_14h28_47