PowerShell Script Scheduling using Task Scheduler Trcik

In this post, I want to talk about my personal experience scheduling PowerShell script using Windows Task Scheduler.

Let us start with Scheduling simple PowerShell Script called MyScript.PS1 located on a directory F:\Scripts\

  • Go to Task Scheduler in Windows> Create Basic Task, give it a name


PowerShell Scheduling 1


  •  In the Actions section, click (Start a Program):
    • In the Program/Script , Type  C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
    • In the Add arguments (Optional), type F:\Scripts\MyScript.PS1

PowerShell Scheduling 2


So what can get wrong?

In the previous example, the Add Arguments (Optional) section, will not behave correctly when you type long data on it.

Suppose that your script takes many parameters, so on the Add Arguments (Optional) field, you will type F:\Scripts\MyScript.PS1 -SendEmail:$true -From me@contoso.com -EmailFrom admin@contoso.com -UseSecure:$false  ….. . This becomes even longer with some complex scripts.

In Summary, you cannot type long strings here and expect everything to work fine.

Solution ?

So the best thing to do, is to open a new PowerShell script file,  type on it the full command that you want to run , in our case F:\Scripts\MyScript.PS1 -SendEmail:$true -From me@contoso.com -EmailFrom admin@contoso.com -UseSecure:$false, save it on the same directory as the original script F:\Scripts with any name like “RunMe.PS1″, and create one schedule task and type F:\Scripts\Runme.PS1 on the Add Arguments (Optional) field.

PowerShell Scheduling 3


PowerShell Scheduling 4


See also my previous post for scheduling Exchange PowerShell Scripts 

Run remote PowerShell scripts and get WMI data from standalone machines (LocalAccountTokenFilterPolicy)


Hi everyone,

Well, i have to admit that the information in this post is so important to me as i always wanted to get data from standalone deployments. I hope that you will find it useful to you also.

Nowadays, with all security concerns and attacks, many application architectures contain standalone not domain joined roles. Take Microsoft Lync for example, they have a separate Edge role to handle media from external clients and for security reasons, this role is meant to be a standalone deployment and never joined to the internal domain.

Exchange Architecture also contains an Edge role acting as SMTP gateway and for security concerns this is a standalone role that is not joined to the internal corporate domain.


As a PowerShell guy, i always write scripts to collect data, report data, or even send SMTP alerts in case of failures. I usually have a dedicated VM that is acting as a script server that runs all my scripts. I always find it challenging to collect remote WMI data from those standalone not domain joined machines.

The challenge is : How a script running on my script server that is member of contoso domain, and running under contoso\user1 credentials, will be able to connect to that remote standalone server and get WMI data for example.



  • Go to the standalone computer, create a local user called User1 with the same password as Contoso\User1 (which is the account im using to run scripts on the domain joined script server) , and i will add it to the local administrators group on the standalone server.

Now, i have domain user called contoso\User1 running my script on the script server with Password = 123 for example, and i have similar but local user on the standalone server called User1 with same Password=123 , and member of the local administrators group of the standalone server.

  • On the standalone server, i have to enable something called (LocalAccountTokenFilterPolicy) to do the trick. This is saying that, if i receive a connection with a user name and password that matches a local user account on my local credential store, i will consider it valid transaction, and even if the matching local account is administrator, i will elevate the remote connection to a token with admin rights.

Key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
Value: LocalAccountTokenFilterPolicy
Data: 1 (to disable, 0 enables filtering)
Type: REG_DWORD (32-bit)


Final thoughts

Enabling this registry key is somehow a balance between scrutiny and usability. Make sure you understand what you are giving by disabling this filter.

Read more about this registry key here http://support.microsoft.com/kb/942817


New Exchange Organization Report (version 2) is coming soon

The new version is available now here :



I got a lot of requests to have version 2 for more customization and cross version support.

I am glad to tell you that i am working on version 2 of the report with initially the below features in mind:

  • Support of all versions of Exchange, including Exchange 2013
  • Support for enhanced filtering. Three filters will be available :
    • Filter by DAG Name : you can enter list of DAGs to include on the report
    • Filter by Server -Like switch : for example Server -like “*NYC*
    • Filter called OnlyTheseServers : so you can do something like -OnlyTheseServers srv1,srv2,srv3
  • Enhanced error handling : There is huge effort to handle many of situation where error popup because of connectivity issues or unexpected nulls
  • Option to enable remote PowerShell connectivity for pulling WMI data in case you have enabled PowerShell remoting on your servers to use HTTP, instead of RPC
  • New Report to show DAG layout across mailbox servers and their activation preference.
  • Big efforts on charts. Now charts will scale automatically depending on the number of items in the input data.
  • Verbose mode to enable enhance troubleshooting
  • More friendly try catch blocks to show friendly warning messages and location of errors if any.

