PerlStalker's SysAdmin Notes

Notes from the life of a systems administrator

Switching to a Standing Desk

System administrators have a fairly sedentary job. With the exception of occasionally racking or unracking servers, we’re pretty much desk bound. I’m certainly no exception.

Several months ago, I noticed that sitting all day was starting to cause me pain in the backs of my thighs. Now, I don’t know about you, but I’m not a big fan of pain, especially while I’m working. The pain would, eventually, drive me from my chair. Standing relieved the pain almost immediately but I could work standing up because my monitors were still sitting on my desk, too low to see.

I had heard of standing desks before and started to look around to see how I could cobble one together on the cheap. There was an old workbench/desk that we were pulling out of the server room that I could use to raise my work surface up so that I could stand. I talked to my boss and he suggested that I look online for something that would do the job without having a seven foot workbench sticking up over the five foot cubicle walls.

In my searching, I discovered the Ergotron WorkFit-S. At almost $400, it’s a little spendy but my boss agreed to get it for me. You can see what it looked like set up on my desk when I first got it.

It was a little strange working standing up, at first, but after a while, I got used to it. It was easy to do since I wasn’t dealing with the pain of sitting down all the time. The best thing about using the WorkFit-S is that it’s a dual sit-stand system which means that if I get tired of standing, I can simply slide it down sit for a while.

Not long after I took the above picture, I rearranged my desk so that I had work surfaces at both standing and sitting height. Generally, all I need while I’m standing is a place to put a notepad to jot things down on while I’m working. (There is a work surface add-on for the WorkFit-S but I didn’t get it.)

On average, I stand about half of my day. Some days a little more some days a little less. It’s all about listening to your body. When my feet start to hurt from standing, I’ll sit down. If my legs start to hurt from too much sitting, it’s back up and I’m standing.

One of the nicest things about standing is that it’s a great way to deal with “that 2:30 pm feeling”. I’ll tell ya, it’s a lot harder to doze off when you’re standing up. I’ve found that if I’m having trouble focusing or am feeling a little tired, standing helps me stay focused.

One thing I began to notice a month or so back is that the WorkFit-S is a bit short for me while I’m standing. I’m six feet tall and, lifted all the way up, the monitors were about 3-4 inches below what was comfortable. The good news is that Ergotron makes a Tall-User kit which can add something like eight inches to the height of the monitors as well as adding a bit of tilt. Set at its lowest level, the Tall-User kit added the extra height I needed. (Actually, it added a bit too much when slid all the way up so I, simply, don’t slide it all the way up.)

There was an extra benefit that I hadn’t counted on. When I’m working with someone on a problem in my cubicle, I can raise my WorkFit-S to the standing position which makes it much easier for both of us to see. Plus, it’s right at eye level if I’m up drawing things out on the whiteboard. I wouldn’t buy it just for that reason but it’s a nice plus.

Now, why did I write this on a blog that’s mostly full of tech notes and documentation? Simple. The notes on this blog are about things I’ve learned which make my job as a sysadmin easier or more enjoyable. Moving to a standing desk certainly qualifies.

Now, here a few of things that you should be aware of if you decide to go with a standing desk.

  • Be sure that you wear comfortable shoes. I’d also recommend a pad like you’ll see cashiers using in the store. You’ll last a lot longer standing up. Also, don’t be afraid to move. I tend to pace a little when I’m standing. Moving around will help your feet as well as helping your circulation.
  • If you go with a standing desk that allows you to sit down, don’t be afraid to do it. It’s all about listening to your body.
  • The first week or two using a standing desk are going to be a bit painful if you aren’t used to standing that much. I tried to go in three hour chunks when I first started. (Three hours standings and then one hour sitting.) That helped me get used to standing but I’ve cut back a bit. As I said, I’m standing about half the time now. Usually, I’ll stand for a couple of hours then sit for a couple of hours but it varies, especially if I have meetings or am working in the server room.
  • If you’re working in a cube farm rather than a private office, be aware that your monitor may be above the cube walls when you’re standing. If possible, position your monitors so that your monitors aren’t as visible. (As sysadmins, we occasionally work with sensitive date. You don’t want to show it off to the entire office.)
  • Don’t be afraid to try it out with a few bricks and boards before spending big bucks on something.

