Execute PowerShell Scripts from Your Smartphone

Suppose you are on vacation/commute/away from your desk and get an emergency IT request. Would not it be cool to just text the PowerShell commands from your phone to your desk, have PowerShell over there execute the script, and send you back the results? ;)

Turns out this is very easy to do. All you need is Outlook, a simple rule in it, a simple PowerShell script and Outlook macro.

Here’s how this all works:

  1. You set up an Outlook rule to check for incoming email with a specific keyword (e.g. $PowerShell$) in the subject and sent from your specific email address.
  2. You send the PowerShell script in the email body and put the keyword in the subject.
  3. The Outlook rule starts an Outlook script and a PowerShell script.
  4. The Outlook script saves the email as a text file and waits for the transcript.
  5. The PowerShell script executes the script exported by Outlook.
  6. Outlook sends the result back.

That is it!

No to the details on how to set this up!
1. Outlook script:
a. In Outlook (I am using 2007 but this should work on the previous ones just fine), click Tools/Macro/Visual Basic Editor.
b. Paste this script into the editor:

' (C) Dmitry Sotnikov
' http://dmitrysotnikov.wordpress.com
' Add this to your Outlook macros project
' Then associate SaveAsText with a rule procesing
' emails from your address with a keyword in subject

' This is to have a Sleep function in Outlook
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

' The main function saving the script email as text
' and sending back the transcript
Sub SaveAsText(MyMail As MailItem)
    ' Export email (with PowerShell script in body) as a text file
    MyMail.SaveAs "c:\scripts\outlook.ps1", olTXT
    
    ' Create a response email
    Dim reMail As Outlook.MailItem
    Set reMail = MyMail.Reply
    
    ' wait till transcript is available
    Set fs = CreateObject("Scripting.FileSystemObject")
    While Not fs.FileExists("C:\Scripts\email_transcript.txt")
        Sleep 1000
    Wend
    
    ' attach the transcript and send it back
    reMail.Attachments.Add "C:\Scripts\email_transcript.txt"
    reMail.Send
End Sub

c. Close the Editor.

2. Create a PowerShell script which processes the script (removes the message header, executes, saves transcript). I called it execute_email.ps1 and saved to c:\scripts. Here’s the script:

# (C) Dmitry Sotnikov
# http://dmitrysotnikov.wordpress.com
# This is a PowerShell companion script for Outlook
# macro processing PowerShell commands from email

# Delete any previous transcripts and start a new one
Remove-Item c:\Scripts\email_transcript.txt -ErrorAction SilentlyContinue
Start-Transcript c:\Scripts\email_transcript_temp.txt

# wait till Outlook saves the script email
while ( -not (Test-Path c:\Scripts\outlook.ps1)) {
    Start-Sleep -Seconds 1
}

# Read the script, skip the header lines, execute the rest
Get-Content c:\Scripts\outlook.ps1 | Where { $i++ -gt 4 } > c:\Scripts\justscript.ps1
. c:\Scripts\justscript.ps1

# Remove the old script
Remove-Item c:\Scripts\outlook.ps1 -ErrorAction SilentlyContinue
Remove-Item c:\Scripts\justscript.ps1 -ErrorAction SilentlyContinue

# Stop transcript and make it available for Outlook to send back
Stop-Transcript
Rename-Item c:\Scripts\email_transcript_temp.txt -NewName email_transcript.txt

3. Create a cmd file which starts PowerShell and executes the script. I called it execute_email.cmd, saved to the same folder c:\scripts and it just have one single line:
powershell.exe "c:\scripts\execute_email.ps1"

4. In Outlook click Tools/Rules and Alerts and create the rule, which executes the Outlook macro and the cmd:

Outlook rule to export PowerShell script, execute it, and send back the transcript

You have just created a remote execution system working from any phone or internet kiosk!

Let’s test it. For example, let’s say I need to add someone to a group. I just send the script to my email address:

A sample email with a PowerShell script

Outlook at my desk gets the email, saves it as text, kicks PowerShell execution, and sends me back the transcript.

Just make sure you change the keyword for something no one can guess, take your smartphone with you and go home. There’s no need to be sitting by your desk anymore. ;)

