Google

2017-02-11

Google Spaces

Update 2021-07-21:
Not sure why I bother but it annoyed me that my last blog post here was about yet another service in Google Graveyard. I've enjoyed many of the tools from Google that were not popular enough, I guess, and got killed. I still use Pocket to save articles that I think I will go back at some point though I rarely ever do. It's still a good tool for digital hoarding, if you are into that.
--
I am a Pocket user, I happily pay them ~$30 a year for the search service they provide for the articles I save in my account.

I am, however, getting warmed up to Google Spaces for some time now. They announced it in May 2016 as a tool for "Small Group Sharing", which is pretty much how I use it right now.

I have a space to post technology stuff I read and find interesting. A post could be about a StackOverflow question, a blog post, some piece of tech news, github code etc... If you are reading this post, you are welcome to join!

There is an iOS app and an Android one that you can use to find out about posts.


2015-11-20

PowerShell Function to Fix System Path

Before Windows 7, max length of the PATH environment variable was 1024 chars. Many years ago, we found ourselves troubleshooting a huge problem in our environment that was caused by faulty install scripts that were appending the PATH variable many times taking the path length over the limit and causing all sorts of problems as PATH was cut at 1024.

Windows 7 increased the length but I've seen the same problem in other environments. I have seen a PowerShell question over at StackOverflow today, and apparently the person was running into the problem because the max path length was exceeded. When that happens, Windows just ignores the characters after the max length is reached.

I had written a PowerShell function that would search for and remove the duplicates that would increase the length of PATH unnecessarily. I put it up as a gist at github & sharing it here. Hopefully it helps someone.

2015-11-15

ConvertFrom-String or Not

One of the new cmdlets in PowerShell v5 is ConvertFrom-String. It's using a Microsoft Research Technology called "FlashExtract", a machine learning technology. I was hoping that it would finally rescue us from RegEx! ~/Yay/Heh/g

I've seen several MVPs write about it, mostly before Microsoft released the final version but some recent as well:
The premise is that, you provide samples of the data and specify what you want to extract out of it, and FlashExtract engine is supposed to be smart enough to evaluate the template you provided and get the properties you want. If only life was that simple...

Alright here is our victim... Let's take a look at our WinSxS (component) folder. People always complain about how big it is and how unwieldy it gets over time as more and more patches come down. It's a tempting target to clean up old patches and other binaries but also the shortest path to breaking your Windows if you remove a needed file.

Anyway, I am more interested in creating an easy to read PowerShell object based database out of it.

They look like this:
C:\windows\WinSxS\amd64_aspnet_compiler_b03f5f7f11d50a3a_10.0.10586.0_none_8037b2faf04e42be

Microsoft tells us how it is formatted - What's that awful directory name under Windows\WinSxS. Oh, it's supposed to be a 'friendly-name' with this format:

proc-arch_name_public-key-token_version_culture_hash

So, the name is separated by a bunch of underscore characters. This makes our life so easy to parse it, right? Wrong! "name" too can have "_", as well as a bunch of other chars like "." and "-".

Still, after two minutes of RegEx tinkering I got that function and it worked perfectly:

PS C:\Users\Adil> (Get-WinSxSRegex)[1,100,1000]

Processor      : amd64
Name           : 1394.inf
PublicKeyToken : 31bf3856ad364e35
Version        : 10.0.10586.0
Language       : none
Hash           : 87b4eef7b03f2543


Processor      : amd64
Name           : c_bluetooth.inf.resources
PublicKeyToken : 31bf3856ad364e35
Version        : 10.0.10586.0
Language       : en-us
Hash           : 14d0dc285c89e9ba


Processor      : amd64
Name           : microsoft-windows-b..iagnostic.resources
PublicKeyToken : 31bf3856ad364e35
Version        : 10.0.10586.0
Language       : hu-hu
Hash           : 2d6242e40cbc20a9

The example above is showing the 1st, 100th and 1000th records. Luckily, they are good examples to demonstrate the challenge that ConvertFrom-String cmdlet faces.

So, I took a couple of folder names and started to put the property names as I would like them.

{Processor*:amd64}
This is supposed to collect the Processor, but if I do not use "*",  it does not work. It seems that when the first matching line is found, that becomes a record, and to be able to get more records, we need that * in there.

{Name:1394.inf}
This will work, but only for some. See, because it starts with a number, without any other sample for Name, engine thinks we want something that starts with 'numbers', and gives us only matching folder names that has numbers in the 'name' section. so, we need to give different Name samples.

{PublicKeyToken:31bf3856ad364e35}
To a human eye, this is pretty easy to locate. It's a hex, and if we were using regex, we would use something like '[a-fA-F0-9]+', in other words one more numbers or characters that stops are between 'a' and 'f'. Well, we are not dealing with a human here.

{Version:10.0.10586.0}
OK, this must be the easiest to identify however you look at it. 4 digits with "." between them

{Language:none}
Language is a bit tricky. It could be none or some international language designation like "en-us" for US English.

{Hash}
Finally, we have the hash, which is pretty much like the PublicKeyToken

I will leave it here. I tried a couple of alternatives in templates, and providing more was not getting me anywhere as I kept on getting error messages telling me ConvertFrom-String cmdlet could not come up with a way to extract data using my template and suggested I email Microsoft. I did, though I am not holding my breath on that.

I really like the concept of ConvertFrom-String, and in my opinion, It would be one of the most powerful features of PowerShell but to me, it feels like it is just not there YET.

Sample templates I used are below. If you will use the functions, pipe them to |out-gridview. It is pretty easy to detect problems that way.


2015-11-01

Clear Recent Documents List on OS X

TL;DR: 
Remove the relevant .sfl file located under the following directory:
~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/

- * -

It's Halloween! Time for scary stuff. As the tech admin of the house, my job tonight was to make sure a series of old scary movie classics, think of Poltergeist , can be streamed to TV.

I decided to clear the existing list of QuickTime Player's Recently Opened Files and pre-populate the list with the scary movies. This can be done from GUI:

QuickTime Player > File > Open Recent > Clear Menu

Nothing is wrong with that, but I wondered how I would do that from Command Line. Interestingly, I could not locate a setting in a .plist. If you Google, you will find suggestions similar to below, but on El Capitan (10.11), these settings did not exist


adil-imac-1:~ adil$ defaults read com.apple.QuickTimePlayerX.LSSharedFileList RecentDocuments
2015-10-31 23:35:21.932 defaults[12222:1913397] 

The domain/default pair of (/Users/adil/Library/Preferences/com.apple.QuickTimePlayerX.LSSharedFileList, RecentDocuments) does not exist

So, to find which file I needed to look at, I decided to use good old, 'find' command. The technique is well known:
  • First create a temporary file
  • Then take action (launch a movie) to cause a file change, as new movie is added to the recent items
  • Lastly do a find for all files newer than the temporary file we created


touch /private/var/tmp/now
sudo find / -newer /private/var/tmp/now

This, usually works, but file list could be larger. In my test, it returned 120 files in that short time frame.

There are other issues with this approach:
  • It's relatively slow!
  • If you have external drives mounted, you might need to try -x to avoid scanning them

There is a better way! Try Spotlight from command line: mdfind.  

