Category Archives: Information Technology

Importing Data to FileMaker 16 via APIs

I’m new to APIs. Have quite a bit of FileMaker experience. I’ve decided to see if I can build a system to track parent-teacher contact. This is a system that once existed for the school in an ASP system that I mocked up and outsourced. It was replaced with Edsby, an LMS system that wasn’t truly designed for this purpose. We were smashing a square peg into a round hole.

My proof of concept for this system involves seeing if I can build something that does three things I’ve never done in FileMaker before.

  1. OAuth login using Google authentication
  2. Sending nightly emails of any records that have changed for those affected
  3. Import data from Managebac’s API

The first step was surprisingly easy. Thanks to a blog post that had the entire process outlined on a simple PDF. Thoroughly detailed post from William Porter of Rucksack Texnology.

Step two is actually step three, but ending the blog post with “I haven’t done this yet” would not be very exciting.

For step three, I got off to a bad start. I did some research and didn’t realize that JSON support was added to FileMaker 16 and was looking at a third-party plug in. That plug in was super-confusing, so I stepped back. At that point I decided to upgrade from FileMaker 15 to 16, as I knew I needed that for the OAuth login.

Now that I had FileMaker 16, I decided to rewatch a Lynda.com video that I had watched months ago, and see if there was anything I missed there. There was, there was a whole section dedicated to JSON. Ready to conquer this task with the built-in functionality of FileMaker 16, I began.

I decided to start with the smallest table, teachers. Obviously there are fewer teachers than students, and fewer students than parents.

Using Terminal.app I was able to get what I needed using this command.

curl --request GET --url https://api.managebac.com/v2/teachers --header 'auth-token: <<AUTHTOKENVALUE>>'

Obviously, I removed the actual authtokenvalue, because I’m not a dum dum.

Open FileMaker, open my working database, create a field in the TeacherContacts table for the temp data dump.

I chose “Insert From URL”
Verify SSL Certificate was selected
Select Entire Contents was selected
URL was https://api.managebac.com/v2/teachers
cURL options was “–request GET –H \’auth-token: <<AUTHTOKENVALUE>>\'”

Everything matched my terminal command, the only problem was I was getting an error saying that “Authorization Failed.” Eventually I realized that my problem was using single quotes around auth-token. I replaced those with a double-quote and it worked perfectly.

cURL options is now “–request GET –H \”auth-token: <<AUTHTOKENVALUE>>\””

Once that was working, I was then able to get rid of the data other than the “teachers” table. Using Insert Calculated Results into the field $jsonTeachers, I used the calculation JSONGetElement ( $json ; "teachers" )

The data needed to be cleaned up a bit, using a new Insert Calculated Results this time it was enter into $jsonTeachersFormatted.JSONFormatElements ( $jsonTeachers )

From there, I needed to create records, and to do that, I needed to know when to stop.

This time I Inserted Calculated Results into the $jsonCount variable. The value was the number of records that was exported from Managebac. ValueCount ( JSONListKeys ( $jsonTeachersFormatted ; "" ) )

The JSON array starts count at 0, so $jsonCount is one more than I need. I began a loop and immediately Set Variable [ $jsonCount ; Value: $jsonCount -1 ]. This would trigger every time the loop began.

I then created a New Record/Request and Set Field [StaffContacts:id ; JSONGetElement ( $jsonTeachersFormatted ; "[" & $jsonCount & "]id" ) . I duplicated that for every field I wanted to import and modified id to the new field name. After all that I had an Exit Loop If [ $jsonCount = 0 ] and closed the loop.

Now I had a script that would pull all the teachers from ManageBac, create a new record for each and bring in the data to FileMaker. Now I just need to get it to update a record if it already exists, rather than creating a new record. We’re getting there.

I spent a long time trying to figure this out and it’s a lot easier than I thought. In the loop, after the decrease of the $jsonCount, I went to find mode, Enter Find Mode [ Pause: Off ] and Set Field [ StaffContacts::id ; JSONGetElement ( $jsonTeachersFormatted ; "[" & $jsonCount & "]id" ) . Needed to Set Error Capture [ On ] and ran the find Perform Find [ ] .

At this, I need to enter the data, either on a new record, or into the existing found record. So:

If [ Get (FoundCount) = 0 ]
     New Record/Request
End If

And I’m done. I win! I just have to do this with all the other tables of data, relate them all and build the actual parent contact part.

Renewing Let’s Encrypt on a macOS machine running 10.12.6 and Server.app

Last time on Never Had To Fight…

Adam installed a certificate using Let’s Encrypt for a macOS server running 10.12.6 and Server.app. It wasn’t exciting, but it worked. Now, three months later, we need to renew.

It was actually really easy.

Renew the certificates

sudo certbot renew

Transfer the .pem files to desktop

Using the cp command enter these to copy your files.

Replace <<FQDN>> with your FQDN. Replace <<USER>> with your username.

sudo cp /etc/letsencrypt/live/<<FQDN>>/privkey.pem /Users/<<USER>>/Desktop/privkey.pem
sudo cp /etc/letsencrypt/live/<<FQDN>>/fullchain.pem /Users/<<USER>>/Desktop/fullchain.pem

Hooray, now these files are on your desktop.

Install Certs

Open Server.app

Go to Certificates.

Click on the +

Choose Import Certificate Identity…

Drag and drop the two .pem files and BAM

DONE!

via GIPHY

Let’s Encrypt on a macOS machine running 10.12.6 and Server.app

Are you doing your web hosting with Server.app? You’re probably not the biggest fan of it, but it works, so… let’s keep on keeping on. Do you want to have a free SSL certificate from Let’s Encrypt? Well, I found some really bad guides, so this is much better.

*Hat tip to MacAdmins slack for a few key points

Open up your Terminal.app (Go to the Go menu, choose Utilities, double-click on Terminal). This isn’t even a step, you should know this.

Step One – Install Homebrew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

At this point, you will be prompted to press RETURN to continue. Press the return key.

It will then start downloading and installing Homebrew.

Install XCode Select

Turns out you need XCode Select installed, too. So I ran this code.

xcode-select --install

That popped up a dialogue box, I said Install.

This install took a few minutes, and then once it was done, I was ready to install certbot.

Install certbot

brew install certbot

That easy? Aye!

Get a Certificate!

We’re almost there. Now to get a certificate from Let’s Encrypt.

sudo certbot certonly

It will prompt you to know what type of server you have. Choose 3: Place files in webroot directory.

It will prompt you to provide the fully qualified domain name (FQDN) for the server. Such as neverhadtofight.com

It will then create some files in a subdirectory called .well-known to confirm you have ownership of this website. Once that’s done it will save the .pem files for you.

Transfer the .pem files to desktop

Using the cp command enter these to copy your files.

Replace <<FQDN>> with your FQDN. Replace <<USER>> with your username.

sudo cp /etc/letsencrypt/live/<<FQDN>>/privkey.pem /Users/<<USER>>/Desktop/privkey.pem
sudo cp /etc/letsencrypt/live/<<FQDN>>/fullchain.pem /Users/<<USER>>/Desktop/fullchain.pem

Hooray, now these files are on your desktop.

Install Certs

Open Server.app

Go to Certificates.

Click on the +

Choose Import Certificate Identity…

Drag and drop the two .pem files and BAM

DONE!

via GIPHY

macOS Sierra Beta

Apple has been seeding developer previews of macOS Sierra (10.12) since the Worldwide Developer Conference in June. I installed developer preview 6 and have been using that and version 7 since their release. Fortunately, everything seems to be running quite well.

One of the features of 10.12 is Siri on your Mac. The one thing I use Siri for the most is the clock functions. Timers and alarms. I tried setting a timer on my Mac using Siri, and there’s no functionality for that in 10.12.

Perhaps Apple will add this functionality. In the mean time we are seeing SiriKit being made available to iOS developers in iOS 10, so perhaps we’ll see something similar in macOS 10.13.

Siri on macOS

Standing Desk

At my work, I have a motorized standing desk. I was looking for apps that would remind me to stand and sit at regular intervals. In the long run, I want an app that conditions me, so it starts at standing for an hour a day for a week, then two hours a day for a week, and so on and so forth. I can’t find that app. Instead I made a quick AppleScript that will prompt me to change position every 45 minutes.


set answer to ""

repeat while answer ≠ "Quit"
set answer to the button returned of (display dialog "Please rise." buttons {"Quit", "Okay"} default button 2)
log answer

if answer = "Okay" then
delay 2700

else if answer is equal to "Quit" then
tell me to quit
end if

set answer to the button returned of (display dialog "Please be seated." buttons {"Quit", "Okay"} default button 2)

if answer is equal to "Okay" then
delay 2700
else if answer is equal to "Quit" then
tell me to quit
end if
end repeat

It’s been a long time since I’ve done any AppleScripting. I forgot how human it is. I expected not equal to to be !=, when it’s just the not equal to sign (≠). I expected else if to be elseif.

I’ve thrown it up on my GitHub repo, you can find that here. A compiled version can be found here.

NetGear ReadyNAS Time Machine Backup

It seems you cannot mount your Time Machine backup on a ReadyNAS device using normal credentials. Time Machine is segmented off with a special user. Which means I needed to restore my computer using Migration Assistant. It took FOREVER over wifi. However, I seem to be missing my Aperture Libraries. What’s the solution? Assuming it was backed up, I need to find the sparsebundle. It took a lot of searching, but I found it, and will share my brilliance with you in case you ever need to find it, too.

/data/.timemachine

To access: SSH in as root. Then copy the files to a SMB or AFP accessible directory.

cd /data/.timemachine
cp -R * /home/<yourusername>

New Job, New Server

Adam

If you weren’t aware, when the month changed from June to July, I also changed jobs. I graduated from elementary school to high school. Today was the first day at my new job where I really had time to myself to do what I please. It was time to play with servers.

The school already had a Hyper-V setup, so I installed a copy of Ubuntu and hit the ground running. Once I had the IP setup and SSH enabled, I was ready to go. First thing to install was Docker.

$ wget -qO- https://get.docker.com/ | sh

With that simple command I had Docker running on the server.

For those unaware, Docker is a container system for servers. It allows you to compartmentalize services on a server without the overhead of extra operating systems, like virtualization does. In other words, when you virtualize, you could have 10 virtual servers on one physical machine, all running full copies of Windows. That’s 10 copies of Windows. That’s a lot of overhead. Docker let’s you run on one single OS, sharing resources, but compartmentalizing services.

Once I had my server setup, I had to create a Munki repository. Munki is a program that allows you to easily distribute applications.

I started by creating a data storage container to hold my Munki files. I used this to guide me, https://registry.hub.docker.com/u/macadmins/munki/

$ docker run --name munki-data -v /mnt/docker_data/munki_repo:/munki_repo busybox

Boom, I had a place to store my files, but I needed to get at the files. So I set up an SMB share. This time it takes three lines of code. I’m not inventing anything here, taking generously from here https://registry.hub.docker.com/u/nmcspadden/smb-munki/

$ docker run -d -p 445:445 --volumes-from munki-data --name smb nmcspadden/smb-munki /munki_repo

$ docker exec smb chown -R nobody:nogroup /munki_repo/

$ docker exec smb chmod -R ugo+rwx /munki_repo/

Now I can access my Munki repo through the Finder on my Mac. Now to populate the repo. To do that, I opened up AutoPKGr, pointed it to the new Munki server, and starting running some .munki recipes. There were some new programs I hadn’t used before that I needed to include. Among them were GameSalad and Sonic Pi. There weren’t AutoPKG recipes for them, so I dove in, and now they’re available to the whole community. There’s still a couple titles I need to create recipes for, but I’ll get to that tomorrow.

Next was activating the web server. Munki is just files on a web server. Using Docker to create an Nginx instance shouldn’t be hard, and it’s already been done for Munki. So all I had to was type in:

$ docker run --name munki --rm -p 80:80 --volumes-from munki-data macadmins/munki

Easy peasy, right?

Wrong.

$ sudo defaults write /Library/Preferences/ManagedInstalls SoftwareRepoURL "http://FQDN/munki_repo"

I, of course, replaced FQDN with the fully qualified domain name. It wasn’t working. Running managedsoftwareupdate on the computer was returning a 404 error. It wasn’t hitting the server properly. What did I do wrong?

After a bit of help from the author of the docker file, I discovered that it’s pointed to http://FQDN/repo, not /munki_repo. D’OH! I could have gone here and seen on the original file that repo is pointing to munki_repo.

But it’s up and running. I could now use Munki to have to client upgraded to OS X 10.10.4, so I can test Yosemite (or Yo, Semite!) in this environment. And that worked like a charm.

That was all I was supposed to do that day, but it was still early. Why not tackle one more job? Let’s set up MunkiReport-PHP!

MR-PHP is a program which lets the client computers report in and give the admin useful data about the state of the fleet. Fantastic! It’s also been Dockerized, so it should be easy. I found it on DockerHub, and I was ready to go…

As you can see from above, my Munki repo is sitting at /mnt/docker_data/munki_repo, so it made sense to put the config file for MR-PHP at /mnt/docker_data/munkireport.

$ sudo mkdir /mnt/docker_data/munkireport

$ cd /mnt/docker_data/munkireport

I needed the config file there.

$ sudo curl -O https://raw.githubusercontent.com/munkireport/munkireport-php/master/config_default.php

$ sudo cp config_default.php config.php

That downloaded the file and copied it, so I had a factory default if needed. I then ran the docker container.

$ docker run -d -v /data/munkireport -v /mnt/docker_data/munkireport/config.php:/app/config.php -p 80:80 macadmins/munkireport-php

Except the ports of 80:80 won’t work! EEK! 80 is in use by Munki. So I ran…

$ docker run -d -v /data/munkireport -v /mnt/docker_data/munkireport/config.php:/app/config.php -p 5000:80 macadmins/munkireport-php

So now I could go to http://FQDN:5000 and generate a password, which I would then throw into the config.php file, along with any other changes I might need to make. Hoorah!

And that’s it, easy peasy lemon squeezy.

Tomorrow I test Yo, Semite!

Yosemite Sam 10.10.3

OS X 10.10 Yosemite

In September, Apple released OS X 10.10 (Yosemite). September is not a good time to release a new OS from the point of view of a K-8 IT Manager. We need a few months before the school year starts to do testing, and that was not able to happen.

In previous years I had waited until the following summer to upgrade. This year with the implementation of Munki at the school, I wanted to roll out 10.10 to staff and students as an optional install after 10.10.3 or 10.10.4 was released. During the Passover break, Apple released 10.10.3, and that release led to a major realization.

Apple had patched a security vulnerability in 10.10, which is also present in 10.9, 10.8, and 10.7. This vulnerability gives a user access to root privileges, allowing one to install software. I can’t think of a better reason to roll out Yosemite.

Upon return from break, I used createOSXInstallPkg to create an OS X installation package. In other words, it makes an installer that one can distribute through normal distribution means; including Munki.

I ran my first test and I was getting an error saying the drive must be an HFS+ drive to install Yosemite. Turns out all it really means is that I have to enable journaling. It’s a simple terminal command to allow one to do that.

/usr/sbin/diskutil enableJournal /

That was easy. Now to do this for the entire school fleet. That’s also easy. I created a nopkg installer through Munki and was left with this file (hosted on GitHub). Once that was in Munki, I watched it go out without a hitch.1

Now that I could install 10.10.3, I did, but umm, why is it taking me through the setup assistant?

I booted into Deploy Studio and told it to skip the setup assistant. On reboot, the computer looked normal, but there was no local admin user (LBDS). Uh oh.

With an email to MacEnterprise email group, I was reminded of a discussion from months ago that Apple now owns users with a userID below 500, back then I wasn’t worried, our local admin user was 501. Turns out I was wrong. Our userID was 499.

To be able to roll Yosemite out to all users, I’d have to change the admin user. Do I make a new one and roll out that package through Munki using CreateUserPkg? Allowing Yosemite to erase the old local admin user? That could work, but what if it doesn’t erase the old user? I could delete the user using dscl, or I could just use dscl to change the userID. What about all the permissions? A quick Google search led me to here.

That would be easy to implement with a nopkg installation through Munki. And I did.

Now just to make those two a prerequisite for 10.10 installation and we’re Yosemite-bound.

  1. We had a weird problem where the actual script wasn’t running, so we put it in the install check, it worked fine that way. []

AutoPKGr

I’ve been using Munki at work for some time. Munki is a system for central management of package installation for OS X computers. It allows end-users to be forced installs from IT, and allows a catalogue of IT-approved installs that end-users can install themselves. It’s really handy.

However, to manually add packages all the time, with constant updates from Google, Mozilla, Adobe, Apple, Evernote, and more and more, all my time would be spent searching for updates. Instead I use a command-line tool called AutoPKG which looks for updates from any program you specify (assuming a recipe has been created), and AutoPKG will download it and install it into your Munki repository.

With the quick command “autopkg run -v Firefox.munki Thunderbird.munki MakeCatalogs.munki”, autopkg will run the Firefox and Thunderbird recipes for Munki and then tell Munki to remake its catalogues.

AutoPKGr is a graphical interface for AutoPKG to make management easier. Instead of having to touch the terminal, I just click off the recipes I want to run and schedule it to run every _ hours. I then give it details to be able to send emails, and I get email notifications of updates.

Screen Shot 2015-02-04 at 11.36.20

I even made a recipe to auto update a package that was missing in the repository. Apparently not enough people use Kobos outside of Canada, and as such, no Kobo recipe was created. I made one!

Recipes have many different functions. For work I mostly just use the .munki one, as it downloads, packages, and imports into Munki. There’s also a recipe format to import into one’s JAMF CasperSuite. We don’t use Casper, as it’s super-expensive, but a fantastic suite. At the core of each recipe is a .download recipe, which just downloads the file. There’s a .pkg recipe which calls the download recipe then packages it. .munki and .jss recipes would just follow the same theme, grabbing from the previous information.

A short while ago, AutoPKG added an .install type. This is what I’m really writing about.

At home I have a computer which I use for classic gaming emulation, and occasional video streaming. If I’m watching CBC’s election coverage on my TV, sadly I can’t get that without a computer. I had an old Mac Mini, so I plugged it into the TV video HDMI, put some classic games on it, and use it to stream video when needed.

During the last provincial election, I found that my Flash was out of date, as my Safari, and Chrome, and Firefox. I needed to update all this software.

Why don’t I automate it?

I installed AutoPKGr. Opened it up, told it run every 24 hours, put in email details so it could report to me. Added the .install recipes for Adobe Flash Player, Firefox, Chrome, Silverlight, and VLC.

Now, once a day, my computer looks to see if any new software is available, and if so, it installs it and emails me to notify me.

I will never again need to anything and have out of date software on this computer.

Apple Canadian Settings through MCX

Background

I was tired of looking at my end-user’s screens and calling up “Managed Software Center” rather than “Managed Software Centre.” I figured I would enforce both the system-wide language setting, as well as enforce keyboard layouts to include both Canadian English and Hebrew.

Those are kept in two files.

com.apple.HIToolbox.plist holds the Keyboard settings
.GlobalPreferences.plist holds the language settings

The leading . means that it’s an invisible file, but you can use the terminal to copy it to another location:

cp ~/Library/Preferences/.GlobalPreferences.plist /PATH/TO/GlobalPreferences.plist

If you notice that in the second path, I removed the leading . to make it visible.

.Plist setup

The keyboard settings file just need to be setup on a test machine, and then copied, and it will work as is.

When I set it up with the Canadian English keyboard and Hebrew keyboard, it looks like this…

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
<plist version=”1.0″>
<dict>
<key>AppleCurrentKeyboardLayoutInputSourceID</key>
<string>com.apple.keylayout.Canadian</string>
<key>AppleDateResID</key>
<dict>
<key>smRoman</key>
<integer>0</integer>
</dict>
<key>AppleEnabledInputSources</key>
<array>
<dict>
<key>InputSourceKind</key>
<string>Keyboard Layout</string>
<key>KeyboardLayout ID</key>
<integer>29</integer>
<key>KeyboardLayout Name</key>
<string>Canadian</string>
</dict>
<dict>
<key>InputSourceKind</key>
<string>Keyboard Layout</string>
<key>KeyboardLayout ID</key>
<integer>-18432</integer>
<key>KeyboardLayout Name</key>
<string>Hebrew</string>
</dict>
</array>
<key>AppleInputSourceHistory</key>
<array>
<dict>
<key>InputSourceKind</key>
<string>Keyboard Layout</string>
<key>KeyboardLayout ID</key>
<integer>29</integer>
<key>KeyboardLayout Name</key>
<string>Canadian</string>
</dict>
</array>
<key>AppleNumberResID</key>
<dict>
<key>smRoman</key>
<integer>0</integer>
</dict>
<key>AppleSelectedInputSources</key>
<array>
<dict>
<key>InputSourceKind</key>
<string>Keyboard Layout</string>
<key>KeyboardLayout ID</key>
<integer>29</integer>
<key>KeyboardLayout Name</key>
<string>Canadian</string>
</dict>
</array>
<key>AppleTimeResID</key>
<dict>
<key>smRoman</key>
<integer>0</integer>
</dict>
</dict>
</plist>

The GlobalPreferences.plist had a lot of superfluous settings in it that could be eliminated. So I slimmed it down to as follows. As you can see, AppleLanguages is an array with many entries and it starts with “en-CA”, or Canadian English, then American English, Hebrew, and then French. The rest is superfluous.

<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
<plist version=”1.0″>
<dict>
<key>AppleLanguages</key>
<array>
<string>en-CA</string>
<string>en</string>
<string>he</string>
<string>fr</string>
<string>de</string>
<string>zh-Hans</string>
<string>zh-Hant</string>
<string>ja</string>
<string>es</string>
<string>it</string>
<string>nl</string>
<string>ko</string>
<string>pt</string>
<string>pt-PT</string>
<string>da</string>
<string>fi</string>
<string>nb</string>
<string>sv</string>
<string>ru</string>
<string>pl</string>
<string>tr</string>
<string>ar</string>
<string>th</string>
<string>cs</string>
<string>hu</string>
<string>ca</string>
<string>hr</string>
<string>el</string>
<string>ro</string>
<string>sk</string>
<string>uk</string>
<string>id</string>
<string>ms</string>
<string>vi</string>
</array>
</dict>
</plist>

You’ll then need to rename the file to include the leading . using the cp tool in the terminal.

Convert to PKG and Deployment

For deployment, I use a wonderful open source program called Munki. You can use anything that will deploy profiles. Munki doesn’t, but it deploys pkg files.

To make this MCX file I need two programs developed by Tim Sutton, mcxToProfile and make-profile-pkg.

I’ve got those two setup on my Munki server

./PATH/TO/mcxToProfile.py –plist /PATH/TO/com.apple.HIToolbox.plist –plist/PATH/TO/.GlobalPreferences.plist -i Canada\ Settings -g Organization -o /PATH/TOCanadaSettings.mobileconfig –displayname ‘Canadian Settings’ -m Once

What this is doing is calling to the python script mcxToProfile, telling it to pick up the two plists com.HIToolbox.plist and .GlobalPrefernces.plist, telling it to identify as “Canada Settings” with the organization name “Organization.” Then it uses -o to know where to spit the mobileconfig file to, including a display name and how to be managed. I want my end users to be able to customize it after first use, so we use the Once flag.

This output my .mobileconfig file. So I could quickly double-click on it and it works! However, that’s not going to help me deploy it to 200+ computers. So I need to get it into Munki, first it needs to be a PKG.

./PATH/TO/make_profile_pkg.py -m /PATH/TO/CanadaSettings.mobileconfig

This python script is pretty straightforward. You call it, tell it that you want it to dump into your Munki repo (-m) and then tell it the path to your mobileconfig file. A few seconds later, it’s in your repo and a duplicate PKG is in the directory that your mobileconfig is sitting at.

Now all you need to do is throw it into the appropriate testing manifest, make sure it works, and then slowly roll it out to your fleet.