That’s about it. I highly recommend using a standing desk. I feel a lot better, physically, if I can stand for a part of the day than I ever did after spending the whole day glued to my chair. In the end, it’s all about being comfortable and not letting your work environment have a negative impact on your health.

Have you considered using a standing desk or are you using one now? Post your experience in the comments. I’d love to hear about it.

Emacs and Tmux

Hello. My name is PerlStalker and I’m an emacs user. I love emacs and use it for nearly everything but there are a few things it’s not good at. (“Like editing,” I hear all you warped vi users cry.) Among them, and most important to me, are window management and terminals.

Let’s start with terminals. I use eshell from time to time to do quick and dirty things on the command line but I always run into weird things that don’t work like I expect. For example, there’s a little one liner I run to convert the mp4 videos of my podcast that I get from YouTube to mp3. Eshell chokes on it. The more powerful and better featured term-mode and it’s more friendly cousin multi-term-mode are pretty good but I still run into tools, from time to time, that break it. (To be fair, it seems to be better in emacs 24 but I haven’t played with it as much.)

My main use of terminals, however, is logging into Linux servers and making changes. I can do weird things with eshell and tramp to edit files but it’s kinda slow. If I try to edit a file on the server through an editor in term-mode, all sorts of things break.

The other thing that emacs is bad at is window management. A little terminology before I go further, emacs uses the term “frames” for what X11 and Microsoft call windows. The term windows is used by emacs when it splits a frame to display multiple buffers. Emacs can split frames horizontally and vertically all day and not have a problem. Where things get hairy is if you use something like gnus which feels like it can do whatever it wants to your window layout at anytime. It’s a real pain in the neck when I have multiple buffers opened, looking at different things, then hit M-x gnus to check my email and boom all of my buffers have been hidden in favor of whatever gnus wants to do. Not cool, emacs. Not cool.

However, something that is good at handling window layouts and shells is tmux. Tmux is similar to screen in that it provides an “always-on” session that you can access from multiple places. Where it beats screen is in its scriptability and window management. Those two features make it especially nice for what I’m going to show you. On a side note, you can apply most of this to screen with a little work but it was really easy to do in tmux.

The super secret ingredient to all of this is emacs server and emacsclient. Emacs server allows you to connect to a running instance of emacs to do things (like edit files or run elisp functions) without starting a whole new emacs instance. That makes it really fast. You can start emacs server by running M-x server-start or have it happen automatically when emacs starts by putting (server-start) in $HOME/.emacs. You can even use emacs –daemon to start emacs in the background when you log in or with the @reboot tag in cron to start it when the machine starts. The –daemon option has the fringe benefit of leaving emacs running even if you log out.

Now to how this all works with tmux. First, I need to redefine “window”. (Don’t you just love overloading definitions?) Basically, tmux only lets you see one window at a time. You can switch between them but you can never see more than one. However, you can split them into “panes” and this is where tmux shines. I’m not going to get into here but you can see the man page to see how easy it is to create, resize and navigate between panes.

The first problem I needed to solve was to quickly and easily ssh into servers. Based on the examples in the docs, I added these lines to my $HOME/.tmux.conf.

bind-key S   command-prompt -p "host" "split-window 'ssh %1'"
bind-key C-s command-prompt -p "host" "new-window -n %1 'ssh %1'"

If I hit the prefix (C-b by default, C-z in my case) followed by C-s, tmux prompts me for a host name (which can also be user@host) and then opens a new window for the ssh session. If I do C-z S instead, it opens the ssh session in a new pane in the same window. Using a pane rather than a new window is useful when I’m checking things on multiple servers at the same time. The window or pane closes when the ssh session is finished.

Now for the fun. Here’s where emacsclient comes in. Let’s say that I want to open emacs inside tmux. By adding this magic to .tmux.conf and reloading the config I can open emacs in a new window (C-z y) or new pane (C-z C-y).

bind-key y   new-window -n "emacs"  "emacsclient -nw"
bind-key C-y split-window "emacsclient -nw"

Emacs opens extremely quickly because it’s already running. Even better, because it’s emacsclient, you can switch to any buffer that you already have open in other clients, even if you opened it in the X11 version of emacsclient.

That’s great but there are other things I want to do in emacs besides edit files. For example, suppose I want to jump into gnus to check my email.

bind-key g   new-window -n "gnus" "emacsclient -nw --eval '(gnus)'"
bind-key C-g split-window "emacsclient -nw --eval '(gnus)'"

