Daniel Robertson Someday I'm gonna start posting…

15Nov/110

Hosting with VPS.NET

I wanted to write a little update on my current hosting status. I have abandoned my previous host due to a serious failure that brought down my server for several days and lost my data. While I was working with Turnkey Linux, I had heard about VPS.NET. They are a cloud hosting provider that has the Turnkey images available. The major benefit of their cloud vps service is the ability to scale up or down or laterally very simply through their control panel. Even though I am not working with TKL for hosting, I decided to give them a shot and bought one node.

The initial setup was very simple. I took my time setting up my server, including rebuilding it a couple times trying out the different available images. The control panel is very easy to use and the server builds only took a few minutes. I have tested upgrading and downgrading the number of nodes applied to my server, each went smoothly. Once I was satisfied with my build, I migrated the DNS. The DNS management tool has a very clean UI, working with it was a breeze.

Apart from the solid infrastructure, I mainly want to talk about some of the software advantages that a VPS.NET account provides. Some hosts will offer you a management software such as cPanel and a billing software such as HostBill. VPS.NET has built several great partnerships that go over and beyond what other hosts offer. I decided against using a management software, however I tried both HostBill and Blesta billing applications. I like Blesta and see the developers are actively working on it. I also use git for version control of my websites. One very nice website offering git hosting and project management features is Codebase, which offers a free account for VPS.NET customers. You can link it to your Github account so code pushes are kept in synch.

Managing a VPS requires much more than a shared hosting account. In order to make sure the server stays up and healthy, you must constantly monitor it. Server Density is a nice monitoring application that will run on your server allowing you to monitor cpu, memory, database and webserver statistics, etc. By being a VPS.NET customer, you can get a discount on the subscription fee. If you aren't monitoring for performance, do it! After I had added some more sites and applications to my server, Server Density monitoring was very helpful in showing me how my applications were performing and let me know when I needed to add another node to my server (which was extremely simple and only required a reboot). Updating your server is also critical. One great software that has been around for a while is Ksplice Uptrack. This will allow you to patch your system without having to reboot it. Ksplice software licensing is free on VPS.NET nodes! This is something that I haven't seen anyone else offer.

I have now been hosting with VPS.NET for a year and have been very happy. So if you are looking for a new host that is fast, flexible, with great support and is always looking for ways to go beyond your expectations, I highly recommend you give VPS.NET a try!

Filed under: Computers No Comments
16Apr/110

Find logged in users remotely with PowerShell

I finally took the plunge to learn PowerShell.  For my first Script, I decided to find out who is logged into a remote computer.  It grew from there as I wanted to add additional features.  I had a very good learning experience which I would like to share.

First the script:

<#
.SYNOPSIS
    This script allows you to find out who is logged into remote computers.
.DESCRIPTION
    You can call this script to run against a specific computer, a space
    separated list of computers, a file containing a list of computers on
    individual lines.  You can also run it against every computer in your
    domain or an individual OU.
.NOTES
    File Name : loggedin.ps1
    Author    : Daniel Robertson &lt;dan@540tech.com&gt;
.EXAMPLE
    ./loggedin.ps1 computer1 computer2 computer3
.EXAMPLE
    ./loggedin.ps1 "C:\list of computers.txt"
.EXAMPLE
    ./loggedin.ps1 domain -byUser
.EXAMPLE
    ./loggedin.ps1 ou Accounting
.PARAMETER target
    The target may be any number of computers separated by a space.
    The target may be a path to a file.
    The target may be 'domain'.
    The target may be 'ou' followed by the name of an OU.
    If a target is not specified, the script will ask you to enter one.
.PARAMETER byUser
    By default the list will be sorted by computer name.  You can use
    the -byUser switch to instead sort the list by username.
#>
 
param([switch]$byUsers)
$list = $args
 
# Prompt for computers if none were given
while (-not $list[0])
{
    $list = Read-Host "Computers to check"
    $list = $list.split(" ")
}
 
# Allow searching through Active Directory
if (($list[0] -eq "domain") -or ($list[0] -eq "ou"))
{
    if ($list[0] -eq "domain")
    {
        $SearchRoot = New-Object System.DirectoryServices.DirectoryEntry
    }
    elseif ($list[0] -eq "ou")
    {
        # Prompt for OU if one was not given
        $ou = $list[1]
        while (-not $ou)
        {
            $ou = Read-Host "Enter OU to check"
        }
 
        # Can't handle subdomains yet
        $dc1,$dc2 = $env:userdnsdomain.split(".")
        $SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://ou=$ou,dc=$dc1,dc=$dc2")
    }
 
    # Build the search
    $searcher = New-Object System.DirectoryServices.DirectorySearcher
    $searcher.SearchRoot = $SearchRoot
    $searcher.SearchScope = "Subtree"
    $searcher.PageSize = 1000
    $searcher.Filter = "(objectCategory=Computer)"
 
    # Only return the name property
    $searcher.PropertiesToLoad.Add("name") | Out-Null # Suppresses return value of Add()
 
    # Perform the search
    try
    {
        $list = @()
        foreach ($objResult in $searcher.FindAll())
        {
            $list = $list + $objResult.Properties.name
        }
    }
    catch [system.exception]
    {
        "ERROR: " + $_.exception.message
        exit
    }
}
# Allow a file with list of computers
elseif (Test-Path $list[0])
{
    $list = Get-Content $list[0]
}
 
$result = @{}
foreach ($ComputerName in $list)
{
    try
    {
        $result.$ComputerName = (Get-WMIObject win32_computersystem -computername $ComputerName -errorAction stop).username
 
        if ($result.$ComputerName -eq $null)
        {
            $result.$ComputerName = "(Not logged in)"
        }
    }
    catch [system.exception]
    {
        $result.$ComputerName = "(ERROR: Unable to connect to remote computer)"
    }
}
 
if ($byUsers)
{
    # Sort by username
    $result.GetEnumerator() | Sort-Object value
}
else
{
    # Sort by computer name
    [collections.sortedlist]$result
}

Let's look at the beginning.  The first line allows the script to be run with an optional -byUsers switch that I will talk about later.  I pass all arguments into the $list variable that I can then manipulate.  The next part will check to see if any arguments were supplied.

param([switch]$byUsers)
$list = $args

This while loop checks to see if there is at least one argument.  If there isn't one, the script will prompt you for one.  The split(" ") method will allow you to enter a string of computers each separated by a space and convert that to an array.  The while loop also means that if you just hit <enter> at the prompt, it will continue to prompt you until you enter in some text.

# Prompt for computers if none were given
while (-not $list[0])
{
    $list = Read-Host "Computers to check"
    $list = $list.split(" ")
}

The next thing I do is check to see if domain or ou is given as the first parameter.  If it is, we will query Active Directory for the list of computers to check against.  We build the search with a different base depending on if you want to search the entire domain or an individual OU.

if (($list[0] -eq "domain") -or ($list[0] -eq "ou"))
{
    if ($list[0] -eq "domain")
    {
        $SearchRoot = New-Object System.DirectoryServices.DirectoryEntry
    }

I use another while loop to check to see if an OU was given as a parameter.  If it wasn't, you are prompted to enter one.  Searching an OU requires you to enter an LDAP query.  I did not want to hard code the domain information into the script, so I needed to find a way to grab your current domain.  The easiest way I found is to just use the USERDNSDOMAIN environmental variable of the computer you are on.  I then split the variable at the "." between your domain name and suffix.  If you are on a subdomain, this would need to be modified.  One thing I found while writing this is that I couldn't use a single array variable in the method like "LDAP://ou=$ou,dc=$domain[0],dc=$domain[1]".  This is something that is done all the time in PHP.  Instead of concatenating, it was simple to just use two variables similar to the way list() works in PHP.

elseif ($list[0] -eq "ou")
    {
        # Prompt for OU if one was not given
        $ou = $list[1]
        while (-not $ou)
        {
            $ou = Read-Host "Enter OU to check"
        }
 
        # Can't handle subdomains yet
        $dc1,$dc2 = $env:userdnsdomain.split(".")
        $SearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://ou=$ou,dc=$dc1,dc=$dc2")
    }

The next part builds the query.  I found most of this information on Microsoft's Hey, Scripting Guy! blog.  I did notice that as I was running the script, it would print a "0" before the output.  I tracked it down to the Add() method being called which prevents too much information from being returned from the query.  I searched for the method information on MSDN and found that it returns an integer which is the index in the array where the property was added.  Piping it to Out-Null prevents the value from being printed on screen.

    # Build the search
    $searcher = New-Object System.DirectoryServices.DirectorySearcher
    $searcher.SearchRoot = $SearchRoot
    $searcher.SearchScope = "Subtree"
    $searcher.PageSize = 1000
    $searcher.Filter = "(objectCategory=Computer)"
 
    # Only return the name property
    $searcher.PropertiesToLoad.Add("name") | Out-Null # Suppresses return value of Add()

Next it performs the query and builds an array with the names of computers found.  I reset the $list variable to an empty array before adding the entries.  I found that an exception gets thrown if you mistype the name of an OU.  The try/catch block will handle any exceptions thrown while querying AD and display the error message to you.

    try
    {
        $list = @()
        foreach ($objResult in $searcher.FindAll())
        {
            $list = $list + $objResult.Properties.name
        }
    }
    catch [system.exception]
    {
        "ERROR: " + $_.exception.message
        exit
    }

This last test will check to see if what you entered is an actual path and get the contents of the file to use.

elseif (Test-Path $list[0])
{
    $list = Get-Content $list[0]
}

The next part builds the hashtable of computer names and logged in users using the Get-WMIObject class.  If the username is empty, no one is logged in.  The -errorAction stop is essential for you to be able to catch the exception in case you can't connect to the remote computer.

$result = @{}
foreach ($ComputerName in $list)
{
    try
    {
        $result.$ComputerName = (Get-WMIObject win32_computersystem -computername $ComputerName -errorAction stop).username
 
        if ($result.$ComputerName -eq $null)
        {
            $result.$ComputerName = "(Not logged in)"
        }
    }
    catch [system.exception]
    {
        $result.$ComputerName = "(ERROR: Unable to connect to remote computer)"
    }
}

And the very last part will sort the list.  If you used the -byUsers switch, the $byUsers variable will be true.  In order to sort a hashtable with the Sort-Object, you must use the GetEnumerator() method.  In order to sort by key, it is much easier to cast the result to a SortedList object.

if ($byUsers)
{
    # Sort by username
    $result.GetEnumerator() | Sort-Object value
}
else
{
    # Sort by computer name
    [collections.sortedlist]$result
}

That is the end of the script.  I had a good time learning how to use the various PowerShell and .NET features.  While I was able to find good information from Microsoft TechNet, Hey, Scripting Guy!, and StackOverflow as well as other blogs, it was a challenge to find everything  I needed.  Let me know if there is anything that you think I could do better.  I will be putting this script along with any other PowerShell scripts I make on Github.

20May/102

Bitnami Redmine Appliance

I have used several of the Turnkey Linux appliances and have had good experiences with them. With the new 10.4 LTS version of Ubuntu out, I have eagerly anticipated grabbing the updated ones. So eager in fact, that I decided to try out a Bitnami stack of the Redmine application.

In case you don't want to read more, or I fall asleep before I finish typing this, here's the deal: Bitnami pales in comparison to TKL's offerings. I actually think it would have been easier for me to just install everything by hand.

Immediately after installing I noticed that there was no simple config page. Ok, no big deal. So I have to use ifconfig and manually set my static IP address. That worked fine... for a few minutes. While trying to use aptitude I couldn't find the host. Checked the resolve.conf, yep my DNS servers were still in there. Restart the interface and I'm back up... until it happens again. Restart interface again and this time it sticks.

Next to install VMware Tools (this is on ESX 3.5 btw). I installed the dependencies needed to build. VMware Tools installed ok, then tried to run the vmware-config-tools.pl. It failed to build vmxnet file. Ok, whatever, I'll deal with that later.

Rather than doing everything via the console screen, my next objective is to get SSH working. The instructions on Bitnami's site work for getting it going. I have Putty and used it to export my .ppk into RSA format. Updated the sshd_config file to only allow RSA authentication. Took me a while to realize that all the extra text that Putty likes to put in there is throwing the server off. So reminder: If you ever use Putty to convert a key to RSA format, go back and edit all the extra garbage out!

One thing that I love about TKL appliances is the pre-configured Webmin and PHPMyAdmin. With Bitnami, no Webmin, and PHPMA is disabled by default. So I find the deb from Webmin's site. I can't seem to install libmd5-perl so a manual download from mirrors.kernel.org gets it there. Webmin of course throws a fit but apt-get -f install calms it down. Enable port 10000 for it in the firewall and check it out. Uggh, the default theme looks horrible! I honestly have never seen it without the nice theme pre-installed. Looking around the web I see the Tiger theme, which honestly is the only good looking Webmin theme available.

Ok, that installed, let's take a look at Apache. Oh wait... Webmin doesn't see Apache installed. Of course Bitnami puts it in their own folder. So I check out the directories and update the Apache Webmin configuration. That works (for the most part..).

After playing around with Webmin for a bit, I decide to actually get started with Redmine. I browse to it and log in with the default username/password. Looks good. I edit the config to use my smtp server. That works. Now for the important part: Git. I test the git command in my terminal. No good. Ok so I install git-core. Now I can create repo's. I create one in my home directory and link it to the Redmine project. That's good. I set up a local repository and access the one on the server via ssh. Everything good. Until I try to push that is. I kept getting a weird error: "git /home/bitnami/website.git is not a git command". Grrr. Search and search and I can't figure out what is going on. Then I find a post that looks interesting. Yes I find out that Bitnami does install git, but it is in their own folder. And of course they forget to put it in the PATH environment variable. So here's what you do. Go ahead and make a copy of your ~/.bashrc file. Then make a new one with this in it:


#!/bin/sh
if [ -r "/opt/bitnami/scripts/setenv.sh" ]; then
. /opt/bitnami/scripts/setenv.sh
fi
PATH="/opt/bitnami/git/libexec/git-core:$PATH"
export PATH
if [ -x "/home/bitnami/change-password.sh" ]; then
/home/bitnami/change-password.sh
fi

Finally!! I can now push to my repository and have Redmine see it. Remember earlier I said Bitnami doesn't enable PHPMyAdmin by default? Well after looking around, I see that they actually don't have PHP at all! So much for actually seeing what those files I pushed will actually look like on a webserver. (Yeah yeah, I know I could view locally, but I don't want that on my work laptop). Of course now, it's been about 8 hours since I started and I have no motivation whatsoever to install and configure PHP on this piece of shit appliance. At least the TKL appliance is about 170MB smaller (almost half the size) so it won't take as long to download.

Now I am back to waiting for Turnkey Linux to put out their betas of the 10.4 servers. But after this experience, I can definitely say it will be worth the wait!

Filed under: Computers 2 Comments
29Apr/100

Site moved to new vps

Well after a few hours of work I finally have this site moved over to my new vps.  Here's a short rundown of what I did today to get this thing transferred:

  • Backed up and uploaded the database via phpMyAdmin
  • Backed up and uploaded the WordPress files (modifed the wp-config with new db info)
  • Realized the auto-upgrade wasn't working...
  • Rebuilt apache with php 5.3.2 along with additional modules (thank you easyapache!)
  • Changed the php 5 handler to suphp from dso
  • Reset all my home directory owners (/scripts/chownpublichtmls)
  • Noticed I couldn't log into the admin area any more...
  • Reset the file permissions in wordpress directory

And here I am!  After all was said and done, I have an upgraded installation without the GoDaddy hacks and plugins are able to auto-install again.  I think that is enough for one day.  Good night!

Filed under: Computers No Comments
31Jan/100

ls command for Windows

Since switching to Windows 7, I found myself typing the ls command and then having to retype the dir command.  I decided to find a way to port the ls command over.  Luckily, I found a great version online here: http://utools.com/msls.asp.  It also has a version of grep!

First download the self extracting exe file from the website and extract it to a temporary directory.

Next copy the ls and grep files over to C:\Windows\system32\ and also to C:\Windows\SysWOW64\ on 64 bit versions.

Set your default options in an Environment Variable (In the Control Panel -> System -> Advanced System Settings).  I set my GREP_OPTIONS to "--binary-files=text -d skip --color=auto" and I set my LS_OPTIONS to "-bhAC --more --color=auto --recent --streams -l".  (You might not want the -l switch as a default, but I do.)

Now you can use the ls command with all the pretty colors!

ls for windows

Filed under: Computers No Comments
24Jan/100

Moving to Windows 7

After a few years of running Ubuntu as my main OS, I decided to try out Windows 7.  Of course receiving a fully licensed copy of Ultimate x64 through Technet may have played a part in it.  The install went very smoothly.  All my devices were discovered with no issues and all my software seemed to install fine.  Almost...  At some point I was trying to organize some files when Explorer decided to start dragging its feet.  It would take several seconds just trying to move or delete a file.  It seemed to lock up when creating a new folder.  I immediately regretted switching back to Microsoft.  It makes no sense that a fresh install of such a supposedly wonderful OS would be acting like this.  But I can't stand being defeated, so I resolved to figure out what was going on.

First I thought it could be the hard drive.  Quick test went ok.  To help make sure it wasn't hardware, I tested the memory.  Ok there.  I asked my buddy Google, and while he and other people had the same issue, it wasn't clearly an issue with Windows itself.  So I made sure I had updated drivers for my motherboard.  Still the same problem.  It was time to dig out the Sysinternals tools.  I downloaded Process Explorer, Process Monitor, and Autoruns.  There didn't seem to be any rogue service interfering.  So I began disabling all startup processes and shell extensions.  Rebooted and everything was better.  After some more narrowing down, it turned out to be the Notepad++ shell extension specifically.  So if anyone else runs Notepad++, be advised.  Luckily Microsoft can't be blamed and I decided not to abandon it before the first day was through.

Speaking of Notepad++, I am currently trying to find the holy grail in development environments so any input would be nice.  I have set up TortoiseSVN and msysgit for version control, Notepad++ for editing, and Filezilla for FTP.  I am also setting up VMware Workstation so I can host a lamp server locally for testing.  I would love to have an IDE set up, and have downloaded Aptana.  But every time I try to use one, I can't seem to make everything fit together right and it just seems to add more work instead of increasing proficiency.  Anyone have a good setup they would like to share?

Filed under: Computers No Comments