Can you package a configuration profile?

duffcalifornia
Contributor

So, odd use case I know, but here's my dilemma:

Our privileged wifi is allowed via certificates that are installed via configuration profile, and they authenticate based on the machine being bound to AD. Sometimes, our users take their laptops offsite for periods of time, during which their AD password changes. The next time the machine touches our network, it breaks the privileged wifi and puts the laptop on our general network.

The solution has been to delete the wifi profile, delete the keychain entry that our general wifi installs in the login keychain, and then reinstall the wifi profile.

I'd love to come up with a solution that a user can do from Self Service, but the issue becomes: how do i get the wifi certificate back on the machine if it doesn't have internet?

Would it be possible to use Composer to create a package that "installed" the wifi profile, cache it, and then create the Self Service profile that uninstalls the current profile, deletes the single keychain entry, and then install the cached "package" which will reinstall the profile?

1 ACCEPTED SOLUTION

mm2270
Legendary Contributor III

We have a mix of Config Profiles that are deployed directly from Jamf Pro over Apple's MDM framework, and some that are deployed and installed locally with a package or script, bypassing APNs.
What I found, like others posted here, is that there are some reliability issues with Profiles randomly getting removed or vanishing from machines and then being redeployed again when done over APNs. Where the issue comes in is if using an important profile, like one that adds an 802.1x Wi-FI profile, or, in our case, that adds an important Intermediate SSL Decryption Authority certificate, this creates big problems for us. We can't have those profiles disappearing from our Macs for no apparent reason or it creates problems for the end users, so for the most important ones, we deploy them with a script or a package/script combo. They tend to stick around much more reliably when done this way.

I wanted to just add though, that I found its pretty easy to deploy the Profile entirely in a script and bypass a package altogether. Here are the steps you can take to do this.

  1. Create the Configuration Profile .mobileconfig however you want, for example, in Jamf Pro, or in Apple's Profile Manager. As long as you can get a physical .mobileconfig file in the end, or access the direct xml of the profile.
  2. If you created it in Jamf Pro, access it in the GUI and use the Download button in the Profile details.
  3. Use the following command on the downloaded file. This is necessary with any downloaded from Jamf Pro, since they end up as signed Config Profiles:
    • security cms -D -i /path/to/profilename.mobileconfig | xmllint --format -
  4. Take the output from the above command in Terminal and copy it. You will paste this into a script.
  5. Create a script with the following information in it. You will need to edit some of this to correspond to whatever it is that you're deploying, like a name for the profile for example.
#!/bin/bash

## Create the .mobileconfig file in /private/tmp/
cat << EOF > /private/tmp/profile.mobileconfig
*<paste the entire xml code for the configuration profile from step 3 and 4 here, unaltered>*
EOF

## Install the .mobileconfig with the profiles command
/usr/bin/profiles -I -F /private/tmp/profile.mobileconfig

if [ $? == 0 ]; then
    echo "Successfully installed. Deleting local file..."
    rm -f /private/tmp/profile.mobileconfig
    exit 0
else
    echo "Installation of profile failed. Deleting local file..."
    rm -f /private/tmp/profile.mobileconfig
    exit 1
fi

In this way, the profile can be contained in the script and deployed and installed without needing to create a separate package for it, which can sometimes work better. I've been using the above process for deploying some of our profiles and its working well.

Be sure also to create an Extension Attribute that captures the installed profile names or identifiers, so you can build Smart Groups for machines that should get it or not. This may be necessary even if just pushing the profile in a regular package install.

View solution in original post

29 REPLIES 29

CasperSally
Valued Contributor II

Not exactly what you're asking, but you can package a .mobileconfig file (create file, then store it in a temp place like /private/var/profiles and create a package with just the .mobileconfig file in it).

Then use apple's profile command to install it (/usr/bin/profiles -I -F /private/var/profiles/wifi.mobileconfig). Then run a script to trash the /private/var/profiles/wifi.mobileconfig file.

That is how we install our wifi config profile at image time. I didn't want the JSS involved with pushing it because we have such weird random profile issues when pushing through jss.

We use 802.1x - but ours is machine auth so we don't have issues when users change password.

Edit: You might not want to create the .mobileconfig file in the JSS (like I did initially). if you do, you'll get a lot of errors of JSS trying to remove the profile and failing to be able to since the JSS doesn't install it. It was just more clutter in my db.

GabeShack
Valued Contributor III

Similar to what @CasperSally does,
We package up our wireless profile (including the cert for the network), install it into the Jamf folder inside a Wireless Profiles folder (owned by root) and then run the command to install it for all users.
We also don't like having the JSS control the Wifi profile since we have had problems in the past with it randomly removing it. So keeping it local is great. We also have posted this policy in self service so that if a tech assistant sees a problem with wifi, they can just plug into ethernet and run the add wireless policy, which makes it so convenient.

profiles -I -F /Library/Application Support/JAMF/Wireless Profiles/YourProfileHere.mobileconfig

I have this set as ongoing, and to run during enrollment complete for each computer. I also have various profiles for the different buildings/vlans
Gabe Shackney
Princeton Public Schools

Gabe Shackney
Princeton Public Schools

mm2270
Legendary Contributor III

We have a mix of Config Profiles that are deployed directly from Jamf Pro over Apple's MDM framework, and some that are deployed and installed locally with a package or script, bypassing APNs.
What I found, like others posted here, is that there are some reliability issues with Profiles randomly getting removed or vanishing from machines and then being redeployed again when done over APNs. Where the issue comes in is if using an important profile, like one that adds an 802.1x Wi-FI profile, or, in our case, that adds an important Intermediate SSL Decryption Authority certificate, this creates big problems for us. We can't have those profiles disappearing from our Macs for no apparent reason or it creates problems for the end users, so for the most important ones, we deploy them with a script or a package/script combo. They tend to stick around much more reliably when done this way.

I wanted to just add though, that I found its pretty easy to deploy the Profile entirely in a script and bypass a package altogether. Here are the steps you can take to do this.

  1. Create the Configuration Profile .mobileconfig however you want, for example, in Jamf Pro, or in Apple's Profile Manager. As long as you can get a physical .mobileconfig file in the end, or access the direct xml of the profile.
  2. If you created it in Jamf Pro, access it in the GUI and use the Download button in the Profile details.
  3. Use the following command on the downloaded file. This is necessary with any downloaded from Jamf Pro, since they end up as signed Config Profiles:
    • security cms -D -i /path/to/profilename.mobileconfig | xmllint --format -
  4. Take the output from the above command in Terminal and copy it. You will paste this into a script.
  5. Create a script with the following information in it. You will need to edit some of this to correspond to whatever it is that you're deploying, like a name for the profile for example.
#!/bin/bash

## Create the .mobileconfig file in /private/tmp/
cat << EOF > /private/tmp/profile.mobileconfig
*<paste the entire xml code for the configuration profile from step 3 and 4 here, unaltered>*
EOF

## Install the .mobileconfig with the profiles command
/usr/bin/profiles -I -F /private/tmp/profile.mobileconfig

if [ $? == 0 ]; then
    echo "Successfully installed. Deleting local file..."
    rm -f /private/tmp/profile.mobileconfig
    exit 0
else
    echo "Installation of profile failed. Deleting local file..."
    rm -f /private/tmp/profile.mobileconfig
    exit 1
fi

In this way, the profile can be contained in the script and deployed and installed without needing to create a separate package for it, which can sometimes work better. I've been using the above process for deploying some of our profiles and its working well.

Be sure also to create an Extension Attribute that captures the installed profile names or identifiers, so you can build Smart Groups for machines that should get it or not. This may be necessary even if just pushing the profile in a regular package install.

duffcalifornia
Contributor

@mm2270 This works really well! Not only does it solve my issue of needing to reinstall it should it become corrupted, but we can also use this script to install the profile to new computers and not disrupt the existing profile on currently deployed machines (something we were having an issue with when using it as a configuration profile)!