C-z g opens gnus in a new tmux window and C-z C-g opens gnus in a new pane.

I’m using a personal convention that whatever key I bind, by itself, opens in a new window and control plus that key opens in a new pane.

If you can script it in elisp, you can make it a shortcut in tmux. I have shortcuts to open w3m …

bind-key W   new-window -n "w3m" "emacsclient -nw --eval '(w3m)'"
bind-key C-w split-window "emacsclient -nw --eval '(w3m)'"

… and I have one to open the RT command line with emacsclient set as the editor in a multi-term buffer. (The editor magic is hidden in a separate script (~/bin/rtc) to get around some restrictions with the RT command line and the EDITOR environment variable.)

(defun rtc ()
  (interactive)
  (if (get-buffer "*rtc*")
      (switch-to-buffer "*rtc*")
    (rtc-create)
    )
)

(defun rtc-create ()
  (eshell t)
  (rename-buffer "*rtc*")
  (goto-char (point-max))
  (eshell-kill-input)
  (insert "~/bin/rtc")
  (eshell-send-input)
)

Then in .tmux.conf:

bind-key C-r split-window "emacsclient -nw --eval '(rtc)'"

Now I can open my ticket list and edit tickets in a pane while I actually work on the ticket in another pane.

You could use this as an example for opening any shell app within emacs. Obviously, if you just want to bring up the app outside of emacs, you can do something magical like this …

bind-key C-m command-prompt -p "man" "split-window 'exec man %%'"

… which prompts you for a man page when you hit C-z C-m then opens it in a new pane. It’s super convenient if you want to check the docs for a tool you’re using. (I could have used emacs man- or woman-mode instead of calling man directly but this was simple and easy.)

If, for some strange reason, you would rather use vi to edit a file, you could simply replace man in the previous command with vi and change the key binding.

For even more special sauce, you could use byobu with tmux to display any number of fun widgets at the bottom of the window. I use a custom script combined with gcalcli to display the next thing I have coming up on my calendar.

The combination of tmux (+byobu) and emacsclient gives me a very efficient and very powerful way to get things done at work. If you’re an emacs user, I highly recommend looking into emacsclient even if you don’t need tmux or screen but combining the two makes for much joy and happiness.

Managing /etc/hosts With Puppet

So, here’s the situation. I have a stack of VM servers running KVM and libvirt. The hosts need to connect to a SAN for ISO storage and, potentially, VM disks. The problem is that the VM running DNS may not be up yet when the host starts. That’s a problem since I’m referencing the san by it’s host name rather than the IP address. Yes, I could change all of my configs to use the IP instead but host names are a lot easier to deal with, most of the time.

Well, I could work around the lack of DNS by putting an entry for the server in /etc/hosts but then I’d have to update it on every server if I ever change the IP address. Fortunately, puppet makes it easy. Sort of.

Puppet is a wonderful tool for managing Linux (and other *nix) servers. It’s a little weak, though, when all you want to do is add a line to file. That’s exactly what I wanted to do with /etc/hosts.

The good news is that puppet has a hook into the tool augeus. That makes editing the config relatively easy but plugging that into puppet is still a little messy. To make it easier, I created a class and define to tidy that up a bit.

Update [2012-09-03 Mon 07:15]: Puppet actually makes this easier than I thought. There already exists a host data type that I totally missed before. Dominic Cleal also pointed me to a module that he wrote that adds augeus providers to some of the default data types, including host.

class hosts {
  define entry(
    $ipaddr,
    $canonical,
    $aliases = 'UNSET' # I want to make this an array
    )
    {
      augeas { "create_$title":
        context => '/files/etc/hosts',
        changes => [
                    "ins 01 after 1",
                    "set 01/ipaddr $ipaddr",
                    "set 01/canonical $canonical"
                    ],
        onlyif  => "match *[ipaddr = '$ipaddr'] size == 0"
      }

      augeas { "update_$title":
        context => '/files/etc/hosts',
        changes => [
                    "set *[ipaddr = '$ipaddr']/canonical $canonical"
                    ],
      }

      Augeas["create_$title"] -> Augeas["update_$title"]

      # It would be great if I could loop this
      if ($aliases == 'UNSET') {
        augeas { "alias_$title":
          context => '/files/etc/hosts',
          changes => [
                      "rm *[ipaddr = '$ipaddr']/alias[1]"
                      ],
        }
      }
      else {
        augeas { "alias_$title":
          context => '/files/etc/hosts',
          changes => [
                      "set *[ipaddr = '$ipaddr']/alias[1] '$alias'"
                      ],
        }
      }
      Augeas["update_$title"] -> Augeas["alias_$title"]
    }
}