Acknowledgments: this is based on a great Lifehacker forum post on shutting down a computer based on a message. They also have posts on using other email clients such as Thunderbird or Mac Mail.app.

For your convenience I am also attaching the script files:

Tags: , , ,

Retrieving Domain Password Policies

AD cmdlets 1.1 add a few nifty features which let you easily access password-related settings of your domain. Basically, all related properties are just a part of the attributes domain objects have, so you can do:

[PS] C:\>Get-QADObject scorpio.local/ | Format-List Name, *Password*, *Lockout*

Name                     : scorpio
MinimumPasswordAge       : 1 days
MaximumPasswordAge       : 42 days
PasswordHistoryLength    : 24 passwords remembered
MinimumPasswordLength    : 1 characters
LockoutDuration          : 30 minutes
LockoutTreshold          : 0 invalid logon attempts
ResetLockoutCounterAfter : 30 minutes

Or if you do not want to specify the domain name explicitly:

[PS] C:\>(Get-QADRootDSE).Domain | Format-List Name, *Password*, *Lockout*

Name                     : scorpio
MinimumPasswordAge       : 1 days
MaximumPasswordAge       : 42 days
PasswordHistoryLength    : 24 passwords remembered
MinimumPasswordLength    : 1 characters
LockoutDuration          : 30 minutes
LockoutTreshold          : 0 invalid logon attempts
ResetLockoutCounterAfter : 30 minutes

Another tip is that you can actually get a hold of the Domain property for any AD account: e.g. user.

So if you have:

$user = Get-QADObject Dmitry Sotnikov

And want to learn the password policies you can just do:

$user.Domain | Format-Table Name, *Password*, *Lockout*

Nice and easy!

This all applies to domain policies. Fine-grained password policies have their own set of cmdlets as well.

Tags: , , , , , ,

PowerShell CTP2 and PowerGUI

Brave hearts (which is a significant part of PowerShell geeks) already know that the PowerShell team has just released CTP2 (pre-beta Community Technology Preview) of PowerShell v2.

If you are one of them, we have just released an update for PowerGUI (1.0.16) which is fully compatible with this new build. As usual, this is available from our downloads page.

Tags: , , ,

Nested AD Groups and PowerShell

Ability to expand nested Active Directory groups is a very useful feature which got added in the recently released AD cmdlets 1.1.

Before that you could get direct group membership:

[PS] C:\>Get-QADGroupMember Administrators

Name                           Type    DN
----                           ----    --
Administrator                  user    CN=Administrator,CN=Users,DC=...
Enterprise Admins              group   CN=Enterprise Admins,CN=Users...
Domain Admins                  group   CN=Domain Admins,CN=Users,DC=...
Exchange Organization Admin... group   CN=Exchange Organization Admin...

But then you got stuck with just direct members and had to go through each subgroup to get the full list of all indirect members - who in AD world get exactly the same rights as the direct guys but are somewhat hidden. Not anymore! Now with a simple -Indirect parameter you can get them all:

[PS] C:\>Get-QADGroupMember Administrators -Indirect

Name                           Type   DN
----                           ----   --
Administrator                  user   CN=Administrator,CN=Users,DC=...
Enterprise Admins              group  CN=Enterprise Admins,CN=Users...
Domain Admins                  group  CN=Domain Admins,CN=Users,DC=...
Exchange Organization Admin... group  CN=Exchange Organization Admin...
Temp Account                   user   CN=Temp Account,OU=Demo,DC=sc...

And if you just want to see who sneaked in, Compare-Object is your friend:
[PS] C:\>Compare-Object (Get-QADGroupMember Administrators) (Get-QADGroupMember Administrators -Indirect)


InputObject                                    SideIndicator
-----------                                    -------------
CN=Temp Account,OU=Demo,DC=scorpio,DC=local    =>

I don’t know who this Temp Account is, but I am removing him from admins right away!

Tags: , , , , , ,

What’s new in AD cmdlets 1.1.0?

Here’s a quick summary of the new and exciting features added in Quest’s free AD cmdlets 1.1.0 just published on the web (I plan to provide more details and examples next week):

1. Get-QADGroupMember -Indirect - this new parameter allows you to retrieve complete group membership for nested AD groups in one command!

2. Permission management cmdlets:

  • Get-QADPermission,
  • Add-QADPermission,
  • Remove-QADPermission,
  • Get-QADObjectSecurity,
  • Remove-QADObjectSecurity.

3. New parameters of Get-QADUser:

  • HomeDirectory (string)
  • HomeDrive (string)
  • ProfilePath (string)
  • LogonScript (string)
  • Email (string)
  • AccountExpiresBefore (DateTime)
  • AccountExpiresAfter (DateTime)
  • AccountNeverExpires (bool)
  • PasswordNeverExpires (bool)

4. New parameters of Set-QADUser

  • HomeDirectory (string)
  • HomeDrive (string)
  • ProfilePath (string)
  • LogonScript (string)
  • Email (string)
  • AccountExpires (DateTime, nullable)
  • PasswordNeverExpires (bool)
  • UserMustChangePassword (bool)
  • TsProfilePath (string)
  • TsHomeDirectory (string)
  • TsHomeDrive (string)
  • TsWorkDirectory (string)
  • TsInitialProgram (string)
  • TsMaxDisconnectionTime (TimeSpan)
  • TsMaxConnectionTime (TimeSpan)
  • TsMaxIdleTime (TimeSpan)
  • TsAllowLogon (bool)
  • TsRemoteControl (int)
  • TsReconnectionAction (int)
  • TsBrokenConnectionAction (int)
  • TsConnectClientDrives (bool)
  • TsConnectPrinterDrives (bool)
  • TsDefaultToMainPrinter (bool)

5. New properties of User object

  • HomeDirectory (string)
  • HomeDrive (string)
  • ProfilePath (string)
  • LogonScript (string)
  • AccountExpires (DateTime, nullable)
  • PasswordLastSet (DateTime, nullable, readonly)
  • PasswordAge (TimeSpan, nullable, readonly)
  • PasswordExpires (DateTime, nullable, readonly)
  • LastLogonTimestamp (DateTime, nullable, readonly)
  • LastLogon (DateTime, nullable, readonly)
  • LastLogoff (DateTime, nullable, readonly)
  • AccountIsDisabled (bool)
  • AccountIsLockedOut (bool)
  • PasswordNeverExpires (bool)
  • UserMustChangePassword (bool)

6. Set-QADGroup now has GroupType and GroupScope parameters (to change group type and scope ;))
7. New cmdlet Get-QADRootDSE
8. Disambiguation prefixes in Identity parameter: e.g. Get-QADUser ‘dn=cn=object_with@sign’
9. Access to default domain password policies through the domain object:e.g. Get-QADObject mydomain.local/ | format-list *
10. Functionality specific to Quest ActiveRoles Server (this will only work if you have the commercial app):

  • Access template link management,
  • Dynamic groups.

Lots of cool and exciting features and numerous bugfixes.

You can download the beta on the Quest’s AD cmdlets page. Please provide your feedback in the AD PowerShell discussion forums.

Tags: , , , , , , , , ,

How to create a PowerPack?

Kirk “Poshoholic” Munro has just posted a flash tutorial on creating PowerPacks (extending PowerGUI and sharing the extensions with others). He starts with basics and goes into more advanced stuff (script nodes/links/actions/dynamic trees).

Kirk has created a lot of the PowerPacks shipped with PowerGUI and shared in the library so his tips and tricks are definitely worth watching!

Check it out here!

Tags: , , , ,

Groove strikes back: Live Mesh

Am I the only one seeing that Microsoft’s Live Mesh announced yesterday is conceptually a new version of Groove?

Ever since Microsoft bought Groove in 2005 the product seemed to be a foster child in the Office family. Not included in most of Office SKUs, not really advancing the technology, lacking a clear place on the family picture (instead standing somewhat vaguely behind the really loved SharePoint).

Last week on the MVP Summit Groove MVPs applied significant pressure on Ray Ozzie and Steve Ballmer (see transcript) trying to get an answer of where Groove is going and basically not getting much. I would summarize the official vision as: Groove is becoming more integrated with SharePoint, but it will not become a complete offline solution for it any time soon, nor is there a decision whether it should.

Well, yesterday, Groove stroke back. And it is not called Groove any more - it’s Live Mesh. But just go through the screenshot gallery and you’ll see Windows folders becoming Groove-like workspaces with file sharing and sync across devices with associated members lists, news, and discussions.

In addition to the basic Groove functionality, there is also a web access page (hosted by Microsoft) and remote desktop functionality (kind of lame if the goal is to replace an application engine, but probably fine for troubleshooting and getting to your workplace remotely during trips).

So to me, Live Mesh is not Microsoft’s OS in the cloud. It is the new real Groove (as opposed to the old Groove shipped with Office) developed by the core Groove fans (including Ray himself) and having no SharePoint dependencies this time around. ;)

An offtopic for this blog, but I could not help it.

Tags: , , ,

Keep group membership under control

How do you keep group membership in your AD automatically adhere to the right lists of members (whatever “right” means in your case)? We had an interesting thread on that in the AD discussion forum and below the script which came out of it.

Basically, in Matthew’s case he is getting text files for each group listing the members which should be there (I am guessing some HR DB exports). He obviously cannot just remove everyone and recreate membership from scratch - because this can affect users (imagine if this is a DL and en email is being sent when you do this) and cause additional stress on AD. Instead, he needs to detect which users should be taken out and remove them from the group, and detect the ones which he needs to add and add them.

It turns out that with PowerShell this basically comes down to a single command (Compare-Object) comparing the file and the actual output, and an If performing the corresponding action (add or remove) based on the direction.

The only gotcha is to use samAccountName or another simple attribute rather than a DN. This is due to a bug in PowerShell (which you can get fixed if you follow the link and vote for it :))

Apart from that, everything is very straight-forward (you can obviously wrap the code into a function and use it for multiple groups):

# name of the group to update
$groupname = Managers
# import a file: one samAccountName per line
$users = get-Content c:\user_logon_names.txt
# get samAccountNames of current members into an array
$members = @()
Get-QADGroupMember $groupname | ForEach-Object { $members += $_.samAccountName }

# make group membership exactly as it is in the file
Compare-Object $users $members | ForEach-Object {
    If ( $_.SideIndicator -eq <= ) {
        Adding $($_.InputObject) to $groupname
        Get-QADUser $_.InputObject | Add-QADGroupMember $groupname
    } elseif ( $_.SideIndicator -eq =>  ) {
        Removing $($_.InputObject) from $groupname
        Get-QADUser $_.InputObject | Remove-QADGroupMember $groupname
    }
}

The script uses Quest AD cmdlets, so you would need to have them installed and loaded either explicitly in the script (with Add-PSSnapin) or in your PowerShell profile.

Tags: , , , , ,

Microsoft working on PowerShell for AD

One of the key news from the MVP Summit is that Microsoft team has confirmed that they have PowerShell support for Active Directory in the works.

Obviously all the details are under NDA except for the fact that they are working on that - Dushyant Gill allowed us to share that.

I guess the only other thing I can tell is that it is not coming to you right away - you will have to be patient for that a little bit longer. However… :) let me tell you that: whatever you learn while using Quest AD cmdlets today will be the knowledge you will be able to reuse eventually when Microsoft’s version ships. ;)

Tags: , , , , , ,

LINQ for PowerShell?

If PowerShell is the language for IT professionals in the Windows world, similar to .NET/C# getting the niche for programmers, does this mean that we should get a PowerShell way of working with databases, similar to the LINQ extensions the .NET folks got?

Joel has just posted a very good article on why the notion on PowerShell provider would not work for SQL data access.

So what is the alternative we are getting? Invoking Run-SQLCmd and supplying a SQL query is OK for SQL gurus, but is way to complex for me. After all, there are reasons why people are using AD cmdlets to manage their Active Directory instead of going the ADSI path: reduced complexity, unified PowerShell approach and not having to learn the LDAP query language. All of these apply to SQL, right?

And, by the way, I am not sure I know the answer. Like Richard, I wish SQL 2008 had more cmdlets covering all SQL administration tasks. But then, when looking at what LINQ did to .NET, I can’t help thinking that there might be yet another very elegant solution we are all missing. (Or maybe not, and like in AD, a carefully designed set of cmdlets would do the job.)

PowerShell is all about reducing the complexity and providing unified administrative approach across all platforms. I wonder what needs to be done to get this include the database world.

Tags: ,

Next Page »