mm2270
Legendary Contributor III

@duffcalifornia Glad that helped out!

If you're interested, I also have a script that can use the JSS API to download any Configuration Profile that's in your JSS, directly from it and install it to the local machine. It's designed to use script parameters to pass the API account information as well as the Configuration Profile JSS ID. It will download the profile, convert it into the proper xml format and install it to the machine using the profiles command. So it even bypasses the need to put the config profile data into the script. Its more flexible also since it can be used for any Configuration Profile simply by changing the parameter for the profile's ID you pass to the script at run time.

monosodium
Contributor

@mm2270 How do you recommend creating an extension attribute? I am not very familiar with creating these from scratch. Our deployment of config profiles through the JSS has always been really unreliable and I am looking to deploy these through another method. Also, do you know how to remove these script-installed profiles? Do I need to copy the package to the machine to be able to remove it from the "profiles -R -p UUID (of the file in question)"?

Love the script! Thanks!

mm2270
Legendary Contributor III

Hi @monosodium Here are two Extension Attribute scripts. These aren't exactly what we use, but they're close and produce the same results either way

First one gathers just the installed Configuration Profile identifiers, which sometimes are not human readable because of how the JSS inserts a random UUID string into each one.

#!/bin/sh

installedProfiles=$(profiles -C | awk -F'profileIdentifier: ' '/attribute/{print $NF}')

echo "<result>$installedProfiles</result>"

This one gets the proper names for the profiles, which would be the display name you've given them when you created them in your JSS

#!/bin/sh

installedProfiles=$(profiles -Cv | awk -F'attribute: name: ' '/attribute: name:/{print $NF}')

echo "<result>$installedProfiles</result>"

Both of these could be fleshed out a bit more with some error checking, like making sure the variable contains some information before echoing it or something, but these should get you started at least. See how they work for you.

jstasik
New Contributor II

@mm2270 , I would love to take a look at the script that you are using to pull down the Configuration Profiles directly from the JSS. Would you be willing to share that?

Thanks in advance,
Jim

mm2270
Legendary Contributor III

@jstasik Funny you should ask. I just posted it to my github repo yesterday based on another thread I was participating in on installing Config Profiles. You can find the script here, and the description for this script in the repo is here, which has details on how to use it.

Nix4Life
Valued Contributor

Nice set of scripts!!

khey
Contributor

i have a question.

I have a mobileconfig that will request a cert from Microsoft CA by using /usr/bin/profiles method.

Everytime the script runs, it will create a new cert based on machine name. Is there a way to replace or delete the existing cert? this is mainly for certificate renewal or if i need to modify the config and re-deploy new cert.

thanks

bbot
Contributor

@khey I have a script that will remove all of the machine certificates. You can run this before the renewal process.

compname=dsconfigad -show | grep "Computer Account" | awk '{print $4}' | sed 's/.$//' Certs="$compname.your.domain.here" hashes=$(security find-certificate -c "$Certs" -a -Z|grep SHA-1|awk '{ print $NF }') for hash in $hashes; do echo deleting duplicate certs $hash sudo security delete-certificate -Z $hash done

Do you guys find that when you install profiles created from the JSS, but installed using profiles -I -F, you get a ton of messages in the JSS saying "Cannot remove profile 'AOFJ19284-9303-4CC5-9329-BB6314EAFF9' because it was not installed by the MDM server <MDMClientError:96>" ?

jnice22
New Contributor II

@mm2270 I'm using your Install-configuration-profile script. Everything works great. Just noticed an echo in the script "Single ID was passed. Running in single mode"
Does this mean I can run multiple ID's at one time? If so, how is this accomplished?

Really appreciate your script. Saved some major headaches.

mm2270
Legendary Contributor III

@jnice22 Yes, it does mean you can pass multiple Config Profile IDs at once and it will attempt to download and install all of them one at a time. You do this by simply passing multiple IDs with spaces between them. Since the IDs are simple integers, there's no need to quote them. It uses the space between each id to separate them out and loop over the entire list and run the download and install function for each one.

Note however, that if you pass a value to the rename flag to have the profiles renamed with a human readable name from the description in your JSS, it will do that for all of the ones passed to the script. You can't have it rename some profiles but not all of them.

Hope that helps.

khey
Contributor

@bbot Thanks!

bbot
Contributor

Hoping to get some help here on a renewal process for certificates
We use computer auth 802.1x for our wifi.
Our config profile is currently set to deploy via a script that utilizes /usr/bin/profiles through Self Service. (works perfectly)

However, if we push the same exact script through Jamf, it'll request and generate a new machine certificate, but won't create the proper keychain entry in the login keychain... instead, the com.apple..network.eap.system.identity.wlan.ssid.SSID is created in the system keychain.

When ran through self service, com.apple.network.eap.user.identity.wlan.ssid.SSID is created in the login keychain. (this works.. note the USER in the keychain entry.

Just to add more info. The installation of the config profile is working, but I'm seeing two different behavior when running the script as the user through Self Service, and pushing the script through a policy at recurring check-in.

danshaw
Contributor II

Hey @mm2270 - When installing config profiles manually, how do you set up the profile in the JSS to a) Not offer the profile in self service and b) not try to remove it if that computer is not in scope. If I set it to install automatically and the computer is not in the scope, it will error out saying that it cannot remove the config profile. If I add all the computers to the scope it will try to push the config out to all of them.

Also, why do you suggest to un-sign the config profile? I thought that was to solve my issue above, but the unsigned profile that I have now installed still wants to still be removed from the JSS.

5eb82206a03945cd8c42dd43ed80e49a

russeller
Contributor III

@danshaw We had a similar issue. I found that if you modify the PayloadUUID in the script of the CP before you deploy it, it won't match the UUID that the JSS generated, thus avoiding the "Remove Configuration Profile" MDM failures in the JSS.

Here is what it looks like in the CP in your script:

<key>PayloadUUID</key>
    <string>4B79E550-DEFB-4DAF-BECB-C62BE15A0966</string>

You can use a terminal command called uuidgen and it'll poop out a new UUID everytime you run the command. Replace the UUID payloads in your CP and save and redeploy it. You'll have to remove the previous one because this new one won't replace the old one since the UUID's are different. Hope this helps. Thanks!

danshaw
Contributor II

Thanks @ssrussell ! I'll give this a shot. Did you also un-sign your CP's like talked about above. Curious to know if this is needed or not.

russeller
Contributor III

@danshaw I'm not sure how to unsign a CP, but when I change the UUID and deploy it via the @mm2270 method it shows up as unsigned in SysPrefs > Profiles. I removed the Profiles Pane for all the student account in my school district and allow teachers/staff to remove the CP if they want. I know there is a bit you can flip in the CP to not allow removal, not sure if that works if it isn't signed or delivered from an MDM. I'll let someone else answer that who might be more familiar with that if that was your concern.

mm2270
Legendary Contributor III

@ssrussell is correct. If you change the unique identifier the Jamf Pro server won't see it as the same profile and won't try to remove it. Somehow I left that out of the older instructions above, but it's what I tend to do. Plus, you can use something identifiable and readable, like com.organization.profilename.setting instead of a long uid string that doesn't mean anything to anyone but a computer. Can't tell you how many times I've run sudo profiles -P on a machine, only to realize I can't tell which profile is which unless I throw in the -v flag to give me verbose output and slog through the info to figure out what they actually are.

FWIW, in my script on github that pulls down the profiles and installs them, there is a flag to rename the UUID to something identifiable by using an organization substring and the display name of the profile with spaces replaced by dashes. If you use that, it prevents the JSS from trying to remove it. I should probably add something to the script that generates a new UUID using uuidgen and plugs that in place of the original, if the rename flag isn't set at run time.

kwoodard
Contributor III

@ssrussell When I run your script, everything appears to work and I get an output. However, the output is empty. Looking at the logs I see this...

Stage 4: Scanning all policy scope details and creating report...
warning: failed to load external entity "/Users/xxx/Library/Application Support/_JSS_REPORT/_POLICY_DATA/.xml"
warning: failed to load external entity "/Users/xxx/Library/Application Support/_JSS_REPORT/_POLICY_DATA/.xml"
warning: failed to load external entity "/Users/xxx/Library/Application Support/_JSS_REPORT/_POLICY_DATA/.xml"
warning: failed to load external entity "/Users/xxx/Library/Application Support/_JSS_REPORT/_POLICY_DATA/.xml"
warning: failed to load external entity "/Users/xxx/Library/Application Support/_JSS_REPORT/_POLICY_DATA/.xml"

Any assistance?

LaMantia
New Contributor III

@mm2270 have been trying to get a user config profile top install via script or package. Both ways fail. My script errors and thinks I'm trying to install device profile but it's a user profile I downloaded it from my Jamf server and I did you steps listed in this thread. If you or anyone has an idea please let me know. Thank you.

Error..

Script result: profiles: verbose mode ON
profiles install profile /tmp/my.mobileconfig for user: (null)
profiles install for file:'/tmp/my.mobileconfig' and user:'root' returned 1 (An identity preference payload can only be included in a user profile.)
profiles: returned error: 1
Installation of profile failed. Deleting local file...

This only thing I'm doing different is the profiles command format.

Install the .mobileconfig with the profiles command

/usr/bin/profiles install -path /private/tmp/my.mobileconfig -forced -verbose

mm2270
Legendary Contributor III

User profiles have to be installed as the current user logged in, not as root, or it assumes they are device profiles.

But to take a huge step back here, why is there a need to install profiles in this way now? Back when this thread was first spawned, it made more practical sense, but I can't say it does anymore. If you aren't installing profiles over an MDM like Jamf Pro, you're going to run into a lot of problems. Some profile payloads now can't even be installed on devices unless they specifically come from an MDM.

LaMantia
New Contributor III

Intersting @mm2270 , so I would need to call out the current user in the script. Okay, thank you.

The reason is that this profile delivers a user MFA cert using scep. I've been hearing of MFA certs disappearing once in a while and the user then looses all o365 access. I think the profile is disappearing once in a while if APNS is unreachable and removing the cert in the keychain as a result. I have not been able to verify this but it seems logical that the issue in this thread is happening to my users as well.

I'm open to suggestions. It's know that the networks of the affected users don't all have great APNS connectivity and no fixes will be coming anytime soon as they are migrating off old networks/vpn's.

mm2270
Legendary Contributor III

Hmm, I actually don't know with any degree of certainty if a bad or poor APNs connection would be removing profiles. I suppose it's possible, but I haven't seen anything like that happen to us in quite some time now.

But, if you're determined to go this route for now, I would suggest taking another look at the script I posted above on my GitHub page (found here), which has the necessary code to install User Level profiles as the current user. The relevant code starts on line 167. You'll see that I'm getting the logged in user and the logged in UID, and then on line 174, it installs the profile as the user. You could try to incorporate what I have there into your own script.
Unlike System Level profiles, User Level get installed to an account, not globally like System profiles.

LaMantia
New Contributor III

Thanks again. I don't want to go this route but support keeps getting tickets that the user loses o365 so I am hoping to have this available in self service for them. Just in case. I will be opening a ticket with Jamf support as well to discuss it. Maybe they can find the issue.

Joyrex
New Contributor III

delete

wifichallenges
Contributor

FYI they removed the ability to install a profile with the command line profile command in osx 11.0

"profiles tool no longer supports installs. Use System Preferences Profiles to add configuration profiles."

see here:
https://www.alansiu.net/2021/01/06/semi-automating-profile-installation-in-big-sur/

so the method above wont work. I am happy that other people find the configuration wireless profile to be buggy as well. My macs are all losing their profile randomly sadly so i was looking for alternative methods to connect them up. I guess this wont be it. The open command referenced in the article does not do what i wanted.

i think we will move towards a certificate based install anyway and away from PSK. I dont supposed someone has a guide on that? well i am going to look around and research it now.