There are a couple of known issues. First, you can’t unset an entry. If you add a host with this and then decide that you no longer want it there, you can’t take it out. I don’t think it would hard to add that feature but I haven’t.

Second, the define only lets you set the first alias. Augeas allows for multiple aliases and I could pass an array to the define but I don’t know how to loop through that list.

Anyway, here’s how you use the define within a node or class definition.

include hosts
hosts::entry { 'san':
  ipaddr    => '192.168.1.5',
  canonical => 'san.example',
  aliases   => 'san'
}

You can use as many hosts::entry blocks as you want.

Testing for “Bitness” in Configuration Manager 2012 App Deployments

We started our deployment of System Center Configuration Manager 2012 last week and I ran into an interesting problem.

One of the first apps I rolled out to test with was Strawberry Perl. I grabbed the 64-bit MSI and ran through the Create Application wizard and added the MSI to the deployment types. One quick deployment later and ConfigMgr was happily installing perl on my servers.

… Most of my servers.

You see, I still have a couple of 32-bit servers hanging around and the 64-bit MSI wouldn’t install. D’oh. So, I figured it would be easy to jump into the Requirements of the distribution and limit the package to 64-bit systems. It wasn’t. While there are options for RAM amounts, CPU speed and disk space there’s nothing to test for CPU architecture.

"Create Requirements"

To fix this, I created a new Global Condition to test for the “bitness” of a server.

The information I’m looking for is in the AddressWidth property of the Win32_Processor class. You can see the list of properties by running gwmi Win32_Processor in powershell. If you run

gwmi -query "select * from Win32_Processor where AddressWidth = 32"

and get back a screen full of text, your system is 32 bit. If you specify the wrong value for AddressWidth, the command will exit with no output.

The condition properties should look something like this when you’re done.

"bitness Properties"

Once that’s done, it was a simple matter of adding the check to the deployment.

"Create Requirement"

Set the Value field to 64 for 64-bit systems and 32 for 32-bit systems and you’re done.

Having said all that, I’m still not sure that I haven’t missed a setting somewhere. One would think that a test to see if an app matches the target architecture would have been a no-brainer to include. If there’s a setting I missed, please let me know because not having it just doesn’t make any sense.

Update [2012-05-01 Tue 08:25]: One of my co-workers pointed out that there is, in fact, an “easier” way.

When you add a requirement, there’s an option for Operating system. In the tree view at the bottom of the pane, you can select just the 64-bit version of the OS that you’re targeting.

"OS Requirements"

I totally missed that before. It takes a bit more clicky-clicky to use for every 64-bit or 32-bit only package you deploy but it may be more obvious to the next admin.

gPodder and EMMS

I use gPodder to pull podcasts for all my listening pleasure at work. I had been using gnome-mplayer to listen to them but after a recent re-install, gnome-mplayer started to hanging every when I pause the playback. Gmplayer works fine so I know that mplayer, by itself, is not the problem but that doesn’t have a systray icon to make it easy to click and pause playback.

Since I’m so frequently using emacs, I thought to myself “I wonder if I can do this in emacs?” Of course, I can. Enter emms.

1 emms Configuration

Based on a little bit of research, I decided to use mplayer as the backend. The setup is pretty easy. This is what I’m using based on this blog post.

;; emms

(require 'emms-player-mplayer)
(require 'emms-source-file)
(require 'emms-source-playlist)

(setq emms-player-mplayer-command-name "mplayer"
      emms-player-mplayer-parameters '("-slave")
      emms-player-mpg321-command-name "mpg123"
      emms-player-list
      '(emms-player-mplayer
        emms-player-mplayer-playlist
        emms-player-mpg321
        emms-player-ogg123))

2 gPodder Configuration

Now that I can listen to the music via emacs, I need to tell gPodder how to feed the podcasts into emms. It’s simple to add them to playlist from outside of emacs with this:

emacsclient --server-file=work --eval '(emms-add-file "TD140.mp3")'

In $HOME/.config/gpodder/gpodder.conf, you can set the player setting. gPodder replaces the %U with the file name. … In theory. In practice, gPodder only replaces %U if it’s at the end of the line. That means I needed to write a quick wrapper.

(add2emms.pl) download
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/perl
use warnings;
use strict;

my $file = shift @ARGV;

my $emacs = "emacsclient --server-file=work";

my $command = "$emacs --eval '(emms-add-file \"$file\")'";

#print "$command\n";

system "$command";

I run emacs in daemon mode (with TCP). If you don’t, you’ll want to change the $emacs variable.

And you’re done. Happy listening.

IkiWiki and Org-mode

I love emacs. I practically live in emacs at work. One of my favorite features is org-mode. At its simplest, org-mode is simply a markup language similar to wiki-text or markdown. When you get into it more, the real power of scheduling and task management comes out. In my case, it’s the only system that’s I’ve been able to use to stay organized. I’ve been so happy with org-mode that I start to looking into using for this blog.

I looked at several options from using org-mode’s publish feature to publish HTML that I could paste into Drupal or using the native publish feature on its own. In the end IkiWiki won out.

IkiWiki has several useful features for me; mainly, the RSS/Atom feeds and a tag cloud. Most important, the formatting language is pluggable and there’s a plugin to use org-mode instead of the native markdown. Getting it setup was pretty straight forward but had a couple of hang-ups because of my personal setup and there are a couple of things to look out for when actually using it.

1 Installing

The first step (after installing IkiWiki) was to get Chris Gray’s excellent new_org plugin. This is what does the translation of the org files to HTML. Actually, it doesn’t do the translation. Instead if feeds the file to emacs to spit out the HTML. That’s were my first problem hit.

As I said, I already use emacs and one of the things I did on my server was start emacs in server mode. Once in server mode, I use emacsclient as my editor which really fast. The problem is that new_org also uses emacs server to keep from starting emacs for every change which speed up the translations. Unfortunately, the two servers conflicted. I think there was an issue with the two trying to use the same unix socket or something. I eventually switched emacs to use tcp sockets by putting (setq server-use-tcp t) in $HOME/.emacs.

There are also a few changes in new_org.pm needed to support tcp sockets. First emacsclient -s org-ikiwiki-compiler needs to be changed to emacsclient –server-file=org-ikiwiki-compiler. Second emacs –daemon needs to be changed to emacs –daemon=org-ikiwiki-compiler. There are a couple of places where those changes needed to be made. Once those were in place, I was almost able to publish files.

I ran into a problem with emacs hanging when I tried to publish with ikiwiki –refresh. It turns out that I was still using the ancient version of org-mode that came with emacs on Ubuntu Lucid. My problems went away after upgrading org-mode to the latest version.

Those items done, I was able to publish new posts all day long.

2 Publishing

2.1 Blog System Files

The first thing to keep in mind is that the index, sidebar and tag files all need to remain in markdown with .mdwn extensions. When I tried to change them to org-mode, IkiWiki would not process them correctly. That’s a little annoying but no big deal, really, since I hardly ever need to touch them. I mostly org-mode for the blog posts.

2.2 IkiWiki Directives

This is where things got a little weird. IkiWiki directives use the same syntax as org-mode does for links. For the most part, the two play well together. For example, the tag directive does, in fact, add tags to the post. I did run into an issue with images, however. It’s possible to use the basic org-mode link syntax to add an image but it doesn’t properly deal with paths. IkiWiki’s img directive takes care of that but emacs would see the HTML that the directive wrote and happily escape the HTML.

The solution is to wrap the img directive in #+BEGIN_HTML and #+END_HTML blocks and everything is happy.

I haven’t used any of the other directives yet so I can’t tell you how well they work. Though that may be why the index and tag pages where giving me grief.

3 Conclusion

With those little snags out of the way, everything is full steam ahead. I’ll keep y’all informed of anything else I run into but it’s been smooth sailing so far.

Renovations

I’ve been a busy PerlStalker this week. As you may have noticed, this site has changed somewhat. I’ve recently decided to drop Drupal and switch to IkiWiki. Drupal has been good to me but it’s really too heavy for my little blog. I have no use for most of the features that Drupal provides and it’s not worth it to me to keep it up-to-date.

IkiWiki has a couple of really nice things going for it. First, it spits out static pages. Except for search and comments, there’s no need for any dynamic content on my site. Google and Disqus fill both needs. I could use the IkiWiki CGI but there’s really no need.

Second, I can compose my posts in emacs org-mode. It beats the heck out of writing all of my posts in straight HTML. I had to jump through a few hoops to use org-mode but it’s working great now. I’ll post more on that later.

By default, IkiWiki uses a pretty nice syntax called markdown. I chose not to use it because org-mode is, in many ways, simpler and more powerful than markdown and I use it for all of my note taking and documentation at work.

Now, a little housekeeping. One of the drawbacks to moving to IkiWiki was that I was unable to get it to use the original posting dates from Drupal. All of the imported posts have a line at the top that has the original posting date.

Along those lines, you’ve probably already seen the slew of reposts in your feed reader. That’s a symptom of the conversion to IkiWiki. This should be the only time it’ll be a problem.

Thanks for reading.

Setting the Default Monospace Font in XFCE

Originally posted at [2011-12-19 Mon 09:38]

I use XFCE4 on my box at work because it’s lightweight and still provides the features I want. I wanted to change the default font so that emacs used the font I wanted without having to change my .emacs file. Unfortunately, XFCE4 only lets you set system default but not the monospace font. Fortunately, XFCE4 uses fontconfig. All you need to do is edit $HOME/.fonts.conf and add this little XML snippet. (See the fonts.conf manpage.)

<match target="pattern">
        <test qual="any" name="family">
                <string>monospace</string>
        </test>
        <edit name="family" mode="assign">
                <string>Terminus-9</string>
        </edit>
</match>

If you have to create $HOME/.fonts.conf, make sure you wrap that snippet in <fontconfig> tags.

Running Emacs Functions in Batch Mode

Originally posted at [2011-11-10 Thu 10:22]

I use emacs for a lot of things at work. One of the more useful is org-mode for to do lists, scheduling and meeting notes. Org-mode can sync to mobile devices running the app MobileOrg. Unfortunately, that sync is a manual process. The good news, emacs is scriptable and can be run in batch mode to automate things. Here are a couple of things I use.

Note: Emacs batch mode spews a huge amount of crap to stderr. If you’re putting these in cron, you may want to slap a 2>/dev/null on there to keep it quiet.

1 Sync with MobileOrg

The first line pushes the local org files to sync and the second pulls changes coming from the device.

emacs --batch -l $HOME/.emacs -f org-mobile-push
emacs --batch -l $HOME/.emacs -f org-mobile-pull

2 Publish Meeting Minutes

I keep meeting minutes using org-mode and then publish them to my web site. I also maintain my personal work site using org-mode. To publish all configured sites, you can use the script below.

emacs --batch -l $HOME/.emacs -f org-publish-all

If you want to publish a specific site rather than all of them, you can use this incantation.

emacs --batch -l $HOME/.emacs --eval '(org-publish "meetings")'

Updated [2012-03-22 Thu 22:49]: It’s also possible to use –eval with emacsclient. You, of course, need emacs server running but it makes those batch runs much faster.

Finding Old Computer Accounts in Active Directory With Powershell

Originally posted at [2011-06-20 Mon 13:31]

Following up on a previous post discussing finding old user accounts in Active Directory, here’s how you find old computer accounts.

This works on basically the same premise as the user script. In short, we’re going to check the last time the computer logged into Active Directory. That happens on every reboot and from time to time while the machine is up. The same warning applies to computers as it does for user accounts. The last logon timestamp is only accurate to about a week. Since people are generally checking back six months or more, it isn’t much of an issue.

To get the list of stale machines:

get-adcomputer -properties lastLogonDate -filter * | where { $_.lastLogonDate
-lt (get-date).addmonths(-12) } | FT Name,LastLogonDate

You can throw a sort in there if you’d like.

get-adcomputer -properties lastLogonDate -filter * | where { $_.lastLogonDate
-lt (get-date).addmonths(-12) } | sort Name | FT Name,LastLogonDate

It’s just as easy to delete all of those accounts. I’ve added a -whatif to make it harder to do something stupid with cut-and-paste. Take it off to actually delete the computer accounts.

get-adcomputer -properties lastLogonDate -filter * | where { $_.lastLogonDate
-lt (get-date).addmonths(-12) } | Remove-ADComputer -whatif

Updated 2011-06-20 14:15: Added a missing } to every example. Don’t you hate it when you have a bug in your code and you copy and paste that same line over and over again? Me, too.