Integration • 53:18
Gain inspiration--and practical advice--from pragmatic Mac OS X system administrators who use Python, Scripting Bridge, Automator, AppleScript, Ruby and other technologies to automate their world, speed repetitive tasks and build highly useful tools.
Speakers: Andy Leeper, Chris Adams, Josh Wisenbaker
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it may have transcription errors.
This is year number five for scripting for system administrators. And I must admit, I said last year we're running out of ideas. It was hard, but we have some very, very good stuff. I beat an engineer into submission until he learned Ruby in seven days. Let that be a lesson to anyone wanting to do this next year. But Andy did a great job, so we have him coming up here. Chris comes up second, talks about kicking the kicker.
And we have a little contest at the end. We need a name for this script, so he'll be talking about that a little bit. And then we follow up with Josh, who wrote a little script just to use the word injector. So we have a home injector script that you can use at login to do some stuff. So hopefully it gets you guys energized, excited about scripting, show you some new stuff, some of the cool stuff using Leopard and some of the other technologies. For the first time this year, and I'm very excited about this because we've been trying it for a while but never got anything to stick, Chris will be talking about some of the scripting bridges, so how to go from your scripts to actual Objective-C and things like that to add a little more power and to kind of combine both of those worlds. So without further ado, I'd like to introduce Andy Leeper. is gonna come up here and talk to you about some Ruby.
Hello, everybody. I'm here to talk about Ruby. And I'm actually going to do that by giving you my story of how I did this. So if you like a lot of caffeine, this is great. If you just so happen to be one of those people that likes to learn scripting languages in their free time, this is a good way to go through and kind of jump start that whole thing. So let's go ahead and jump in. On the first day, what happened was Joel gave me a buzz and he said, "Hey, Andy, I'd really like you to come speak at WWDC this year." And I was thinking to myself, "Yes, of course. Why wouldn't I want to? This is a great opportunity. I get to get in front of all you guys and talk to everybody. It would be awesome." And he said, "Well, it would be in our scripting for sysadmin session." And I had come for the past four years, you said it was, five years, and I thought it was a really cool session, I felt honored. So I jumped into it and I had just the tool I wanted to show off. It was a tool that my coworker wrote called Ghost Host which takes information from open directory, puts it into a MySQL database and kind of uses some augmented data to kind of bring all this together into a nice user interface. So I said, hey, you know, he developed this thing in three or four weeks. I can come up and talk about that. You know, might be really interesting to some folks. And Joel liked the idea. But he came back to me and said, hey, you wrote this all in PHP. Can you do it in Ruby? And I was thinking to myself, uh.
I don't know. One of my friends at work is a big Ruby evangelist, and he sits there and he talks about, man, you can write everything in two lines of code. You don't have to do anything. In fact, I wrote this wonderful application that does the same thing in like seven lines of code. So I was a little skeptical, so I went and did my own research. And, you know, I went to Google, I typed in Ruby, and the first thing that I got was this website, which is the ruby-lang.org website. So some of the things you'll see over on the right-hand side is the Try Ruby in your web browser. That brings you to this page. And this is a really useful tool for just checking it out on your own time. It kind of hand-holds you through it. And it was a really useful resource for me. So I went through the first chapter. And when I went through the first chapter, I realized that, you know, this is how I do something in Perl. This is how I do something in Ruby, you know, as far as declaring my variables. all that different.
So I said, hey, Joel, you know what? I'd be honored to go ahead and do this. I thought it would be a great opportunity. So I jumped right in. So the next day-- I go to work. I work hard like everybody else. I put in my 10, 15 hours. And when I get home from work, I want to play games. I don't really want to do more work, right? Well, I realize now I should probably be a good engineer.
I should spend some time and get back onto the Ruby thing. So I pulled open Safari. I searched for Ruby again because I forgot what website I went to. And I wound up at the same website. Fortunately, it's the first Google links so I didn't have to do much crawling. And went back to the tutorial. This time I went through chapters 1 through 5. So chapters 1 through 5 kind of uncovered a little bit more stuff. It uncovered stuff like control structures. So in Perl, you know, this is how you do a for each loop to go through an array. And in Ruby, it's not so similar. You end up doing stuff like things.each do thing and it looks nothing like Perl. So I figured, well, the concept's still the same.
I can still, you know, apply what I know about scripting into learning this new thing. So I decided, well, hey, I'm just gonna make a list. You know, so what are some things that I typically use? I typically use variables. I typically use loops. And I typically use functions like everybody else.
And so I decided to map it out between Perl and between Ruby. So the next day, I go into work, do my normal routine, drink some coffee, type some email, do the normal thing there. I got an email that said, hey, your code is due in five days for review.
I was a little bit panicked. No. I don't really have much together. I thought I was going to have a couple weeks to present this thing. I have five days to put this thing together. Oh, I probably shouldn't have played World of Warcraft last night. Not a good idea.
It's really intimidating to learn a foreign language. By the way, I threw this in here because it's a little side joke to those who do know Ruby. The creator of Ruby is Japanese. But it's really intimidating to learn a new foreign language. But I realized that, hey, as long as I remain methodical about it, and I plan everything out, it should still come out OK. So I started off with the basics. This is how you do hello world in Ruby. Not all that different from Perl. You just use a different command.
Actually, you can still use print if you wanted to. This is how you do arrays. Still really basic. Actually, to those of you who know Python, it's pretty close to the Python syntax. And I also found some other stuff along the way, such as, hey, there are methods to call on these things, because everything inside of Ruby is an object. So you get some free stuff.
This is much easier than it is in Perl. So I got so fascinated with the stuff that I was learning when I was going through this that I stayed up until like 3:00 in the morning playing on it. And it was just fun. And I fell asleep on my keyboard.
It's not fun to wake up to beep, beep, beep, beep, beep, you know, that's going on on your computer. The next day was Saturday. which is why I was able to stay until 3:00 AM. And I woke up at 9:00 AM to get a kick start on it. And I like this icon. It's hidden away in system library core services. But basically, I wanted to wake up, drink some coffee, jump straight into code. And what happened?
I'm a real person. I really do like to have my free time. I really do like to play games. So I did some gaming, but then I decided at noon, oh, no, I really need to jump into it. And one of the things I wanted to tackle was how do I get this thing to talk into open directory because this is kind of what I need to develop. I'm sure that there's code that's already done for me here. So I found this thing called RubyGems. This is a box of gems that does -- it's kind of a package manager for Ruby. So it's like CPAN for Perl or like eggs for Python. Ruby has gems.
And I found a module called RubyNet LDAP. And what that let me do is it let me, first off, take a lot of their example code to get my program to talk to Open Directory. But it was not all that hard to get my script up and going based off the example code. I just tweaked the server name, and I tweaked a couple of the things here and there to get what I wanted out of it. And by 10 o'clock that night, I had something that was working. I was talking to Open Directory. I was getting information out. It was parsing it in the way that I wanted it to be parsed. And everything is kosher.
And I think the reason why I was able to get to that point was because I stopped, I listed everything out, I was methodical, and I broke the problem down into smaller chunks. So when you're learning a new language, do the same thing that you do in the other language.
So the next day involves Rails. So I wanted to get a jump start on it. Unfortunately, I'd slept because I was exhausted from all this coding. And I woke up a little bit later. And I had my script that was spitting out stuff like this. It was giving me the IP address out of Open Directory. It was giving me the host name out of Open Directory. I had a little ping function that would see if it was up or down. And I wanted to convert this into something that was a bit more snazzy and put a nice little web interface on it. I wanted it to be like this program. So somebody at work had been evangelizing Ruby on Rails to me, and they said, oh, yeah, Rails is the way to get all your code and put it up on the web and takes five lines of code, same Ruby evangelist guy. And so I started to do some searching, and I came up on the site, which is the rubyonrails.org website. One of the things that was really useful here was the screencast. And people talk much faster than I do through these screencasts, but it shows how you can build a completely full, a fully functional application that talks to Flickr and does all sorts of crazy stuff. It's really inspirational.
And one thing that's nice is OS X has all this stuff built into the system. So I didn't have to go download and install Rails or anything. It's all built into Leopard out of the box. So for me to get a basic website up, I just had to type in these three things. And you get your basic start page. Not all that exciting. So I figured out, oh, I'm just going to follow the next two steps, or two lines of code, to get this thing to generate a template for me to use. These are kind of what you would use. And what's nice is on OS X Leopard, all this stuff automatically creates the SQLite database for you to use, and it does all this other stuff for you magically.
And you get a really basic website. So this doesn't look anything like the other page that I wanted. I wanted to figure out how I can get my code into this and to have it all formatted all nice and pretty and to make it presentable. And I ended up bashing my head into the wall trying to make this work. And I think the biggest reason why is because I was trying to write stuff procedurally, and it doesn't work that way with Rails.
So I found that out by going into Freenode on IRC and wanted to pound Ruby on Rails. The people there were really useful. There's a topic on the channel that pointed me to this really useful Ruby on Rails documentation website called noobkit.com. Very appropriate for me. And it was all interactive, showed a lot of examples, but I still didn't really get it, and I went to sleep. Just kind of gave up. But on the next day, I went into work.
I figured, hey, I'm going to talk to my Ruby evangelist friend because he said it would be five lines of code. I did my five lines on the terminal and it's not doing it. So I talked to him a little bit about what problems I was running up against. And he went on a spiel about the Rails way.
So the Rails way, what is that? Well, in all the sessions at WWDC, we've been talking about model, view, controller. The Rails way means using a model, a view, and a controller for your program. I was trying to write everything procedural. I was trying to generate all my stuff. I had my view mixed in with my logic and all this other stuff. And it wasn't working. And after talking to him about that, he kind of said, oh, yeah, no, it's very much like Objective-C. So I know that you've played around with it a little bit. Just kind of do it that way. So I did. And, you know, I was thinking, oh, wow, okay, that makes a lot more sense. I don't know why it wasn't obvious to me before, probably because of the amount of caffeine I've been having. But I went home and decided, hey, I'm going to jump right in. I'm going to jump into the terminal, get hacking away. And I realized that the solution to my problem was kind of staring me in the face. You know, I already had the SQLite database that is housing all of my supplemental information, like I was doing with my PHP thing. And I realized that there's an additional piece of functionality that's built into Ruby on Rails, which is Rake. And I could just leverage those two technologies to get what I wanted. And the last day came, and I want to show you what I was able to put together.
So if we can go to-- hey, please. So let's pull up my website here. I was able to get it going. This is all in Ruby on Rails. So some nice things here is this lets you see information from open directory that you might find. So like your host name, your IP address. I did the DNS resolution by generating that information.
MAC address I was able to get out of there. The owner information was stuff that I put in. The location was stuff I put in. Serial number I put in. When the record was created, it was done in Open Directory. So I was able to merge these sets of information together. My Rake script that I'm running runs every minute, and it tells me whether or not a server is up and down. This is all, this didn't take less, more than a week to put together.
You can click on this stuff. You can go through and edit stuff. So I can say Pacific Heights, X-Motion, X-Motion, update. And it goes through and it updates it all on the back end and this is all nice and dandy. Obviously this is fake information or else I'd do a little bit more of a detailed demo. But I want to jump into the code that I used to put this together. I hope this is big enough for everybody way back there. But this is showing the view for the web page. So you'll notice it looks like normal HTML.
And down here, there's more normal HTML. You'll see that I have some extra stuff embedded in here. This shows off the templating style that Ruby gives you-- or Ruby on Rails gives you, rather-- which lets you go through and inject Ruby code into your HTML. So this is my view. This is what makes up the lovely page here. The model part-- I'm sorry, the controller part is over here.
This is saying whenever you go to the index page, I'm going to find all of the stuff that's inside of your database, and I'm going to go through and print it out. So the controller is pretty basic. And the model is all default, so there's no point in showing that. But one thing I do want to show you is where most of the stuff, where the magic is actually happening. It's inside of this lib task section. And this lib task section is where you can put these things called rake files, which are very much like make files. But this is the component that goes through, and I have this run nightly out of a cron. I just say rake update db, and it goes through and updates the database with new hosts out of it. And inside of here you'll see that there's the server name, LDAP port, all this other kind of stuff. All it does is it does a search for everything inside of a certain container, grabs it all and puts it inside of the database. I'm not going to dive too much into this because this is pretty much all sample code that you can get out of the RubyNet LDAP documentation. And then for updating the status, if stuff is online or offline, I have this check host script that runs on a, you know, minute basis. And all it does is it goes through and it gets all the records out of it where there's an IP address, and it pings them.
So I was able to put all this stuff together pretty quickly. Let's go ahead and go back to slides real quick. I was able to put all this stuff together in less than a week with a lot of procrastination, playing video games and stuff. And I think it's a really good story of how even if you're busy, you're overloaded, you have all this other stuff to do, you can still do it. It's just a matter of being methodical about it. These are the resources that I used to make myself successful. The rubylang.org website was great for getting started.
The interactive tutorial that they had on there was great. That's what made me say yes to begin with. The noobkit.com website was wonderful for the Ruby on Rails documentation. And the rubyonrails.org website was great for evangelizing all the different things that you can do with Rails. And RubyForge was where I found out about the NetLDAP website.
So these are really good resources to use. The people on IRC were so friendly. It's kind of a grab bag sometimes. But the people on these channels were great. They were really friendly, super accommodating. And the last thing on here, RDoC and RI, that's the online documentation that comes with Ruby. So it's all built into the systems. And with that, I'd like to go and hand it off to Chris, who's going to talk about kicking the kicker.
So I wanted to talk about the bridge support that is a very handy feature in 10.5, but since the goal here is to actually solve problems that we face as system administrators, I'm going to do that in the context of building a replacement for the kicker feature, which disappeared in 10.5.
So the much lamented kicker. So the real underlying goal here, though, is actually explaining how this is a very nice way for us to interact with the system. Now, our systems that we deal with these days are very complicated, and we need to actually do things with that. We have to restart things or edit policy when the network changes. Perhaps we have something where our users may be mounting volumes. They plug in a FireWire drive, and we need to help them pull the video that they've been working on off for an archival project because they're making 20,000 videos, and we don't want this to take a week doing it by hand. There's also a fair amount of time where we need to detect conditions so we can do something like restart a service which has problems with network transitions or some other policy decision. And the problem that we run into is that the traditional approach for this is to create something like a shell script. Maybe we come up with something where we watch on a log file or run something out of cron every minute or, in the worst case, give our user something and say, when it breaks, run this and it should hopefully be unbroken. The problem there is that this doesn't really work that well. You know, you set up your tail, and you watch grep for things out of syslog, and then a point release comes out, and you find out that somebody at Apple helpfully added more information into the log message, which is great, because now your logs convey useful information that you wanted to have, except that you got to go update your regular expression and push that out to all the machines, and that gets old. So... What we're trying to do here is get out of this business of being a maintenance programmer where we have to watch because we're consuming things that weren't really intended for our usage there. We're dealing with things like syslog that wasn't a reliable interface. We have things like running jobs out of cron where we have to deal with the fact that our job has to run every minute and it has no state. And it means that our scripts tend to be somewhat useless there. we have these 30 or 40 lines of code there, and exactly four of those actually solve the problem that we wanted.
In this case, restarting auto-mount whenever it started hanging, which was a problem in our environment prior to 10.5. And that's a lot of code for us to maintain. In this case, if you look all the way up at the top, you can see it uses the file tail module, which is not standard, so we had to install that on some of the machines that needed it.
This approach doesn't scale for the kind of complicated systems that we tend to be building these days. And a lot of the needs that have been coming up are the kind where the conventional approach is becoming less and less suitable for that. Shell languages are not particularly advanced, and now we need to do complicated string processing. We need to deal with other things like LDAP servers or databases. We might have to parse an XML file or a plist. And then we run into the other major problem for system administrator is that a lot of the software we use wasn't designed with us in mind. So we have to do something like script GUI events or reverse engineer a configuration file.
I think the answer is for us to go ahead and rethink exactly how we're going about running our systems there. So if we started to ask that question, what would we really want? Well, the answer is clear. We want to become first-class users of our systems. When we have problems that we need to solve, we want to solve it the same way that the engineers at Apple or at a software development company might solve it using defined APIs, things which were designed for that task so that they don't break, so that we don't run into the little list of caveats to go with things. In short, we want it to become more like software engineering where we actually are having time to think about things like performance, robustness, usability there, which is really getting past the idea that every problem should be solved with a couple of lines of shell scripting, which I think is a good thing. In this case here, this is an example of the scripting bridge framework code, And it's actually quite interesting when you look at it. It's ten lines of Python there. It's actually somewhat hard to have a network change monitor, which is what this is here. It will look every time the network interface changes. It will efficiently be notified, and it can run an arbitrary command at that point. So there's a significant chunk of your kicker replacement, and it's less work than it would have taken to go put something together in that you can get this running in a couple minutes, customize it, save it, and get on with life, and it won't break because we're actually using the native API. This is the big change that 10.5 gave us, is now on every Mac we have a standard interface for accessing the Cocoa APIs from a language such as Python or Ruby there. The frameworks will actually handle translating native Python or Ruby variables into whatever the Objective-C API is expecting. And I realize that this is a somewhat controversial issue, but I find that the resulting Python code is frequently cleaner than the Objective-C code just because the dynamic languages tend to have a very rich syntax, and we can take advantage of that without having to get into the conventional software distribution process and worrying about shipping out 32 and 64-bit binaries and so forth. So the major advantage here is the access that we get to things like the system configuration framework. We can load things like QuickTime if we feel like it. But it's also hard to underestimate the value of switching away from a shell script. And that tends to lead to things like being able to reuse the standard libraries. This is one of the selling points for a language like Python or Ruby, is that most of the problems that we need to deal with have already been solved. Want to add command line argument parsing so your users get something a little less hostile? It's pretty easy. Rich's logging there, which, as system administrators, we demand all of our vendors produce good logging so we have an idea of what their processes are doing. Well, we can do the same thing as well. So this is an example of the fairly minor differences between Objective-C and Python for most of what I'm showing here. You can see it's largely syntax, And in the examples that I worked with there, most of the time I was able to take an Objective-C example out of Apple's developer site, copy it into a text editor, change a few brackets, add an underscore, and run it, and it worked out of the box there. It's a pretty easy process there, and there is some good documentation available. The last part, I think, here is the key one, namely that if you have a Mac with the developer tools, You already have most of this code on there, everything from simple command line scripts up to things involving WebKit or complicated GUIs. So it's very much worth looking at.
I did run into a couple of issues which I wanted to point out. The main one just being the understanding that you are, in fact, now becoming a Cocoa programmer if you go down this route. So you have to understand that you're not pure Python anymore. In this case, it was an issue with run loops. It was fairly easy to solve, but I found that this was well worth the time investment.
And then the other point here is you can build GUI applications. there's actually a deprecated example in the developer tools, but it's better just to start with the Xcode template there. Now, actually, earlier in the week, it was at the Cocoa Heads talk, and they mentioned that there's actually an application there which you can go find on the shelf at most Apple stores called Checkout, which is written entirely in Python using the Objective-C bridge. The developer actually was saying that there wasn't a single line of Objective-C code in that application. So this is something that can be used in production.
Also take a second for a little bit of Python advocacy here. The situation is similar with Ruby, but basically if you're a system administrator, you want to know about at least the modules listed here and then look through the rich standard library out there. Because again, many of the problems that we need to deal with and the sorts of things that can take a great deal of time in a shell script are already done. So, at that point, let's go back to the kicker discussion here.
Kicker existed because the people at Apple working on OSX had the same needs that we do for dealing with changes. So they needed to be able to run code when the network changed, when the power state changed, the active user changed. And the problem was that this was done in the system configuration framework with an XML file. Now, if you're already thinking about this, you're going, wait, doesn't that live under /system? And that's the problem. Kicker wasn't a supported feature. It was the easiest way to have something run when an event fired off, but unfortunately it disappeared completely in 10.5 when they changed some of the structure and we were no longer able to piggyback on that code.
There are also a few other things that we can look at. Kicker was handy for what it was, but frequently we also run into cases where we might want to handle other sorts of events. FSEvents allows us to just monitor arbitrary file system activity in a very low overhead fashion. This is the same thing that tools like Spotlight use. And it's really handy if you do have some sort of complicated workflow where you need to be able to react to users creating files there.
You get an efficient notification, and you can process it directly in your Python code, the same as any other event. Workspace notifications are also handy. Those apply any time things like a disk image or a network volume is mounted, the active user changes, an application launches. And there's one other improvement that we can make here, which is that we can make this something that doesn't require running as root. This actually means that we can open this up to being a user-accessible tool where some of our users, many of mine are scientists have these complicated workflow requirements, which they're never really going to get quite set in stone, because their experiments change. And so I don't wanna give them root on the system, but I also don't wanna have to be restarting things and vetting their code all the time. Now they can just run it under their account, and I can just help them out if they get stuck.
I also took the opportunity to take advantage of the logging features and add a few things like being able to list the sort of events that you can monitor on a system so that you can do a little bit of discovery if you're trying to see what sort of tool you can solve. This example is a very simple one here. It uses the network change event just to tell Squid to reconfigure itself. There's Squid's kind of handy if you're doing web development, but unfortunately it doesn't handle network migration particularly gracefully.
And as you can see here, there's basically one line saying which key we want, and then we run a single command. And in the example below, you can see that when the network state changes, that fires off, and I didn't have to do anything more than that from the perspective of solving a problem.
Now, we're about to switch to the second and more interesting demo. But this one, to provide a little background there, OpenSSH has a dynamic proxy mode. When you run that, it will pretend to be a SOCKS server and tunnel any request over to the remote end of its connection there. So since we have launchd available, I can have that set up to maintain that SSH connection.
It'll restart it if it happens to time out. And then with the public key agent support in 10.5, I can be sure that my SSH agent is going to be up if I have a network connection. So the only thing left here is going into my system preferences and checking the little box that says use SOX, and then poof, I'm on a conference wireless network. It's secure. I'm off at a place that has some sort of weird ad injection. No problem. Except that I don't want to use it at work because there we have a gigabit network, and I don't want the overhead. And so the solution here was basically to have a little bit of code which can go in and selectively enable or disable the use of the proxy based on whether or not I'm remote. And can we switch to the demo computer?
So as you can see, currently we have the SOX proxy is disabled. There's absolutely nothing set. Now, unfortunately, the network people here don't want us reconfiguring things. So this is actually a rigged demo, but it's rigged in a way that illustrates how powerful this idea is. Because instead of having this triggered by firing off a network change event, I'm going to trigger it by mounting a disk image. And as soon as that runs, You can see that this is updated. Down here, we can see the log messages. And if we run the SCUtil, we can see that that's already updated there.
Now, the configuration for this is pretty simple. You can see up here, I'm looking for an NSWorkspace event. And it's the didMount notification, and it just points to a Python class that I wrote. There. Down here, you can see the part which would fire the same event, and called the same code if the network configuration changed. There's the entire class, the key part being right here, where it looks to see, and it's a little bit more Am I on the corporate network? And if so, go ahead and enable the proxy. So we can switch back to slides.
So there's an example of replacing kicker, going a little bit beyond, and it's in a few lines of Python there. It's also open source there. So now if you have to react to anything dynamically, I would recommend that you take a look at this. We're trying to get this expanded a little bit, try to work on some helpful utility classes. Like one of the reasons that demo was fairly simple is that I wrote a little utility class that handles the entire details for interacting with system configuration so that you can simply say, set my proxy to this and not have to worry about the exact details for going and doing this for every network interface. So if you have any comments, we'd love to hear them. There's also something else you can help out with, which is that we don't actually have a name for this yet. It started out with the very direct kicker replace, But now it's doing things that Kickr never really intended. So if you have a suggestion, we're working on the details for getting a prize at a later date. And next up is Josh Wisenbaker, who's an enterprise consulting engineer at Apple.
Thanks, Chris. Hey, everybody. How are you doing today? And we're going to take a look here at something called Home Injector. And unlike the last two speakers who got up there and talked about the death of Shell, I did this in Bash because it was quick, dirty, and easy.
So the goal we had in mind here was we wanted to allow users to use portable home directories. For those of you that don't know, just really fast here, portable home directories are very cool. We can use this to take a user that has both a local home and a network home and we synchronize the two. They're synchronized so if they make a change to the local home, it's synchronized up to the network. If they make a change to the network, it goes the other way. The newest file wins and then you have everything in one place all the time. That way when you leave the office, you have the stuff on your laptop, and when you come back to use your big eight core machine, you have everything there as well. It solves issues with applications that hate network homes. Everybody that has tried to run network homes in an organization has found at least one or two things that just cannot tolerate network homes, and this gets rid of that issue as well because you then just have a local home. It works with open directory, obviously, as in Apple technology.
But honestly, more important for most of my customers these days, it works with active Active Directory, because Active Directory is very, very prevalent in the enterprise workspace. So we need to be able to share we work with Active Directory as well. The syncing is all client side kind of function, so the client just needs to know where to go to get the data and put the data.
The key to portable home directories is they need a couple things. They have to have a mobile or an external account. These are exactly the same in Leopard. You can take a laptop with a mobile account on it and put it in target disk mode, and instantly it's an external account. The only difference between a mobile account and an external account is that the external account isn't in /users.
You need to have a network home location, obviously, otherwise there's nothing to sync to. This is going to be embedded in the user's account in the directory services. And you need to have a local home location. Again, obviously, if you don't have a local home, you have nothing to sync. Thank you.
So Network Home is required. In Open Directory, this is very easy to do. You go into Server Admin, and you set up an auto-mount in your directory service. And then when you come into Workgroup Manager, you can take a look there under the Home tab, and you will have that there available for use. Active Directory, it's a little bit different. Here you can see that we go into the AD profile for the user, and you're going to go down and tell it what drive letter you want to connect, because everyone knows you can never want more than 26 network drives connected. and then we go ahead and name it out with the UNC path. We want to make sure we use the fully qualified domain name in here, although it's not always a common practice in the AD world. When you do these two things in AD, the AD plug-in has a little checkbox in it that says go ahead and resolve the user's network home. It will take this UNC path and convert it into an SMB URL or an AFP URL, and then you can use that to connect and mount as well. There is a problem, though, right?
Otherwise I wouldn't be talking to you here, Because if it all just worked every time in every situation, there'd be no need for a script. So there's a lot of legacy habits in AD administration. And a lot of these guys are still partying like it's 1999, and they don't want to use the AD profile for any number of infuriating and arbitrary reasons. So how do they do this then?
Well, they use logon scripts. We change the vowel in ours to log in, and they use logon. But other than that, they're pretty much the same. Windows does this in a fairly archaic and brutal way. The client typically mounts a hidden share. Hidden shares in Windows are anything with a dollar sign at the end of them. Then it's going to execute a script. It's going to actually look out there for text files that are scripts that are named appropriately. The network home is going to be determined then by looking at group memberships.
So if you're a member of group foo, then your home folder is on foo.something.com. It has to go through this. You're out of the logic to go through, do all these group detections and other things like that. And then the network home is then mapped to a drive letter. Network homes in Windows typically are just a map drive that's a close location. They really don't have that whole concept of a network home like Apple does.
So the solution to this, obviously, is a script. Otherwise, I'm in the wrong room. So we need to replicate the logic of a logon script. So we're going to need to take this, and we need to figure out how to turn that VB script or.bat file or whatever the heck it is that they're running into something that a Mac can understand. We need to check for group membership. Apple has some very nice tools built specifically to do this. We also need to set the home accordingly.
So there's a couple issues with this. The first one is where do you get the script? A lot of times the guy who maintains these scripts is maintaining this giant script because he doesn't want to have to do something else or realize that if they just filled in the profile he is no longer needed. But a lot of times they're also kind of shut-ins. They're off in a cube somewhere in a basement room and they like someone to come talk to them. So just go ask. If you just go ask a lot of times, they'll just give you the script. And then you can take a look at it and start converting that over to whatever language you want. The other one is how do you set up a portable home account to work? To set up the portable home account to work, we need attributes. What attributes?
How do we construct these? Where do we put them? Things like that. Another way to get that script, which I actually forgot to mention, is that remember I said that Windows machines typically just mount a share and go get the script. Look at a Windows machine that's logging in, see what share it mounts, just go to your Mac then and connect to SMB colon whack whack, just go download the script straight to your machine as well. I've done that a couple times as well.
So the attributes that we need, we came up with, are going to be built by home-injector.bash. And home-injector.bash does a couple things. And this is going to look really similar to the logon script. It runs it log in instead of it log on. It checks for group memberships. It's going to inject the needed attributes and construct those attributes into the user's cached account. And it turns out you only need two attributes to make all this work. You need SMB home and you need original home location. If you have these two attributes, you can add them to any mobile account, whether that account has a network home defined for it in it or not, and all of a sudden, portable home syncing magically works.
So the actual script, and we're going to go through this step by step here, and then we'll do a quick demo with it actually working. So first we need to establish some variables we use. And this is the part where if you consider yourself... a bash expert, you should look away from the screen. I showed this to a friend of mine from Google earlier today and his head literally nearly separated from his shoulders as he just stared, gape mouth at this code. Mainly the group array there where I win the award for the most creative use of the strings command to generate a bash array. So we just have a couple things we need to do. We need to go ahead and figure what we're going to have and then I've got, for this example, I use kids and parents because that's always easy. You put in what the UNC path will be and what the SMB path will be. The UNC path is usually what it will find in the AD profile. The URL is what the AD plug-in would generally generate then in order to use this. So there we've got Gannon for the kids. It's our SMB server. Our link server was a Mac OS X server in this case. SMB on both platforms. We're just going to go ahead and use that.
The next thing we need to do is check for group membership. So when you're doing a login hook, login hook has a couple of special variables you can call. $1 in this case is the name of the user logging in. So this way we can just say, I'm checking for groups for this person. And we go back to my cunningly crafted array, and we go back and we use DSEditGroup to go ahead and check for the members in there. This is just a built-in function of DSEditGroup. It's been around since Tiger, and it very easily allows you turn a good answer, is this person in this group? If we do find it, then we go ahead and just say yes.
So that's the group they're found in. Obviously, if you're doing this on a much larger scale where someone could be in multiple groups, you'd have to write that logic into this. But like I said, this is a proof of concept script. So just so you can get the idea of what we're doing here. We're then going to build out these attributes, which is extremely easy. It's just substituting in some of the variables we set earlier. So if they're in kids, then set the kids URL path and the kids SMB path into these attributes here. When I do scripting, if I'm going to have an attribute or something like that I need to inject, I just name the variable whatever the actual name of what I'm going to be using is. In this case, the attribute SMB home will be created from the variable SMB home. It makes it very simple when you're writing your script to keep up with what you're doing.
And then I'm going to inject these into the local directory services and exit. So I just use Discl. I just use Discl local. And in that user, I create these two attributes. You can see again I'm using the $1 there, so I don't have to think about anything. So login hooks will take care of that. And then we just exit out of that cleanly. So I'm going to switch over to the demo machine now.
and we'll take a look at this. So I have here a Windows 2008 domain controller, and if I were to log in on it, I'll show you I have a user. This machine is bound to Active Directory, but I don't currently have any users set up. Oh, I do have that one. I'm gonna remove him real fast.
delete the home. OK, now we're ready. So if I look in here and I open up ADUC-- We can take a look at Owen's account here. And you'll notice that Owen does not have a network home set, so profile, nothing set. In Active Directory, you can have the local home or the network home. And in this case, there's just nothing to find at all, so we won't have to have it worry about anything. We can look at what he's a member of. He is a member of a group called Windows Homes.
And there he is in there. And that way we know that when we-- that's the group we're going to key off his home folders on a Windows server. So if I cancel out of that, and then I log out of this workstation, getting control of my mouse back, we can log in as Owen. So I'm going to log out, leave my domain controller up and running.
The login is Owen. And I've set the AD plugin-- and I'll show you these settings in a second-- I've set the AD plugin to just go ahead and make a mobile account and to not bother the user with it. Because inevitably someone checks the wrong box there, and then you have to go get the box back for them. So you can see his home is just a local home here. And if he was to go into the system preferences and look at the account preferences-- You'll notice that he has the mobile account here, but it's grayed out. I can't click on it, so I can't set up mobile home syncing or anything like that.
So what I want to do is I want to set my login script so that when I log into this machine, it's going to go ahead and run that script, add in the attributes that we need depending on the group memberships. Now, the best way to do a logon script is with policy. And if we can use MCX to set our login scripts. I've extended the schema in this Active Directory server. So I'm going to take a look here. and you're about to see a bug in WorkRouteManager that is known and being fixed. So I need to authenticate first.
And now that I've authenticated, I can click on preferences. And I want to set this preference at a machine level. So I'm going to pick my machine here. And here's my bug. That's a known issue and it is being fixed. It's completely cosmetic issue. It does not affect anything from actually functioning.
So if I come in here to scripts and I click on always, here you see I can have login scripts and logout scripts done with policy. Rather than push out the login script to every machine with ARD or something like that, I can just put it in the directory, which is kind of the whole point of using a directory service, and all the workstations then get their script from here. That way if I find a typo, if I need to make an edit to it or something like that, I don't have to worry about it. I just change it in Workgroup Manager and all the workstations get it automatically. So I do want to manage the login script. So I put this in a convenient location here to load it in.
Click Open. That loads that into the directory after I clear some dialogs. And now it's ready to go. If I were to look at this, it actually stores that. It encodes it. So it's a little bit scary to look at. that is now attached to this computer record. You can see the dock is over here on the side. I did that already with some policy. in the dock settings. So already there was some machine-specific MCX attached to this. Now, to get that to work, obviously a login hook, I have to log in, right? So I'm going to log out.
and log back in as Owen. And when I do that, all of a sudden, my home syncing should be functioning properly. So I'm going to log back in as Owen. To the user, there's no actual change in the behavior that they're seeing. So we get all logged back in, and now I'm going to open up System Preferences. Imagine if the button now works. So if I click on Settings here, I can actually go in and set this up. I'm going to say Automatically Sync My Home Folder, and give me my status up there so I can see what I'm doing.
and I'm going to sync the home now. This is following the local sync settings that are default on the client. You could push down more sync settings, though, using Workgroup Manager and Policy. So it's going to take a second here to do this. It is mounting a home to itself in a Windows VM running on a Mini, so this will take a second. So now the home has synchronized. And if I were to make a folder here, Demo is working. Then we log out of OWEN. It'll synchronize again here.
And then we'll log back in and we'll actually just look at the file system in Windows. And we should see that it's complete. It did work earlier in the lab downstairs, so I'm assuming it's going to work again. And you can actually do this to inject that in so that that sort of thing works. This is because I had synchronized before. The network is newer in this case.
There we go. Because the network is newer because I just created this mobile account. So as soon as this is done syncing, we'll log back in and we'll take a look. And you'll see that it actually did work. And I never had to change the AD profile for the user at all. So synchronizing is now complete. And we can take a look at that file system.
So if I go back to my VM now, take a look in the console, take a look at the computer. Oh, not that drive. Like how they sort them alphabetically now. Holmes, Owen, desktop. demo was working. So you can see it actually did synchronize the changes back up there. And if we go back and look at Owen's account still, again, just to verify that there is no Home Path set. So we can go back to the slides now.
So a couple things to keep in mind about this. I can see some gears around the room grinding in minds right now because I'm sure there's someone in this room that is stuck with these antiquated Windows logon policies determined network homes. So the real solution is not to script this at all. The real solution is to use the AD profile like Microsoft tells everyone to do.
So static logon scripts can be hard to maintain if you have a large amount of logic that are in them because if you change the name of a group somewhere and things like that. This can be made easier if you deploy your logon hook, your log in hooks with policy though. That way you don't have to actually go touch every computer. You just update it in your directory services. Sometimes.
You can go download this script now. It's on afp548.com. And that way you can take a look at it yourself. It's very simple. I wrote it in Bash. And it's very simple to understand and easy to look at. So go ahead and go grab it and take a look. And you can possibly use that or just even use it as kind of a template for working with log-in scripts on your own. So hand things back to Joel now. Thank you very much.