Those are initially the new features in mind. I hope you all will like it.

I hope i can finish coding it  before I fly to TechEd North America 11th May 2014.

Chart Bars232

Exchange Database Health Get-DBCopyHealth

This post is updated to include information about the new version of the script (Version 3) published in April 2015 and includes a filter option to filter by DAG.

Hi everyone,

I got couple of requests to enhance and add new features on my script that reports if any Exchange DB is dismounted or placed on not optimal server according to the activation preference. See my previous post http://wp.me/p1eUZH-a8

Script Features

This script will collect all your Exchange databases and will report two things:

  • If any database is mounted > Email notification will be sent
  • If any database got switched over to another server > Email notification will be sent to report that the DB is not mounted on the server with activation preference =1 (not the optimal location)

The idea behind the script is to schedule it to run every hour or so, and you will get email notification only if something bad happens.

I explained more in a previous post more about this script and functionalists, but now i have created a new version (Version 2 ) with new features:

  1. Exchange 2013 support
  2. Reporting the current mounting server location in addition to where the DB should be mounted
  3. Switch Option to disable checking for mounting status if needed
  4. Verbose logging
  5. Performance enhancement
  6. Option to run the script without Email notification option
  7. Now you can filter databases with the InputDAG option, so you can provide a list of DAG names.


Run the script from PowerSehll without SMTP email notification
.\Get-DBCopyHealth.ps1 -HTMLReport myfile.html

Run the script from PowerShell and filter for databases on DAG1 and DAG2
.\Get-DBCopyHealth.ps1 -HTMLReport myfile.html -InputDAGs DAG1,DAG2

Run the script from PowerShell with verbose logging and without SMTP email notification
.\Get-DBCopyHealth.ps1 -HTMLReport myfile.html -verbose

Run the script from PowerSehll without SMTP email notification. We do not want to check mounting status, only optimal DB placement
.\Get-DBCopyHealth.ps1 -HTMLReport myfile.html -DontCheckMountStatus

Run the script with Email report option
.\Get-DBCopyHealth.ps1 -HTMLReport myfile.html -SendMail $true -MailFrom noreply@contoso.com -MailServer smtp.contoso.com -MailTo admin@contoso.com




Download Version 3 here

Click here to get the Get-DBCopyHealth version 3:  Get-DBCopyHealth


Configuring RDS 2012 TLS Certificate

I was working on a deployment of Windows 2012 R2 RDS, where i have couple of servers participating in a session host pool (Collection), one server acting as a broker, and a TS Licensing server.

I have two Session Host servers (SRV1 and SRV2), so i have created two DNS records, both with the same Host Name ( Apps.contoso.com) and each one pointing to one of the Session Host servers (DNS Load Balancing).

The problem is that when users are connecting to (Apps.contoso.com), they are getting some certificate warning regarding certificate name mismatch.



This is because the Session Host servers will generate self signed certificate with the name of the session host server, and not (Apps.contoso.com).

So i have created a digital certificate with subject name (Apps.contoso.com), i installed on on SRV1 and SRV2, and i could not find any place to instruct the session host servers to use my certificate, not the self signed one.

Then i found the solution:

  • Install the Apps.contoso.com certificate on SRV1 and SRV2 ( which are the session host servers), and take note of the thumbnail of the certificate.
  • On each session host server, open PowerSehll using Admin credentials, and type :

$path = (Get-WmiObject -class “Win32_TSGeneralSetting” -Namespace root\cimv2\terminalservices -Filter “TerminalName=’RDP-tcp'”).__path 

Set-WmiInstance -Path $path -argument @{SSLCertificateSHA1Hash=”‎Thumnail”} 

Note: replace Thumbnail with your custom certificate thumbnail

  • Restart both servers
  • Use this command to get the certificate hash being used already

wmic /namespace:\\root\cimv2\TerminalServices PATH Win32_TSGeneralSetting Get SSLCertificateSHA1Hash

  • Or you can use this command to do it in one command:

wmic /namespace:\\root\cimv2\TerminalServices PATH Win32_TSGeneralSetting Set SSLCertificateSHA1Hash=”Thumnail”


Email moderation is a great replacement for mail group restriction. Instead of maintaining access lists on your key mail groups, just enable moderation on them and distribute the responsibility for approving email flow.

When you deal with Dynamic groups, those with predefined cafeterias for manipulating membership,  it becomes interesting when those groups become so big. For example, you can create a dynamic group for all people who has a specific country code or office location. Those groups are very good candidate for moderation I believe.

Sometimes, when you deal with dynamic groups in Exchange , you  should moderate those groups specially if those groups represents all employees in specific country or office,etc.

The strange thing about Moderated dynamic groups is the fact that you cannot view the moderation info of a moderated group from the Exchange Admin console, you need to go to powershell to do that.

Also, many times, you want to give your IT teams, or business people, a documentation about moderation info in the organization. In my case, i have a script that generate an excel sheet with all moderated groups, along with the moderation info for each group and i publish them via email enabled SharePoint list.

Script Feature :

Generate CSV with option to email it, with info about all moderated groups and the following info :

  1. Name
  2. ModerationEnabled
  3. ModeratedBy
  4. BypassModerationFromSendersOrMembers
  5. SendModerationNotifications
  6. ManagedBy
  7. RequireSenderAuthenticationEnabled
  8. WindowsEmailAddress
  9. Type


Download script

You can download the script from here:  Get-MailingGroupModeration

Given a list of users, get distribution groups they manage !

Hi, I got a request to generate a powershell script.  So, say you have a list of users that you need to know which mailing group they manage, and output the results in an CSV file.

This script will take two input variables :

  • InputFile : required parameter, which is the text file containing all usernames that you want to see which mailing groups they manage. Example: c:\users.txt
  • Outfile : required parameter, which is the csv file to generate. Example is c:\output.csv

So, say you have a text file named c:\users.txt that contains three lines ( Johns, JeorgeM, AliceO), then you can type the following :

Get-SpecificDGManagers -inputfile c:\users.txt -outfile c:\output.csv 

So the output.csv will contain a list of those three users along with all mailing groups they manage.

Download the script

You can download the script from here:  Get-SpecificDGManagers

Note: the list of users in c:\users.txt should be the samaccountnames and not the displaynames of the users 🙂 this is to ensure uniqueness.

Note: If no output is available ( the users are not managing any groups, then no csv will get generated). Run the script in verbose mode to see more info 🙂

Note: run it from Exchange Management PowerShell with user that has read access only.

Get Exchange Distribution Groups Manager

Hi again,

I got a request to list all distribution groups and their managers in a csv file. This way, you can quickly see which groups that do not have a manager 🙂

The script is simple, just run it from Exchange Management Shell, and supply the following parameters:

  1. File : required the csv file name and path to save the output csv
  2. OU : optional. used to narrow the scope of the script to a certain OU.

Get managers of all groups and export results to c:\files.csv”
PS C:\>Get-ExchangeDGManagers -file “c:\file.csv”

Get managers of all groups under the specified OU.
PS C:\>Get-ExchangeDGManagers -OU “ou=mailing groups, dc = contoso, dc=com” -file “c:\file.csv”

Download the script 

You can find the script here: Get-ExchangeDistributionGroupManagers


Get-ExchangeDGManager Snapshot


Get Random HashTable

Sometimes you are testing or piloting some functions or other scripts and you need access to test data to see how your function or script will deal with it.

I usually find my self in need to a test hashtable that should be big in size or sometimes small in size. This is why i want to share my personal test hashtable function.

The function takes one parameter which is the number of items in the hashtable. The function then will return hashtable with the requested size and filled with test data (the test data is city names and their population).

Here is the script function code:

Random Hashtable

Download the script 

You can download it from here: Get-RandomHash

Charts and PowerShell !! PowerShell Wrapper “Light Edition”

Check Out the Light Edition of this PowerShell Wrapper : Get-CorpCharts-FullEdition

I think one of the most interesting things when writing PowerShell scripts, is to make the results appear in a nice chart or couple of them. If you are going to show disk space info, nothing more than a nice chart will worth looking at. If you are sending a report to your management, nothing will take their attention more than charts. I love getting charts as  a high level output, with more tables and data for extra details.

Why this is different than others?

Because this script is a wrapper around the dirty stuff that you do not want to worry about, or may be you are new to PowerShell and  just what the chart to get generated.

You can just give it some data, and charts will start decorating your screen , ready to be emailed maybe to someone else to look at the data in a nice way.

The script

The script is FULL with examples and information of how to use it. Do not panic if you find the code long or complex for you. Just look at the examples and you will find your way.

Do not forget that you need to install two things before running the script on your machine :

So how the script works?

You have to enter a parameter called (-data) that is essentially an array of objects.

Suppose you have an object called (City) with two properties : City_Name, and City_Population

and let us say you have ten of those objects and they are stored in an array called $Cities

So now, all what you have to do is to give the $Cities array to the function as -Data parameter, and supply the -Obj_Key parameter as “City_Name”, and -Obj_Value as “City_Population”

So the command will be : Get-CorpCharts-LightEdition -data $Cities  -Obj_Key  City_Name  -Obj_Value City_Population -FilePath C:\graph.png


Download version 2

You can download version 2 of the script from here: Get-CorpCharts

New in Version 2

  • Sortable data option via -sort parameter.
  • Fix label alignment via -fix_label_alignment parameter.
  • Exposing X and Y axis intervals via parameters.
  • Show data as percentage via -show_percentage_pie parameter.
  • Collected threshold to group data items below the threshold into one item called ‘Others’.
  • Customizing chart data column colors via -chart_color parameter.
  • Dynamic dimension depending on the number of items in the input data.

Charts with PowerShell 1

Charts with PowerShell 2

Charts with PowerShell 3

Charts with PowerShell 4Charts with PowerShell 5

Charts with PowerShell 6



Chart by supplying array of objects as input. We are interested in the Name and Population properties of the input objects. In this case, we should also use the -obj_key and -obj_value parameters to tell the function which properties to draw. Default chart type ‘column’ is used.

PS C:\> Get-Corpchart-LightEdition -data $array_of_city_objects  -obj_key “Name” -obj_value “Population”

-filepath “c:\chart.png”


Specifying chart type as pie chart type.

PS C:\> Get-Corpchart-LightEdition -data  $cities  -obj_key “Name” -obj_value “Population”

-filepath “c:\chart.png” -type Pie


Specifying chart type as pie chart type. legend is shown.

PS C:\> Get-Corpchart-LightEdition -data $cities -obj_key “Name” -obj_value “Population”

-filepath “c:\chart.png” -type Pie -showlegend


Specifying chart type as SplineArea chart type.

PS C:\> Get-Corpchart-LightEdition -data $cities -obj_key “Name” -obj_value “Population”

-filepath “c:\chart.png” -type SplineArea


Specifying chart type as bar chart type, and specifying the title for the chart and x/y axis.

PS C:\> Get-Corpchart-LightEdition -data $cities  -obj_key  “Name” -obj_value  “Population”

-filepath “c:\chart.png”  -type Bar  -title_text  “people per country”  -chartarea_Xtitle  “cities”

 -chartarea_Ytitle  “population”


Specifying chart type as column chart type. Applying the -showHighLow switch to highlight the max and min values with different colors.

PS C:\> Get-Corpchart-LightEdition -data $cities -obj_key “Name” -obj_value “Population”

-filepath “c:\chart.png” -type Column -showHighLow


Chart with percentages shown on the pie/doughnut charts.

PS C:\> Get-Corpchart-LightEdition -data $cities -obj_key “Name” -obj_value “Population”

-filepath “c:\chart.png” -type Doughnut -Show_percentage_pie


If the chart type is pie or doughnut, you can specify a threshold (percentage) that all data values below it, will be shown as one data item called (Others).

PS C:\> Get-Corpchart-LightEdition -data $cities -obj_key “Name” -obj_value “Population”

 -filepath “c:\chart.png” -type Doughnut  -CollectedThreshold 16


Column chart with green columns.

PS C:\> Get-Corpchart-LightEdition -data $cities -obj_key “Name” -obj_value “Population”

-filepath “c:\chart.png” -chart_color Green

Get Server Up Time in a smart way (WMI)

This function will take a server name or array of server names, and return Server Up Time in form of TimeSpan data type.

Why this is different ?

What makes this script different than other scripts is two things:

– Faster : Instead of getting all WMI data from Win_OperatingSystem name space, we will only get the server up time property, so less data transfer the wire

– Returns object with two properties : ComputerName, and UpTime.

– Better error handling : Even if there is trouble getting WMI data from the remote computer, the uptime property will be replaced with “n/a” instead of throwing exception.

– Returns TimeSpan : So the UpTime property will be TimeSpan, so you can browse its properties and get uptime in days, minutes, seconds, or even more accurate information.

– Flexible Input Data: Can take input from the pipeline in form of single server or array of server names.

Download the script

You can download the script from here  Get-CorpUpTime


Get WMI data the right way – Get Disk info using WMI

You can find many scripts out there digging and getting WMI data, especially disk space stuff.  I was searching online for a PowerShell function to get disk space information, and there are two interesting things (not problems for sure), but things that I didn’t like and I will share with you what I think.

First point is that most scripts out there, will deal with errors and timeouts in different ways. Most of the time they will either throw exception, write something in the screen or in a verbose stream, or in best case scenarios, they will write the name of that computer in an error log file.


The below figure shows my own point of view of how I think the first point should be handled. If I requested WMI Disk information about a computer, I expect an object back representing information about this computer.

  • I do not care if the computer is reachable or if there is problem querying wmi data.
  • I do not want to see or handle exceptions.
  • I do not want to look at log files.
  • JUST GIVE ME AN OBJECT BACK SIMPLY. Leave the technical stuff for yourself dear script and shut up.


So, an object should be returned silently. If the remote computer is not reachable, then I will still get an object with “n/a” for the disk information property.

It is the calling function job then to receive that object and investigate the fields. This level of abstraction allows the calling function to deal with all results coming back from the WMI function in the same way. The calling function should always receive an object silently.

The second interesting thing is that most of those scripts online will throw disk info objects on the pipeline, with a computername property for you to distinguish. So you will receive an object for each disk (not computer). Something like the below figure.


Well, all what I wanted is information about two machines, so I expect two objects in return ,an object per computer, not an object per disk. Why shall I do some house cleaning on behalf of such scripts?

The answer to this issue is to return an object per computer. This object will contain two properties:

  • ComputerName
  • Child Object : containing disk information


The Script

Taking into consideration the above points, the script will get the following information:

  • ComputerName
  • Child object called (Disks)  with the following properties:
    • Drive
    • VolumeName
    • Size in GB
    • Free space in GB
    • Free space percentage

As you can see from the below figure, we will get one object back, containing child object for the disks.


Same thing when we query two computers, we will get two object back .In case of error when doing the WMI query, the child object called (disks) will be replaced by the string “n/a”


Download the script

You can download the script from here : Get-CorpDisks

The script is using (Get-WmiObject) to get disk info. I recommend that you replace the Get-WmiObject  with (Get-CorpCimInfo). This way, the script will try to use Get-CimSession, then remoting then DCOM to retrieve wmi data. Have a look to my Get-CorpCimInfo script here.

PowerShell – script to get WMI data, try CIM, then remoting then RPC


There are many scripts to get WMI data in the internet, but this script will use a unique technique. It will try getting remote data using many methods, preferring new ones and falling back to old legacy ones.

What is unique about this script is the amount of customization you can do with it. You can customize it completely just by adding one the script parameters and you are done.

If you run the script using -verbose mode, you will be presented with the right information for you to discover the power and logic of the script. Running the script by its own will not do anything as it is a wrapper function for you to use in other places where you need to get WMI data.

The script will basically get computer name or names, try to collect WMI information from them.

The script is smart enough to test if the remote computer is running PowerShell 3.0 at least and supports WS-MAN 3.0. If this is the case, the script will try to get WMI information by creating CIM session (Get-CIMSession).

If the script cannot connect to remote computer using CIM Session, then PowerShell remoting will be tried. If this fails too, the script will try to fall back to RPC call to get WMI data.


Version 2 of the script

After a quick chat with Jeffery Hicks (@JeffHicks) who i based my script upon his own version, Jeff suggested couple of modifications on my version of the script, specifically the concept of always returning objects instead of strings or other types. After tweaking the script to do that and add more verbose and exception additional information, i came with version 2 of the script.

Download Get-CorpCimInfo_v3  

PowerShell Tools for Visual Studio


I love Visual Studio, and i love writing code. I have come across PowerShell Tools for Visual Studio this morning, and i tried it out. The tool simply allows you to write your PowerShell Scripts from within Visual Studio (2012) or (2013).

I was so excited to try it out. So i downloaded the tools which is still in beta from the below link:


PowerShell Tools for Visual Studio


If you are familiar with Visual Studio, you are going to love it. Nevertheless, i guess since the tool is still in beta, it needs some time to mature. I got couple of termination errors and it is hard to evaluate complex variables when in debug mode. For example, if i have an array variable, i cannot get its content in debug mode, which leaves me to get back to my ISE environment for now.

After all, great work from Adam Driscoll Indeed !

PowerShell Tools for Visual Studio3