One of my uglier scripty bits

So – sed and awk versions on the Mac are a little behind. Turns out if I write an expression to wrap a forward slash into braces that it doesn’t exactly translate when moving from Linux to a Mac. So this happened (make fun, it’s cool):

#!/bin/bash #Extracts occurances of a file path from a text file #Then sorts and counts up each echo "Please make sure to run script from same Original_file PATH" read -p "Enter filename: " original_file cp $original_file tempfile.txt && sed 's/ /\n/g' tempfile.txt > tempfile1.txt && sed -i '.bak' '/^$/d' tempfile1.txt && cat tempfile1.txt | grep '^/' | sort |uniq -c | sort -k2nr |sort -r| awk '{printf("%s,%s\n",$1,$2)}' > filtered-sorted-file.txt && rm -rf tempfile*.txt

The script extracts all file paths from a text file and sorts/counts them. I thought it would take 5 minutes… Famous last words…

Microsoft Defender Comes To The Mac

Today Microsoft announced that Defender is coming to the Mac. This is a basic malware scanning and remediation solution but specifically built for reporting back through and configuration through Intune and Jamf.

Yet another great tool in the arsenal for combatting little nasties that show up on the network!

Use Carthage For Build Automation In Cocoa Apps

Note: these are just my notes to get started with Carthage and by no means a definitive resource of information.

Carthage is a tool that automates build dependencies for Cocoa apps and provides binary frameworks without modifying out-of-band project files. If you’re using homebrew, installing carthage is a one-liner:

Once installed, build a Cartfile in the home directory of xcode projects. The cartfile will list required projects, by version, as you can see here, which lists AlamoFire:

brew install carthage

Once Carthage is installed, you’ll need a cartfile for each project, to map the dependencies. This gives you the chance to resolve those when you’re building new software. Each time you add something new, simply put the dependency in there, taking into account when you want to use a given version or branch.

# Use the latest version github "AFNetworking/AFNetworking" # Use a local project git "file:///Users/krypted/project1" "branch1"

The above is an example that requires Alamofire (a http networking library commonly used) for the build process to complete as well as a local project file at /Users/krypted/project1 – and while this uses branch1 you can just delete that to not require a specific branch.

Next, we’ll do a quick update, which creates a Carthage directory in your .xcodeproj directory, as well as a cartfile.resolved file.

carthage update

That Carthage directory has a Build folder, which includes the artifacts required to build your project. Put all those in the appropriate platform folder.

Next, we need to tell Carthage to copy the frameworks at build time. To do that, we’ll run a script in the build phase, so click on the Build Phases settings tab and then click to add and select New Run Script Phase from the options. Simply put the following text in there:

/usr/local/bin/carthage copy-frameworks

Click on Input Files and then provide the path to the framework bundle, using “$(SRCROOT)” as the faux root of your .xcodeproj directory, as follows:

$(SRCROOT)/Carthage/Build/iOS/AFNetworking.framework

Click on Output Files and then provide the path to the framework bundle, using “$(SRCROOT)” as the faux root of your .xcodeproj directory, as follows:

$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/AFNetworking.framework

Then do a quick copy-frameworks to make them available and do the routine checking:

/usr/local/bin/carthage copy-frameworks

To then see warnings when artifacts are out-of-date, run Carthage with the outdated verb and a –xcode-warnings flag as follows:

/usr/local/bin/carthage outdated --xcode-warnings

To add this to your build process and have Xcode feed the warnings to you automatically, add this as another Build Phase.

Create Quick And Dirty PDFs from Web Pages in Python

Let’s say you want to make a script that creates a PDF of a web page. pdfkit makes that pretty easy. Simply import pdfkit and then call pdfkit.from_url, passing along the source location as your first parameter and the resultant file as your second, as follows, using http://docs.jamf.com/10.10.1/jamf-pro/release-notes/What’s_New_in_This_Release.html as our source and just calling the pdf we create Release_Notes.pdf:

import pdfkit pdfkit.from_url('http://docs.jamf.com/10.10.1/jamf-pro/release-notes/What's_New_in_This_Release.html', 'Release_Notes.pdf')

Your source location could also be from a standard html file (e.g. if you’re running from your site location) and for those you’d use pdfkit.from_file instead of pdfkit.from_url. If you don’t have pdfkit installed, you might need to pip it first:

pip install pdfkit

One last note, you can also change a few options you can pass pdfkit for job processing: page-size, margin-top, margin-left, margin-right, and margin-bottom:

import pdfkit myoptions = { 'page-size': 'A4', 'margin-top': '1in', 'margin-left': '1in', 'margin-right': '1in', 'margin-bottom': '1in', } pdfkit.from_url('http://docs.jamf.com/10.10.1/jamf-pro/release-notes/What's_New_in_This_Release.html', 'Release_Notes.pdf',options=myoptions)

When I’ve used options, things always seem to take a long time and a lot more resources to run, so I don’t any more. But that’s just me…

Pull TeamID and BundleID from KextPolicy in scripts

This type of thing is usually done interactively, but when I’m piping output that doesn’t work. So here’s a quick one-liner in bash for pulling the TeamID and BundleID from kexts out of the KextPolicy sqlite database:

sqlite3 /var/db/SystemPolicyConfiguration/KextPolicy "SELECT * from kext_policy;" ".exit"

Approve Or Deny GSuite Access For Devices

The Google Directory integration with GSuite allows you to manage which devices have access to GSuite. This allows you to control access based on a variety of factors.

Below, you’ll find a Google Cloud Function that is meant to respond to a webhook. This function takes an action to set a device into ‘approve’ or ‘deny’ as a state within Google Directory. Before using the function you’ll want to set CustomerID, ResourceID, and EMAIL_ACCOUNT for your GSuite account before using.

Once you have all that, you can upload mobiledevice.py in your Google Cloud Console.

#
# Google Cloud Function meant to respond to a webhook
# Takes an action to set a device into approve or deny state
# Set CustomerID, ResourceID, and EMAIL_ACCOUNT for your GSuite account before using
#

from google.oauth2 import service_account
import googleapiclient.discovery

SCOPES = ['https://www.googleapis.com/auth/admin.directory.device.mobile']
SERVICE_ACCOUNT_FILE = 'auth.json'
EMAIL_ACCOUNT = ''


def get_credential():
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject(EMAIL_ACCOUNT)
# admin = googleapiclient.discovery.build('admin', 'directory_v1', credentials=credentials)
admin = googleapiclient.discovery.build('admin', 'directory_v1', credentials=delegated_credentials)
return admin


def get_mobiledevice_list(admin, customerId):
results = admin.mobiledevices().list(customerId=customerId).execute()
mobiledevices = results.get('mobiledevices', [])
print('mobile devices name and resourceId')
for mobiledevice in mobiledevices:
print(u'{0} ({1})'.format(mobiledevice['name'], mobiledevice['resourceId']))
return results


def action_mobiledevice(admin, customerId, resourceId, actionName): # actionName: "approve", "block",etc body = dict(action=actionName)
results = admin.mobiledevices().action(customerId=customerId, resourceId=resourceId, body=body).execute()
return results


def main():
admin = get_credential()
customerId = ''
resourceId = ''
action = "approve"
#action = "block"

mobiledevice_list = get_mobiledevice_list(admin, customerId)
print(mobiledevice_list)

action_mobiledevice(admin, customerId, resourceId, action)
print ("Approved successfully")


if __name__ == '__main__':
main()

This is likely to evolve, given that you’ll likely want to migrate your settings into a database as part of your build process, but the general logic is here for now. Happy Googleatinging!

Manage the Look of Launchpad

You can control the number of columns and rows in LaunchPad. To do so, edit the com.apple.doc defaults domain with the key springboard-rows for the number of rows to display and springboard-columns to control the number of columns displayed. So to set the number of rows LaunchPad will show per screen, send the write verb into defaults for com.apple.dock along with the springboard-rows and an -int of 4:

defaults write com.apple.dock springboard-rows -int 4

Likewise, to set columns to 8:

defaults write com.apple.dock springboard-columns -int 8

Then just killall for Dock:

killall Dock

In some cases you will also need to send a resetlaunchpad boolean into com.apple.dock (for TRUE) along with a killall for Dock (or reboot):

defaults write com.apple.dock resetlaunchpad -bool TRUE; killall Dock