Approach is pretty similar to find, except there is no need to create a temporary file. 
  • Make the change
  • Immediately run mdfind telling it to look for changes in the last, say 15secs.

mdfind 'kMDItemFSContentChangeDate>$time.now(-15)'

k: kind
MD: Metadata
FS: File System
$Time variable is explained in the Apple Developer Documentation link below

Apple Developer documentation on MetaData Query Expression Syntax has some examples on using MetaData information. There are a few examples here as well.  

mdfind command pointed to this file:

~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.apple.quicktimeplayerx.sfl

What's .sfl? Not exactly sure but it probably stands for 'Shared File List' and apparently is meant to hold "Bookmark Data".

The content of the .sfl file is kinda cryptic. Incidentally, I found a blog post from a couple of days ago that goes about Exposing "BookmarkData"

When you manually clear the Recent Documents, the .sfl file becomes shorter in size. However, as there does not seem to be a tool to manipulate the file yet, deleting the file seems like a safe way to clear bookmarks.

Just in case people are wondering, yes, VLC has 'org.videolan.vlc.sfl' located at the same folder, and removing that file cleared the VLC recent docs as well. 


2015-09-13

No Birthday Reminders for People You Don't Know

I made it a habit to add my Google account to "Internet Accounts" in OS X, which also enables me to see calendar events on the notification window when I am on my Mac. Problem is that I get bombarded with birthday reminders for people I do not personally know.




My first shorthand solution was to uncheck "Calendar" from Google Account in the OS X Calendar application.


That solves the problem on Mac, but I am 'still' a Chrome user and see these alerts on Chrome too.

There is a long article on Techcrunch from early 2015 complaining about this issue, but a note at the end that Google was working on a solution. No update after that, but I think Google has fixed it now, and you can stop these alerts at the source :


  • Go to google.com/calendar, you should see Birthdays under Calendars
  • Under My calendars, click on dropdown box to the right of Birthdays and select "Calendar Settings" or "Edit Notifications"


  • Select "Contacts only" option next to "Show birthdays from"


  • Hit "Save" at the bottom


Enjoy!

2015-04-25

Chrome Remote Desktop Eating Up Free Space

Last night, I noticed my iMac was a bit unhappy as it had run out of (250GB) SSD space. I recalled that the night before I had installed Google Drive and left it synchronizing and also had not yet moved the new Photos app to larger secondary drive, which was importing more and more photos in the last few days. By default Photos app installs at ~/Pictures. So they were both eating up precious SSD space.

To  move the Google Drive to secondary drive:
  • exited the app, 
  • moved the ~/Google Drive/ folder to second drive, 
  • restarted the app
  • Disconnected and reconnected the account choosing the new location where I moved the directory

To move the new Photos App to the secondary drive:
  • exited the app
  • moved the ~/Pictures/Photos.Library.photoslibrary to secondary drive, next to my iPhoto Library
  • Restarted the app
  • It detected the new location and prompted me to select the library.



April 22 was the earth day. So, today, I took my daughter to her school and together with her friends and their parents we cleaned up the school playground. 

I came back home and checked the Disk Utility to see if my clean up from yesterday had tidied things up enough. To my surprise, there was still a ton of space consumed by "something".

I launched my favorite tool "Disk Inventory X" which quickly detected that I had a 150Gig log file: 
/var/log/org.chromium.chromoting.log.0

Looking at the folder, it seemed that the log file was roller over, but the old log file somehow kept going and consumed the whole disk. This file is created by Google's awesome "Chrome Remote Desktop" extension which I really like to use. 

Unfortunately, there is currently an open issue for this case: 452121. It's not clear when this bug was introduced but it has been there for the last few versions of chrome (38..42) and some people have seen it eating up to a TeraByte of space, unless "remoting_me2me_host" process is killed and restarted with: 

killall -HUP remoting_me2me_host

Well, thanks Google for giving a reason to clean up ;)

2015-03-11

Automount NAS share on Mac

NAS

It's been several years since I was sold on using a NAS to solve the data sharing problems at home. QNAP TS-439 Pro was my choice. Frequent firmware updates kept it up to date and relevant. When something becomes popular, say Google Drive, you get a firmware update that integrates it to NAS. One firmware for all versions of QNAP, new and old. So, your old hardware does not become irrelevant in a year or two.

SMB Versions / Issues

Traditionally AFP (Apple Filing Protocol) was the protocol preferred on Macs for network access. Of course, Windows used SMB (Server Message Block) protocol of Microsoft. Currently Windows 8.1 uses SMB v3. With Maverick (v10.9) release, Apple backed away from their own AFP protocol in support of SMB v2, and that was a problematic release to say the least. With 10.10 Yosemite Mac OS X supports v3 as the default sharing protocol.

QNAP, unfortunately, does not support SMB 3.0. With the release of firmware 4.1.0 Build 0605, QNAP changed samba default protocol to SMB 2.0

SMB Nightmare
After upgrading my Macs to 10.10, I started connecting to my QNAP via SMB 2.0, and problems started. The biggest issue was that whenever I wanted to copy a bunch of files to NAS, it would start copying, and after the first one or two, I would get an error that read:

"Operation cannot be completed because file is in use",

and copy would fail.

I could workaround the problem by going to terminal and use 'scp' to transfer files to NAS, but it was not that convenient.

Many people complained about this problem on Apple forums. None of the solutions offered helped in my case, so I went back to AFP, and that problem disappeared. I still see other issues when, for example, editing a word document: Word somehow cannot save the file the first time, then lock is released and file is saved. That's more of an Microsoft office problem, rather than AFP/SMB though.

Automounting NAS

One problem that annoyed me was that even with AFP, I had to connect to my NAS over and over. It was not as bad as SMB which kept on prompting me, but it was still an hassle, especially on my Macbook, which is not always near my wifi to connect to home NAS.

Over the weekend, I took another look at Apple's Autofs: Automatically Mounting Network Files Shares whitepaper  and implemented it for my macs.

The implementation is simple. Apple now has /etc/auto_master file, which can lookup other files for indirect mounting information and automatically carry out the mount instructions.

adil-imac-1:~ adil$ ls /etc/auto*

-rw-r--r--  1 root  wheel   149 Sep 19 03:15 /etc/auto_home
-rw-r--r--  1 root  wheel   233 Mar  8 17:47 /etc/auto_master
-rw-------  1 root  wheel   203 Mar  8 21:32 /etc/auto_qnap
-rw-r--r--  1 root  wheel  1935 Sep 19 03:15 /etc/autofs.conf

Now let's take a look at auto_master, notice the last line I added

adil-imac-1:~ adil$ cat /etc/auto_master
# Automounter master map
#

+auto_master  # Use directory service
/net   -hosts  -nobrowse,hidefromfinder,nosuid
/home   auto_home -nobrowse,hidefromfinder
/Network/Servers -fstab
/-   -static
/-   auto_qnap

And here is what /etc/auto_qnap look like:

adil-imac-1:~ adil$ sudo cat /etc/auto_qnap

/opt/mnt/myshare -fstype=afp afp://MyUser:MyPassword@MyServerIP/MyShare
/opt/mnt/public  -fstype=afp afp://192.168.1.5/Public

My initial thought was that I would use /Volumes to mount the folders, but that's really a folder Mac uses for dynamically mounting everything and auto-unmounts them. It's more like a temp directory. So I decided to use a folder more common in linux distributions /opt/mnt, which did not exist on my system of course, so I created it:

sudo mkdir -p /opt/mnt

then I created the auto_qnap file and changed its permissions to be read/writeable by root only, as it might need to contain password to connect to certain network shares.

sudo touch /etc/auto_qnap
sudo chmod 600 /etc/auto_qnap

The format for AFP and SMB are similar:

/Local/Mount/Folder   -fstype=afp    afp://MyUser:MyPassword@MyServerIP/MyShare
/Local/Mount/Folder   -fstype=smb    ://MyUser:MyPassword@MyServerIP/MyShare


I used IP address as it is static, but you could use FQDN of your Network Server. Once this is all done, you can run the following command to clear the cache and verbosely execute the mount command:

sudo automount -vc

I then created a symlink to the new mount points and put them in the Finder Sidebar:

ln -s ~/Pictures/PicsOnNAS /opt/mnt/public/pictures

Using this setup, I could now tell Flickr Uploader Mac Client to watch ~/Pictures/PicsOnNAS folder, and automatically upload pictures when I add a new ones there.

There you have it. It's not as easy as it is for Windows which automatically detects connection state of the mapped network drives, but close enough. Enjoy!

2015-03-10

Flickr Uploadr for Mac


Last year Yahoo revived Flickr, and they offered something none other are offering even now: 1TB free space for your pics and videos. I had my Flickr account from early 2000s that, like everyone else, I had not used for ages. I started using Flickr again...

Yahoo offered Automatic Camera Upload feature from phones, which is pretty much what everyone else was offering and it had a couple of hick ups especially when new iPhones with newer iOS arrived, but other than that it pretty much worked OK.

I kept on using it in tandem with Google+, photos uploading to both. One nice feature I really liked about Flickr was that it allowed me some family members who live worlds apart to my 'family' and automatically share pictures with them when pictures were uploaded, which is not a something I could do with Google+



This setup worked fine 'going forward' but until now there was not a good way of uploading pictures. A few weeks ago, Yahoo released Flickr Uploadr for Mac (beta) and I found out that it works perfectly for my use case.


It can use iPhoto, which I do not really use. But more importantly, it can upload folders of pictures and it creates an Album with the name of the folder.

The way I organize my pic folders is like this:

YYYY > YYYY.MM > YYYY.MM_Event_Location

So when I upload a year, it creates albums with the name YYYY.MM_Event_Location


Of course not all my folders were perfectly named like that, sometimes I had to use PowerShell to fix them (may be subject of another blog post).

It not only uploads them once, but can upload any new pictures you put in those folders. So, if you have an upload folder, for example, anything you drop in there will be automatically uploaded.

It also has de-duplication. Not a very smart one at the moment, so if you have the same picture with a different size, it will not detect that but if you have the same picture in more than one folder, it will detect that and will not upload it again.

I also had to deal with (SMB) connection issues I was having from my iMac to my QNAP NAS where I keep gigs and gigs of pics, and came up with a method to Automatically mount the NAS whenever I needed it without bugging me with credentials and stuff (definitely another post).


Here is link to feedback forum for the product: https://www.flickr.com/help/forum/en-us/72157649816391975/

That's all for now! Enjoy, and let me know what you think if you try it.


2015-02-22

Fixing Home and End buttons in Mac Terminal

I hate it when Terminal in my mac scrolls up when I hit 'Home' key, or all the way to the bottom, when I hit 'End' key. As most would do, I expect 'Home' key to take me to the beginning of the line, and 'End' to the end of it.

I can of course use 'control+a' and 'control+e' to do that, but it's tough to beat muscle memory.

There is a Stack Exchange 'Ask Different' entry for this, but the keys mentioned there for terminal were not working for me: https://apple.stackexchange.com/questions/16135/remap-home-and-end-to-beginning-and-end-of-line

Terminal Keyboard Binding




What seems to work for me is:
* \033[H -> Home
* \033[F -> End

These can be set from Terminal > Preferences > Keyboard as shown in the screenshot. Enjoy!

2015-01-22

How to move iPhoto Library to a different drive

I wanted to move my iPhoto Library from my SSD disk to secondary SATA disk on my iMac.
It's pretty easy to do:

  • Create a folder in the target drive
  • Hold down Command button and move the "~/Pictures/iPhoto Library.photolibrary" directory to the target drive 
  • Double click on the "iPhoto Library.photolibrary" directory to launch iPhoto. 

That's it! It remembers the directory next time you launch it.

















In the screenshot above, you can see the iPhoto Library directory at the bottom, and the target directory (/Data/pictures) on the secondary (SEAGATE) drive at the back right while library is being moved.

Note: My iPhoto version is 9.6. Also found this KB article from Apple describing the same process, so this seems to work for other versions as well: http://support.apple.com/en-us/HT1229

2014-12-11

Even False counts in PowerShell

I was testing one of my PowerShell scripts and noticed a problem, a PowerShell gotcha if you will, that's actually quite easy to miss.

I had something like this in the script:

$rebootGroups=@(([adsisearcher]"(&(objectCategory=computer)(cn=$env:COMPUTERNAME))").FindOne().Properties.memberof -replace '^CN=([^,]+).+$','$1' -match 'grp.reboot.')


That is a one liner to check if the current computer where the script is running is member of any AD groups that starts with 'grp.reboot'.

Later in the script, I had an if statement checking if I got any groups back and act on them:

If ($rebootGroups.count -ne 0) {...}


If computer is indeed in any one of these groups, we would get a count greater than zero.
What happens if computer is not member of any such groups? We might expect to get back a count of '0', but in fact we get a count of '1'.

Here is proof:

PS C:\> $a=@($false)

PS C:\> $b=$false

PS C:\> $c=$true

PS C:\> $a.count;$b.count;$c.count

1

1

1



It could be argued that the first one makes some sense. At the end, it is an [array] type with a single element and hence count should not be 0.

The more interesting is the second one '$b', which is a boolean and regardless of its value ($true or $false), the count property of a boolean is 1.

So, in this case, we would fix the logic by checking if $false was returned, as ($rebootGroups.count -ne 0) will never be equal to 0!

2014-10-29

PwdLastSet vs PasswordLastSet Property

One of the interesting things, when you run "GET-ADCOMPUTER" cmdlet to find out the last time computer password was set, is that there are actually two different properties for that value. One of them, pwdLastSet is NOT so friendly...

D:> Get-ADComputer adil-7600-8 -Properties pwdlastset
pwdlastset        : 130575614253222449


The other property, PasswordLastSet is as helpful as it gets:

D:> Get-ADComputer adil-7600-8 -Properties passwordlastset
PasswordLastSet   : 10/12/2014 12:23:45 AM

Why? Well, I am not sure why Microsoft decided to provide two different properties for this value, probably pwdLastSet is easier for calculations. That is probably why the type of pwdLastSet is signed 64 bit integer (int64) vs. DateTime for PasswordLastSet

D:> Get-ADComputer adil-7600-8 -properties pwdlastset |get-member pwdlastset

   TypeName: Microsoft.ActiveDirectory.Management.ADComputer

Name       MemberType Definition
----       ---------- ----------
pwdlastset Property   System.Int64 pwdlastset {get;set;}

D:> Get-ADComputer adil-7600-8 -properties passwordlastset |get-member passwordlastset


   TypeName: Microsoft.ActiveDirectory.Management.ADComputer

Name            MemberType Definition
----            ---------- ----------
PasswordLastSet Property   System.DateTime PasswordLastSet {get;set;}

2014-06-04

DD-WRT OpenWRT Bricking Router and springing it back to life

These are my notes from bricking my router and bringing it back!

How it all began? 
I have a love-and-hate relation with DD-WRT. I have been on and off for the last 5 years. After recent revelations of backdoors on routers and modem, I decided to go back to it.

DD-WRT
Upgrading from stock firmware to DD-WRT is documented well enough.
I never bothered with 30/30/30 cycles when upgrading. Simply used firmware upgrade page and pointed it to the DD-WRT image.

OpenWRT
It's been working OK but today I decided to check out OpenWRT. So, I downloaded the image and flashed it. Unlike DD-WRT, there is not a web service readily running. As expected, Wi-Fi is not enabled at this point either. So, back to good ol' Putty and telnet/ssh business...

First Login
## http://wiki.openwrt.org/doc/howto/firstlogin
## First telnet (192.168.1.1:23)

login as: root
root@192.168.1.1's password:

BusyBox v1.22.1 (2014-06-03 08:07:42 CEST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 BARRIER BREAKER (Bleeding Edge, r40982)
 -----------------------------------------------------
  * 1/2 oz Galliano         Pour all ingredients into
  * 4 oz cold Coffee        an irish coffee mug filled
  * 1 1/2 oz Dark Rum       with crushed ice. Stir.
  * 2 tsp. Creme de Cacao
 -----------------------------------------------------
root@OpenWrt:~#

Get the Web Interface Up
## Install LuCI (web) using ssh 192.168.1.1:22
## http://wiki.openwrt.org/doc/howto/luci.essentials

 === IMPORTANT ============================
  Use 'passwd' to set your login password
  this will disable telnet and enable SSH
 ------------------------------------------


BusyBox v1.22.1 (2014-06-03 08:07:42 CEST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 BARRIER BREAKER (Bleeding Edge, r40982)
 -----------------------------------------------------
  * 1/2 oz Galliano         Pour all ingredients into
  * 4 oz cold Coffee        an irish coffee mug filled
  * 1 1/2 oz Dark Rum       with crushed ice. Stir.
  * 2 tsp. Creme de Cacao
 -----------------------------------------------------

# Let's get some help...
root@OpenWrt:/# help
Built-in commands:
------------------
        . : [ [[ alias bg break cd chdir command continue echo eval exec
        exit export false fg getopts hash help history jobs kill let
        local printf pwd read readonly return set shift source test times
        trap true type ulimit umask unalias unset wait

## OK. time to change password...
root@OpenWrt:/# passwd
Changing password for root
New password:
Retype password:
Password for root changed by root

## Keep on following wiki...
root@OpenWrt:/# opkg update
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/Packages.gz.
Updated list of available packages in /var/opkg-lists/barrier_breaker.
root@OpenWrt:/# opkg install luci-ssl
Installing luci-ssl (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-ssl_svn-r10265-1_brcm47xx.ipk.
Installing luci (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci_svn-r10265-1_brcm47xx.ipk.
Installing uhttpd (2014-04-08-a0c33bdbc4873210598acdccb292ff77fb6bf624) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/uhttpd_2014-04-08-a0c33bdbc4873210598acdccb292ff77fb6bf624_brcm47xx.ipk.
Installing uhttpd-mod-ubus (2014-04-08-a0c33bdbc4873210598acdccb292ff77fb6bf624) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/uhttpd-mod-ubus_2014-04-08-a0c33bdbc4873210598acdccb292ff77fb6bf624_brcm47xx.ipk.
Installing luci-mod-admin-full (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-mod-admin-full_svn-r10265-1_brcm47xx.ipk.
Installing luci-mod-admin-core (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-mod-admin-core_svn-r10265-1_brcm47xx.ipk.
Installing luci-lib-web (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-lib-web_svn-r10265-1_brcm47xx.ipk.
Installing luci-lib-core (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-lib-core_svn-r10265-1_brcm47xx.ipk.
Installing lua (5.1.5-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/lua_5.1.5-1_brcm47xx.ipk.
Installing liblua (5.1.5-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/liblua_5.1.5-1_brcm47xx.ipk.
Installing libuci-lua (2014-04-11.1-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/libuci-lua_2014-04-11.1-1_brcm47xx.ipk.
Installing libubus-lua (2014-05-06-9fda19140e65457d967ff6fe424e420f656c06c9) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/libubus-lua_2014-05-06-9fda19140e65457d967ff6fe424e420f656c06c9_brcm47xx.ipk.
Installing luci-lib-sys (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-lib-sys_svn-r10265-1_brcm47xx.ipk.
Installing luci-lib-nixio (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-lib-nixio_svn-r10265-1_brcm47xx.ipk.
Installing luci-sgi-cgi (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-sgi-cgi_svn-r10265-1_brcm47xx.ipk.
Installing luci-proto-core (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-proto-core_svn-r10265-1_brcm47xx.ipk.
Installing luci-i18n-english (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-i18n-english_svn-r10265-1_brcm47xx.ipk.
Installing luci-lib-ipkg (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-lib-ipkg_svn-r10265-1_brcm47xx.ipk.
Installing luci-theme-bootstrap (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-theme-bootstrap_svn-r10265-1_brcm47xx.ipk.
Installing luci-theme-base (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-theme-base_svn-r10265-1_brcm47xx.ipk.
Installing luci-app-firewall (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-app-firewall_svn-r10265-1_brcm47xx.ipk.
Installing luci-proto-ppp (svn-r10265-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/luci-proto-ppp_svn-r10265-1_brcm47xx.ipk.
Installing libiwinfo-lua (49) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/libiwinfo-lua_49_brcm47xx.ipk.
Installing libustream-polarssl (2014-03-25-fc0b5ec804ee43c532978dd04ab0509c34baefb0) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/libustream-polarssl_2014-03-25-fc0b5ec804ee43c532978dd04ab0509c34baefb0_brcm47xx.ipk.
Installing libpolarssl (1.3.7-1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/libpolarssl_1.3.7-1_brcm47xx.ipk.
Installing px5g (1) to root...
Downloading http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/px5g_1_brcm47xx.ipk.
Configuring luci-lib-sys.
Configuring liblua.
Configuring libuci-lua.
Configuring lua.
Configuring libubus-lua.
Configuring luci-lib-core.
Configuring luci-lib-nixio.
Configuring luci-sgi-cgi.
Configuring luci-lib-web.
Configuring luci-proto-core.
Configuring luci-i18n-english.
Configuring luci-mod-admin-core.
Configuring libiwinfo-lua.
Configuring luci-theme-base.
Configuring luci-theme-bootstrap.
Configuring luci-app-firewall.
Configuring luci-lib-ipkg.
Configuring uhttpd.
Configuring uhttpd-mod-ubus.
Configuring luci-mod-admin-full.
Configuring luci-proto-ppp.
Configuring luci.
Configuring libpolarssl.
Configuring libustream-polarssl.
Configuring px5g.
Configuring luci-ssl.
root@OpenWrt:/#

Almost there...
## Start the web server (uHTTPd)
root@OpenWrt:~# /etc/init.d/uhttpd start
Generating RSA private key, 1024 bit long modulus
Generating selfsigned certificate with subject 'C=DE;ST=Berlin;L=Berlin;CN=OpenWrt;' and validity 20140604233124-20160603233124
root@OpenWrt:~#

## login to web interface
https://192.168.1.1/cgi-bin/luci

Disappointment!
All good, and then I wanted to configure the wireless.That's when I realized I lost ability to use 802.11n. A bit of research and sure enough, only bg is supported.

OK, that's a deal breaker. Time to go back to DD-WRT or Stock image!

Unfortunately, every time I tried to use the firmware page, I got an error saying the firmware was not supported type. So, I got stuck. Then, I found instructions to do it from CLI and followed them

## Uninstall openwrt, and go back to stock Cisco E3000 stock firmware
http://wiki.openwrt.org/doc/howto/generic.uninstall

cd /tmp
wget http://downloads.linksys.com/downloads/firmware/1224655340609/FW_E3000_1.0.06.002_US_20140409_code.bin
mtd -r FW_E3000_1.0.06.002_US_20140409_code.bin firmware
rebooting...

Real Disappointment!!!
Unfortunately, that bricked the router = Blue Power Led kept on blinking forever!
I tried 30/30/30 reset; that did not bring back UI but got ping reply from 192.168.1.1

At this point, I was not sure if I could bring back the bricked router but kept on trying:

  • Directly connect PC network cable to Router
  • Open (elevated) Command Prompt (cmd)
  • cd /to/downloaded_dd-wrt-firmware_folder/ (for me c:\temp\w)
  • use tft to transfer firmware

 C:\Temp\w>tftp -i 192.168.1.1 put dd-wrt.v24-14929_NEWD-2_K2.6_std_usb_ftp-e3000.bin dd-wrt.v24-14929_NEWD-2_K2.6_std_usb_ftp-e3000.bin

Gave it 10secs and reboot (did not work). Then, while holding down reset button,

  • Disconnect Power Cable
  • Wait for 10secs
  • Reconnect Power Cable
  • Wait for 10 secs
Victory!
Voila! http://192.168.1.1 is accessible and Power Led is solid blue! My wireless router is back to life! OK,so I am back to where I was, but I think it is fair to call that a victory, right? Riiiight!

Time to reconfigure DD-WRT and blog this...experience!

Gist notes here as well: https://gist.github.com/AdilHindistan/0bfb376165313da693d0

2014-05-30

About PowerShell's PSBoundParameters

PSBoundParameters are useful, simple and could trip you!
Here is our test script t.ps1

param(
    $Name="foo",
    $LastName="bar"
)

"Name: $Name LastName: $LastName"
"PSBoundParameters : " 
$PSBoundParameters
    
"Removing 'Name'"
$PSBoundParameters.Remove('Name')

"After Removing 'Name'"
"Name: $Name LastName: $LastName"
"PSBoundParameters : " 
$PSBoundParameters

    
"Removing 'LastName'"
$PSBoundParameters.Remove('LastName')
    
"After Removing 'LastName'"
"Name: $Name LastName: $LastName"
"PSBoundParameters : " 
$PSBoundParameters

## Let's start testing
D:\> C:\temp\t.ps1
Name: foo LastName: bar
PSBoundParameters :      ## <--- It's empty

Removing 'Name'
False                    ## There was no 'Name' parameter to remove

After Removing 'Name'
Name: foo LastName: bar
PSBoundParameters :

Removing 'LastName'
False

After Removing 'LastName'
Name: foo LastName: bar
PSBoundParameters :

D:\>

So, what happened? Well, $PSBoundParameters did not have anything, because nothing was passed to the script.

Now let's try passing it parameters

D:\> C:\temp\t.ps1 -name Adil -LastName Hindistan
Name: Adil LastName: Hindistan
PSBoundParameters :       ## This returns 2 key/value pairs      

Key                                Value
---                                -----
Name                               Adil
LastName                           Hindistan

Removing 'Name'           ## <-- returns True, b/c it could remove the binding
True

After Removing 'Name'
Name: Adil LastName: Hindistan
PSBoundParameters :
LastName                           Hindistan  ## <- now, $PSBoundParameters only has one key/value

Removing 'LastName'
True

After Removing 'LastName'
Name: Adil LastName: Hindistan
PSBoundParameters :

Note that $Name and $LastName got their values assigned and are not affected from $PSBoundParameters.Remove() operation at all. All we are doing is removing keys from a dictionary.

We can also add key/value pairs to it using this:
$PSBoundParameters.Add('Key','Value')

E.g. 
$LastName='Doe'
$PSBoundParameters.Add('NewLastName',$LastName)
$PSBoundParameters are quite useful to determine whether a parameter is using its default value or was it passed a value. It is also useful for splatting:
Some-CommandLet @PSBoundParameters
This supplies the key value pairs a CMDLET or function would require

2014-04-19

PowerShell Gotchas: Enabling Safemode for Windows 8.1 with Bcdedit

In the last couple of days, I started to see a couple of weird issues on my my fully patched Windows 8.1 Update 1 PC. I may blog more about that later but for now, I just want to blog about a PowerShell gotcha, that might frustrate those who are switching from good ol' cmd shell to PowerShell console.

To troubleshoot the recent BSODs, I wanted to enable Safe Mode in Windows 8. Steps are well documented here: http://support.microsoft.com/kb/2809468/en-us

To do this, we need to use BCDEdit utility, which is used to manage Windows boot settings in the newer Windows OSes.

Here I am in PowerShell Admin console...

PS C:\WINDOWS\system32> bcdedit

Windows Boot Manager
--------------------
identifier              {bootmgr}
device                  partition=\Device\HarddiskVolume1
description             Windows Boot Manager
locale                  en-US
inherit                 {globalsettings}
integrityservices       Enable
default                 {current}
resumeobject            {4efceffa-37b1-11e3-9418-e54cd3928210}
displayorder            {current}
toolsdisplayorder       {memdiag}
timeout                 30


Windows Boot Loader
-------------------
identifier              {current}
device                  partition=C:
path                    \WINDOWS\system32\winload.exe
description             Windows 8.1
locale                  en-US
inherit                 {bootloadersettings}
recoverysequence        {8bcbbebd-37b2-11e3-9418-e54cd3928210}
integrityservices       Enable
recoveryenabled         Yes
allowedinmemorysettings 0x15000075
osdevice                partition=C:
systemroot              \WINDOWS
resumeobject            {4efceffa-37b1-11e3-9418-e54cd3928210}
nx                      OptIn
bootmenupolicy          Standard

Let's tell Windows 8.1 to display safe mode options
PS C:\WINDOWS\system32> bcdedit /set {bootmgr} displaybootmenu yes

The set command specified is not valid.
Run "bcdedit /?" for command line assistance.
The parameter is incorrect.


Ugh, ok. I know the command is correct but PowerShell tells us otherwise. Most of the time, dos (I really mean cmd shell) utilities work just fine in PowerShell but when they do not we can tell PowerShell to hand it over to cmd shell
PS C:\WINDOWS\system32> cmd /c bcdedit /set {bootmgr} displaybootmenu yes
The set command specified is not valid.
Run "bcdedit /?" for command line assistance.
The parameter is incorrect.

Same error. OK, I won't keep doing this. You see the curly brackets over there in the command line surrounding bootmgr, that's our problem. PowerShell is trying to parse the arguments and {} is a scriptblock in PowerShell, so we should tell it not to interpret arguments
PS C:\WINDOWS\system32> bcdedit --% /set {bootmgr} displaybootmenu yes

The operation completed successfully.
There! --% was introduced in PowerShell v3 (I believe) and is very useful when handling quotes and such that have a special meaning in PowerShell, and hence parsing them gets hairy.

Note: There are other cases where you need to tell PowerShell to use cmd shell. For example:
d:\>where ssh
## did not return anything
d:\>cmd /c where ssh
D:\Program Files (x86)\Git\bin\ssh.exe

This works because 'where' is an alias for where-object cmdlet in PowerShell. In cmd shell, it tells you where an executable is located (as would `which` in linux/mac)

2014-04-17

Adding reference to PowerShell dll in Visual Studio

Just another quick tip today. I wanted to create a Runspace to run some PowerShell code from C#. When I launched Visual Studio, and tried referencing PowerShell, which lives in System.Management.Automation, all I got was red wiggly underline. Similarly, the PowerShell object was not recognized.



To teach Visual Studio about the System.Management.Automation namespace, I needed to add the relevant DLL, which in my case (as I have x64 Windows 8.1) was found here: 

"%ProgramFiles(x86)%\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll"




Once I add it to the References, wigglies go away and I can now use all the namespace has to offer:



Oh, if you are already in PowerShell console, you can see the current assembly location using this:

D:\> [psobject].assembly |fl
CodeBase               : file:///D:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Management.Automation/v4.0_3.0.0.0__31bf3856ad364e35/System.Management.Automation.dll
EntryPoint             :
EscapedCodeBase        : file:///D:/WINDOWS/Microsoft.Net/assembly/GAC_MSIL/System.Management.Automation/v4.0_3.0.0.0__31bf3856ad364e35/System.Management.Automation.dll
FullName               : System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
GlobalAssemblyCache    : True
HostContext            : 0
ImageFileMachine       :
ImageRuntimeVersion    : v4.0.30319
Location               : D:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
ManifestModule         : System.Management.Automation.dll
MetadataToken          :
PortableExecutableKind :
ReflectionOnly         : False

Fixing Visual Studio Login Issue


If you are seeing messages from your Visual Studio (Express) complaining that your trial expired after 30 days, you need to sign in to your Visual Studio (and/or Microsoft Account).

OK. that was stating the obvious. Here is the frustrating part. You are trying to login but you cannot and you get the following error:

Browser is security restricted or JavaScript is disabled

I was surprised with this message because I do not recall disabling Javascript  on my Internet Explorer. Anyway, to enable Javascript may not be obvious:

  • Launch Inernet Explorer (v11 in my case)
  • Click Tools > Internet Options > Security
  • Click Internet
  • Click Custom level
  • Scroll all the way near the bottom
  • Under Scripting > Active Scripting, click "Enable"
  • click OK and close.




That should do it. Now you can sign in to your account, and activate your copy of VS Express. 



2014-02-10

PowerShell WhatIf

There are tons of resources on the web on how to use -whatif parameter in Powershell functions. I won't go over them but here is a mistake I noticed people sometimes do: They forget to add SupportsShouldProcess to the main script.




Couple of things to note here:

* When you pass -whatif parameter to the main script, it gets passed down to any 'advanced' function that is also supporting whatif.

In out example, Test-WhatifTraversing function support -whatif, because it too has the following line

[CMDLETBINDING(SupportsShouldProcess)]

* Once you use -whatif to call your script, it will not only pass it to Advanced Functions, but it will automatically execute on any other native PowerShell cmdlets that supports -whatif

* You want to wrap the 'dangerous code' you have in that 'If' block so that when you pass -whatif it does NOT execute, but even if you don't have any such code, it is still beneficial to add 'SupportsShouldProcess' to all your functions and scripts because of what I mentioned above.

2014-02-02

iOS7 Google Contacts Sync Issue

I noticed today that my iPhone 5S had some missing contact information. I checked and double checked, and sure enough some contacts I had on iPhone were not sync'ing back with my Google contacts.

I looked at settings and made sure app-password (two factor auth means you cannot use your Google password but the password generated for the app) was correct. There was no sign of any trouble anywhere but clearly it was not sync'ing.

To keep this short; I found out the issue was that I had my Google contacts sync to use 'Exchange' (aka Active Sync). I vaguely remembered that there was some ActiveSync licensing news a while ago and after some digging found two pages of instructions on setting up iOS devices with Google contacts Sync Option.

One from Apple: http://support.apple.com/kb/HT4872 , very short, Apple style, with just the minimum information they could give... (noticed that they are still not using https?)
Follow these steps if you're using iOS 7 to sync your contacts with Google Contacts:
  1. Tap Settings > Mail, Contacts, Calendars > Add Account > Google.
  2. Enter the required information in the fields.
  3. Make sure Contacts is on.

And the other from Google: https://support.google.com/mail/answer/2753077?hl=en
Much more detailed. (E.g. Look at #7 and compare that to #3 above)

They started with this:

Sync contacts with your Apple device

To sync your Google contacts with your Apple device (iPhone, iPad, iPod touch, Mac), we recommend using CardDAV, which is an Internet open standard. When you sync your Google contacts using CardDAV, you’ll be able to edit, add, and remove contacts from your device and keep them in sync everywhere you use them.
If you use Google Apps for Business, you can use Google Sync to sync your contacts to your iOS device.

Sync your contacts

  1. Open the Settings app on your device.
  2. Select Mail, Contacts, Calendars.
  3. Select Add Account.
  4. Select Google.
  5. Fill out your account information in the following fields:
    • Name: Enter your name
    • User Name: Enter your full Google Account or Google Apps email address.
    • Password: Your Google Account or Google Apps password. (If you’ve enabled 2 Step verification, you’ll need to generate and enter an application specific password.)
    • Description: Enter a description of the account (e.g. Personal Contacts).
  6. Select Next at the top of your screen.
  7. Make sure that the "Contacts" option is turned ON. The switch should be green.
  8. Select Save at the top of your screen.
After you've completed setup, open the Contacts app on your device, and syncing will automatically begin.

Apple's article did not give me a clue actually but specific 'CardDAV' emphasis of Google kinda told me that my 'Exchange' set up was the culprit. So, I deleted the existing account:

Settings > Mail, Contacts, Calendars > Gmail > Delete Account

The catch is that it forces you to delete all the existing (google) contacts from the phone :(

Anyway, I went ahead and did it. Then, added Google account back and once I launched the 'Contacts' app, sync started and is working fine now.

2014-01-21

dsget parsing in PowerShell vs cmd

Recently, we had a discussion in a Microsoft Certified Professionals group about how to detect who joined a computer to domain. One of the folks suggested using 'qused' option of 'dsget'. It justs returns a number, so you really need to get the count for all users and then see whose number got higher etc., but that's not the reason I am jotting this down.

It's about an unhelpful message from dsquery and how PowerShell parsing of commands from cmd.exe could be problematic.

So the command we are interested in is:
dsget user <DN> -part <AD Partition> -qused -display

E.g.
dsget user "CN=adil,OU=xyz Users,DC=xyz,DC=org" -part dc=xyz,dc=org -qused -display


Instead of typing  DistinguishedName of the user, it is also possible to get that from dsquery and then pipe it to dsget:

dsquery user -samid <KerberosID> | dsget user -part <AD Partition> -qused -display


E.g.:
dsquery user -samid adil | dsget user -part dc=xyz,dc=org -qused -display
 display         qused
 Hindistan, Adil 700

dsget succeeded


Note that there is a space between 'xyz users', so we enclosed that part in quotes for cmd to parse it correctly. Otherwise you would get an error:

dsget failed:Value for 'Target object for this command' has incorrect format.


If you however run the last command in PowerShell, it will not work:

PS H:\> dsquery user -samid adil | dsget user -part dc=xyz,dc=org -qused -display
  dsget failed:A referral was returned from the server.
  type dsget /? for help.


To error becomes more meaningful if you omit the dsquery part and just run dsget:

PS H:\> dsget user "CN=adil,OU=xyz Users,DC=xyz,DC=org" -part dc=nyumc,dc=org -qused -display

 dsget failed:'dc=org' is an unknown parameter.

 type dsget /? for help.


So, PowerShell is looking at the part after -part and does not interpret it as cmd.exe would. There are several ways to fix this:

1) Simply use quotes or single quotes around the part after -part
PS H:\> dsget user "CN=adil,OU=xyz Users,DC=xyz,DC=org" -part "dc=nyumc,dc=org" -qused -display


The other option is to tell PowerShell to run cmd.exe and let it parse the rest using call operator:

PS H:\> &cmd.exe /c 'dsget user "CN=adil,OU=xyz Users,DC=xyz,DC=org" -part dc=nyumc,dc=org -qused -display'
Display         qused
Hindistan, Adil 700
dsget succeeded

And here is a little function I wrote to beautify it (yep, I know, beauty is in the eye of the beholder)

function Get-DomainJoinCount {
 param (
          [Parameter(ValueFromPipeline)]
          [Alias('ID','SamID')][string]$KerberosId='adil'
       )
 dsquery user -samid $KerberosID |
 dsget user -part "dc=xyz,dc=org" -qused -display).trim() -replace '\s{2,}',';' | select -skip 1 -First 1 |
 ConvertFrom-Csv -Delimiter ';' -Header "User Name","Count"
}

PS H:\> Get-DomainJoinCount adil |ft -AutoSize

User Name        Count
---------        -----
Hindistan, Adil 700 

2014-01-19

Updating Environment Variables

I installed Python 2.7 on my computer, but install path was not added to environment variables. A couple of quick notes on doing this with PowerShell:

  • PowerShell does not offer a direct cmdlet to manipulate $env:path that would stick after the shell is closed.
  • [System.Environment]::GetEnvironmentVariable() and [System.Environment]::SetEnvironmentVariable() .NET methods are the way to go about this. 
  • Standard PowerShell window would not work in this case, and you need to be on an elevated PowerShell window.
  • You can also directly manipulate the registry key which holds system-wide environments variables [HKLM\System\CurrentControlSet\Control\Session Manager\Environment\Path]

To get the current path, you can either use .NET method or built in $ENV variable:

PS C:\> [System.Environment]::GetEnvironmentVariable('PATH')
C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMD APP\bin\x86;...

PS C:\> $env:Path
C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMD APP\bin\x86;...

Note that, with the .NET [System.Environment], you have also the option of getting USER variable only:

PS C:\[System.Environment]::GetEnvironmentVariable('PATH','USER')
C:\Program Files (x86)\Google\google_appengine\

To add my Python Path (c:\Python\27):

PS C:\> [System.Environment]::SetEnvironmentVariable('PATH',$env:Path + ';C:\Python\27', 'MACHINE')


It's important to remember that when you are trying to make system-wide changes, or sometimes even to access system data, you need to be on an elevated shell, which you can get by right clicking PowerShell launcher and choosing 'Run As Administrator'

Most of the time, PowerShell will give you hints that you need to be on an elevated shell but not always. For example, if you run the command above in a regular shell, you do not get an error. In fact, the change will seem to go through, except it won't and when you launch a new shell, you will notice that path you added is not there.

Below is an example where you do get an error if you attempt to run it in a non-elevated shell:

function get-smart {
gwmi -namespace root\wmi -class MSStorageDriver_FailurePredictStatus |
        select InstanceName,PredictFailure,Reason |ft -a
}

Regular shell will 'yell' at you in red:

PS C:> get-smart
gwmi : Access denied 
At line:2 char:2
+     gwmi -namespace root\wmi -class MSStorageDriver_FailurePredictStatus |select In ...
Run the same function in an elevated window:

PS C:\> get-smart

InstanceName                                                              PredictFailure Reason
------------                                                              -------------- ------
SCSI\Disk&Ven_Hitachi&Prod_HDT721010SLA360&Rev_ST6O\5&24ba3dff&0&000000_0          False      0
SCSI\Disk&Ven_&Prod_ST31500341AS\4&5ecf4f&0&020000_0                               False      0

By the way, that function will check the SMART status of your hard drives and tell you whether SMART expects any failure

2014-01-15

Fixing quser access is denied error

Daily PowerShell tips from PowerShell.com are a great way to learn PowerShell tips and tricks. They sent a tip yesterday about 'quser'. If you have been around for a while, you may remember that command. A long long time ago, Citrix helped Microsoft to create 'Terminal Server'. They also created some 'q****' commands.

If you go into your system32 directory and list executables that start with q, you will some others, like qwinsta.exe and qprocess.exe, as well as quser.exe Microsoft kept these executables ever since.

I am familiar with them, because many years ago I had written a perl application to monitor Citrix Servers and these commands came handy at the time.

Anyway, PowerShell tip yesterday was about finding out logged on user:

PS H:\> quser  
USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
  >adil              console             1  Active      none   1/15/2014 11:35 AM PS 

H:\> (quser) -replace '\s{2,}',','|ConvertFrom-Csv
 SERNAME    : >adil
SESSIONNAME : console
ID          : 1
STATE       : Active
IDLE TIME   : none
LOGON TIME  : 1/15/2014 11:35 AM


The first command shows what you would have seen by running the command on the current machine, and Tobias Weltner's tip shows us how to first replace 2 or more spaces with ',' and then, use ConvertFrom-CSV cmdlet to convert string into a reusable PowerShell Object (PSObject).

Today, there was a follow up on the tip to find out who logged on on a remote computer, using /server parameter. One of my colleagues tried it and and reported that it was not working for her.





Error 0x000006BA enumerating sessionnames
Error [1722]: The RPC server is unavailable.


The first line of error suggested that it was a permissions issue, but she was an admin on the remoe windows 7 box.
The second line was telling us that the remote machine was not responding to Remote Procedure Call (RPC).

So, to fix this, we needed to enable RemoteRPC calls in the registry of the remote machine.
PS H:\> invoke-command -computername adil-w7x32vm2 -Command { set-itemproperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name AllowRemoteRPC  -Value 0x1 -Force }

 And once the RemoteRPC is allowed, query goes through without any errors.

PS H:\> (quser /server:adil-w7x32vm2) -replace '\s{2,}',','|ConvertFrom-Csv

  USERNAME    : testuser
  SESSIONNAME : console
  ID          : 3
  STATE       : Active
  IDLE TIME   : none
  LOGON TIME  : 1/15/2014 10:29 AM
 Note that, you migh also use the following command to connect to remote registry and change the setting, in case you cannot use invoke-command:

reg add "\\ComputerName\HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v AllowRemoteRPC /t Reg_Dword /d 0x1 /f

And query to make sure the change took place:

PS H:\> reg query "\\adil-w7x32vm2\HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v AllowRemoteRPC

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server

    AllowRemoteRPC    REG_DWORD    0x1

2014-01-09

Finding and dealing with a special char in PowerShell

Recently I ran into an issue while doing regex on Bios Serial Numbers. A couple of machines returned a special character as their serial number when I ran the following command

(Get-WmiObject Win32_Bios) .SerialNumber

�����

Not sure if it will show up fine on your computer but it looks like a black diamond shape with question mark in it.

For my purpose, I needed to eliminate such computers from my results, so naturally I wanted to be able to match them in my regular expression.

The problem was that I had no idea how to produce it in my regex.
I knew I could have produced any character using this

[char]'CharCodeGoesHere'

But  how do I find the char code? Here is how:

I copied the first '?' character and then pasted it between the single quotes (could have been double quotes but in general if you do not want something to be evaluated, avoid double quotes) below in PowerShell

PS D:\> [int][char]'�'
65533

So I could reproduce the weird character by dropping [int]

PS D:\> [char]65533


This meant that I could now use it in regular expression like this:

PS D:\>(Get-WmiObject Win32_Bios) .SerialNumber -match "$([char]65533)"
True

While we are at it, here are a couple of valid and invalid ways to do this comparison

PS D:\> '�' -match "$([char]65533)"
True

PS D:\> "�" -match [char]65533
True

PS D:\> '�' -match [char]65533
True

PS D:\> '�' -match '[char]65533'  (Included this because single quote some times trip people. PowerShell does not evaluate the stuff inside single quotes.

PS D:\> � -match "$([char]65533)"
� : The term '�' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ � -match "$([char]65533)"
+ ~
    + CategoryInfo          : ObjectNotFound: (�:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

In the last example above, Left side of -match operator is not enclosed within any quotes and therefore an error is generated. Error is quite self explanatory.

2013-10-16

Permissions to access WMI remotely

Accessing WMI on a remote machine is pretty mundane for System Admins. They do not even think about it, they have Administrator rights, and it works (for the most part).

In my case, I needed to run a script with a service account and connect from one Windows 2008 R2 Server to another. I needed to figure out what permissions that account should have on the server, short of making the service account an admin. How difficult this can be? Well, it proved to be more difficult to find information on this than I thought it would be.

Here is what happens if the service (domain) account I use tries to access remote server, where it does not have any permissions:

PS> gwmi win32_ComputerSystem -ComputerName test.adilhindistan.com
gwmi : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
At line:1 char:1
+ gwmi win32_ComputerSystem -ComputerName test.adilhindistan.com
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException
    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand
I looked at several resources including venerable StackOverflow, where I have found several responses to this type of questions but none worked for my case.

There are many KB articles on the subject. This dated MSDN article claimed that you need to be admin on the target box. WMI Troubleshooting, Connecting to WMI on a Remote Computer by Using Windows PowerShell,

I asked in PowerShell Community Group chat, where Joel "Jaykul" Bennett pointed me to this article.

All of them helped, but in the end, I had to find my own solution to it:

1) Add the user to 'Distributed DCOM Users' group on the target server
2) Grant WMI permissions to user by following the steps below:

  • Launch compmgmt.msc and connect to target server
  • Right click on Services and Applications > WMI Control and select "Properties"
  • Click Root (CIMV2 did not seem to work but see update below) and then "Security"
  • Add the domain user and click on "Advanced"
  • Double click on user name
  • Change Applies to to "This namespace and subnamespaces"
  • Click on "Remote Enable" checkbox and hit "OK"
See screenshots below.



Note that I only needed read access. If you need to write or execute WMI methods, then additional checkboxes (Execute Methods, * write) will need to be added.

Update 2013-10-17:
I got an e-mail from Colyn, who had helped answering the question on StackOverflow thread asking me if  adding user to "WinRMRemoteWMIUsers__" group would work for me.

I did not have that option because that group did not exist on the server, which had PowerShell version 2. CIM cmdlets became available with PowerShell v3, so I guess that explained why.

However, I had access to another Windows 2008 R2 server with PowerShell3. Sure enough, that server had the group. the group definition says:
"Members of this group can access WMI resources over management protocols (such as WS-Management via the Windows Remote Management service). This applies only to WMI namespaces that grant access to the user."

On target server:
1) I added domain test user to  "WinRMRemoteWMIUsers__".
2) I granted both "Enable Account" & "Remote Enable" access to  Root/CIMV2 namespace

FAILURE:
Get-WMIObject call failed with the same access denied error:

PS D:\> get-wmiobject win32_ComputerSystem -Computer TEST01
gwmi : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
At line:1 char:1
+ gwmi win32_ComputerSystem -Computer TEST01
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException
    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand

Similarly, Get-CimInstance with DCOM protocol fails as well

PS D:\> $cimoption = New-CimSessionOption -Protocol Dcom
PS D:\> new-cimsession -computername wstwsdcpvm001.nyumc.org -SessionOption $cimoption
new-cimsession : Access is denied.
   + FullyQualifiedErrorId : HRESULT 0x80070005,Microsoft.Management.Infrastructure .CimCmdlets.NewCimSessionCommand


SUCCESS:
But Get-CimInstance cmdlet which uses WS-MAN protocol succeeded:

PS D:\> get-ciminstance -classname win32_ComputerSystem -ComputerName TEST01

Name             PrimaryOwn Domain     TotalPhysi Model     Manufactu PSCompute
                 erName                calMemory            rer       rName
----             ---------- ------     ---------- -----     --------- ---------
TEST01    Windows...   4026064896 VMware... VMware... TEST01...

NO ALIAS PLEASE!
Here is another twist to this story. If I use the dns alias instead of actual hostname, Get-CimInstance too returned an error, but return code is different (0x80070035).

WMI SERVICE GOTCHA
Another note: if you will be playing with the WMI permissions, you may sometimes see unexpected results if you do not restart WMI service (get-service winmgmt | restart-service -force)  after making a change. For example: I wanted to test removing "Remote Enable" permission and see if I would still be able to use Get-CimInstance. To my surprise it worked for a while...until I restarted the service. Then I started to get errors (HRESULT 0x80041004). I had to add back the "Remote Enable" and restart WMI service to get it back up.

Finally, here are a couple more useful links: