1

I have tried using AppleScript to change my startup disk for Boot Camp and Mac. Previously I have tried using terminal (do shell script) but it seems that that option requires me to enter an administrative password.

So:

  1. How do I make it such that I can retrieve the password? I can use keychains.
  2. How do I program the AppleScript/use Terminal through AppleScript to:
    • go into System Preferences/launch it if it is not already open
    • go to the Startup Disk preference pane
    • set the bootdisk to Windows/Mac and have the option to shut down/reboot. (I would like to be able to have a single script to handle everything even if I am restarting into macOS, or shutting down and will use Windows later etc.)

Thanks in advance! (P.S. I have tried searching for some answers and there is a really good answer here over at StackOverflow but it doesn't seem to work on Sierra. Also the other responses I have found on SO and Ask Different don't work.)

The two sets of code I tried (first one using System Preferences, second using Terminal, both using AppleScript):

display dialog "Select a startup disk" buttons ¬
    {"BOOTCAMP", "Macintosh HD"}
if button returned of the result = "BOOTCAMP" then
set bootVol to "BOOTCAMP" as text
else if button returned of the result = "Macintosh HD" then
    set bootVol to "Macintosh HD" as text
end if

do shell script "security 2>&1 >/dev/null find-generic-password -gl \"Insert Password Here\" | awk '{print $2}'"
set myPass to (text 2 thru -2 of result) as text

do shell script "bless -mount \"/Volumes/" & bootVol & ¬
    "\" -setBoot" user name "username" password myPass with administrator privileges

do shell script "shutdown -r now" user name "username" password myPass with administrator privileges

(Insert Password Here refers to the generic key. What I found:

  • when running the command in terminal without backslashes the command works and gives me the password assigned including the quotes like this "Password"

tell application "System Preferences"
    activate
    set the current pane to pane id "com.apple.preference.startupdisk"
    get the name of every anchor of pane id "com.apple.preference.startupdisk"
end tell

try
    tell application "System Events" to tell process "System Preferences" to click button "BOOTCAMP Windows" of radio group 1 of scroll area 1 of group 1 of splitter group 1 of window 1
end try

try
    tell application "System Events" to tell process "System Preferences" to click button "Restart..."
end try

I need to be able to unlock System Preferences, preferably using the same keychain password as I have mentioned in the first script. The second command tell application "System Events" to tell process "System Preferences" to click button "Restart..." does not work.

5
  • Can you clarify what didn't work from the answer you referenced at SO? Also, are you able to share your code - obviously removing any username/password details. It may be that what you already have only needs very minor editing in order for it to work.
    – Monomeeth
    Oct 4, 2016 at 5:50
  • @Monomeeth When inputting the code into AppleScript and running it stated that on set myPass to getPW() script doesn't understand the error message. I have two sets of code, one trying to use System Preferences, one trying to use Terminal via AppleScript.
    – Brick
    Oct 4, 2016 at 6:42
  • The part about putting Bootcamp as the volume after receiving bootcamp can be shortened to just copy the returned box but I would like to be able to customise the name if need be in the future.
    – Brick
    Oct 4, 2016 at 6:55
  • @klanomath I'll assume you meant enabling the following lines separately. Lines 9-10 when activated gives "my_real_password"; lines 11-13 when activated gives The variable BootVol is not defined but substituting it for an actual drive gives the variable myPass is not defined, to which I substituted my password, and it worked, giving "". Enabling lines 14-15 gave "shutdown -r now" after entering in my administrative password.
    – Brick
    Oct 4, 2016 at 13:56
  • Doing the bless command with administrative permissions with password var myPass returns The administrator user name or password was incorrect. Value of myPass is "Password" including quotes. Lines 9-13 were activated.
    – Brick
    Oct 4, 2016 at 14:16

2 Answers 2

1

In my environment the script works as expected. I used a different name for the generic key though: boot_key

The attributes of boot_key:

enter image description here enter image description here

The password is self-evidently also klanomath's login password. security is always allowed to use the key!


To debug the Apple script enable "Show Log" with cmd3

The script:

display dialog "Select a startup disk" buttons ¬
    {"BOOTCAMP", "Macintosh HD"}
if button returned of the result = "BOOTCAMP" then
    set bootVol to "BOOTCAMP" as text
else if button returned of the result = "Macintosh HD" then
    set bootVol to "Macintosh HD" as text
end if

do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
set myPass to (text 2 thru -2 of result) as text

do shell script "bless -mount \"/Volumes/" & bootVol & ¬
    "\" -setBoot" user name "klanomath" password myPass with administrator privileges

do shell script "echo \"shutdown -r now\"" user name "klanomath" password myPass with administrator privileges

will then reveal - after choosing "Macintosh HD" - in the "Replies" part:

tell application "Script Editor"
    display dialog "Select a startup disk" buttons {"BOOTCAMP", "Macintosh HD"}
        --> {button returned:"Macintosh HD"}
end tell
tell current application
    do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
        --> "\"Passw0rd\""
    do shell script "bless -mount \"/Volumes/Macintosh HD\" -setBoot" user name "klanomath" password "Passw0rd" with administrator privileges
        --> ""
    do shell script "echo \"shutdown -r now\"" user name "klanomath" password "Passw0rd" with administrator privileges
        --> "shutdown -r now"
end tell
Result:
"shutdown -r now"

You may log various variables explicitly by adding log $variable lines (which is redundant with the "Replies" in some cases):

display dialog "Select a startup disk" buttons ¬
    {"BOOTCAMP", "Macintosh HD"}
if button returned of the result = "BOOTCAMP" then
    set bootVol to "BOOTCAMP" as text
else if button returned of the result = "Macintosh HD" then
    set bootVol to "Macintosh HD" as text
end if
log bootVol

do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
set myPass to (text 2 thru -2 of result) as text
log myPass

do shell script "bless -mount \"/Volumes/" & bootVol & ¬
    "\" -setBoot" user name "klanomath" password myPass with administrator privileges
log bootVol
log myPass

do shell script "echo \"shutdown -r now\"" user name "klanomath" password myPass with administrator privileges
log myPass

which then reveals:

tell application "Script Editor"
    display dialog "Select a startup disk" buttons {"BOOTCAMP", "Macintosh HD"}
        --> {button returned:"Macintosh HD"}
end tell
(*Macintosh HD*)
tell current application
    do shell script "security 2>&1 >/dev/null find-generic-password -gl \"boot_key\" | awk '{print $2}'"
        --> "\"Passw0rd\""
    (*Passw0rd*)
    do shell script "bless -mount \"/Volumes/Macintosh HD\" -setBoot" user name "klanomath" password "Passw0rd" with administrator privileges
        --> ""
    (*Macintosh HD*)
    (*Passw0rd*)
    do shell script "echo \"shutdown -r now\"" user name "klanomath" password "Passw0rd" with administrator privileges
        --> "shutdown -r now"
    (*Passw0rd*)
end tell

or as a screenshot:

enter image description here

Now you should be able to detect the error in your script or your environment.

1
  • Thanks @klanomath for this answer but is it possible to have a dialogue that will prompt for shutdown or reboot right after the bless command, replacing the makeshift shutdown -r now
    – Brick
    Oct 5, 2016 at 5:36
0

Working solution

The SIP was the first problem I've encountered on Big Sur. Turned it off looks like a bad idea. The second problem was target volumes list items doesn't have actions. Which make impossible to click on them via click or "click at" functions perhaps because of some new additional protections on Big Sur. Click with AST and other scripts also doesn't works due to new MacOS restrictions. The only way I found is using python click(but this leads to a slight delay while script selects target volume).

So here is a fully automated switching:

property targetVolume : "BOOTCAMP" # find name of required volume inside System Preference > Startup Disk
property passwordValue : "yourSystemPassword" # Can be empty

tell application "System Events"
    tell application "System Preferences"
        set current pane to pane id "com.apple.preference.startupdisk"
        activate
    end tell
    tell application process "System Preferences"
        tell window "Startup Disk"
            set volumePosition to {0, 0}
            set lockFound to false
            
            # Check if auth required
            set authButtonText to "Click the lock to make changes."
            if exists button authButtonText then
                click button authButtonText
                
                # Wait for auth modal
                set unlockButtonText to "Unlock"
                repeat
                    if (exists sheet 1) and (exists button unlockButtonText of sheet 1) then exit repeat
                end repeat
                
                # Autofill password if setted
                if passwordValue is not equal to "" then
                    set value of text field 1 of sheet 1 to passwordValue
                    click button unlockButtonText of sheet 1
                end if
                
                # Wait for auth success
                repeat
                    if exists button "Click the lock to prevent further changes." then exit repeat
                end repeat
            end if
            
            # Wait until loading volumes list
            repeat
                if exists group 1 of list 1 of scroll area 1 then exit repeat
            end repeat
            
            # Click on target volume (posible a slight delay because of shell script executing)
            repeat with m in (UI element of list 1 of scroll area 1)
                if (value of first static text of m = targetVolume) then
                    tell static text targetVolume of m
                        set volumePosition to position
                    end tell
                end if
            end repeat
            set volumePositionX to item 1 of volumePosition
            set volumePositionY to item 2 of volumePosition
            my customClick(volumePositionX, volumePositionY)
            
            click button "Restart…"
            
            # Wait for restart modal appears
            repeat
                if (exists sheet 1) and (exists value of first static text of sheet 1) then exit repeat
            end repeat
            
            click button "Restart" of sheet 1
        end tell
    end tell
end tell

# shell script to make click work on target volume
on customClick(x, y)
    do shell script " 

/usr/bin/python <<END

import sys

import time

from Quartz.CoreGraphics import * 

def mouseEvent(type, posx, posy):

          theEvent = CGEventCreateMouseEvent(None, type, (posx,posy), kCGMouseButtonLeft)

          CGEventPost(kCGHIDEventTap, theEvent)

def mousemove(posx,posy):

          mouseEvent(kCGEventMouseMoved, posx,posy);

def mouseclick(posx,posy):

          mouseEvent(kCGEventLeftMouseDown, posx,posy);

          mouseEvent(kCGEventLeftMouseUp, posx,posy);

ourEvent = CGEventCreate(None); 

currentpos=CGEventGetLocation(ourEvent);             # Save current mouse position

mouseclick(" & x & "," & y & ");

mousemove(int(currentpos.x),int(currentpos.y));      # Restore mouse position

END"
end customClick

on simpleEncryption(_str)
    set x to id of _str
    repeat with c in x
        set contents of c to c + 100
    end repeat
    return string id x
end simpleEncryption

on simpleDecryption(_str)
    set x to id of _str
    repeat with c in x
        set contents of c to c - 100
    end repeat
    return string id x
end simpleDecryption

You just need to change two properties targetVolume and passwordValue. Password can be empty and in that case you can provide it manually. Then just copy this script, paste it to the Script Editor and export via File -> Export -> file format - Application, select Run-only -> Save. You can do the same process for all systems you have, for example Big Sur 1, Big Sur 2, Bootcamp.

2
  • Hi @ratojakuf thanks for your answer. Is there a way to store the password more securely? The reason why my original question asked for a method to retrieve a keychain password was because I think it's quite insecure to store a password in a plaintext AppleScript file – all it takes is a rogue programme to read the AppleScript file, or a stranger to physically access your computer while unlocked to discover your system password. Or perhaps I have misunderstood your answer; please clarify with me then. Thanks again :)
    – Brick
    Sep 4, 2022 at 11:18
  • 1
    @brick, Hi, you can add encrypted password like in this answer stackoverflow.com/a/14616010/4008381 and then just save apple script as Application
    – ratojakuf
    Sep 5, 2022 at 13:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .