Mac – SCCM – CMMAC and PKG

email me

I noticed while creating a package on a Mac, which would eventually be added to SCCM, Microsoft’s instructions on the entire process were pretty vague in places. Here are my own notes on packaging and CMMACs.


Build Process


On Dev Mac

Create your YourApp.py (I used pycharm) > use pyinstaller to create APP file > add APP file + any scripts to Packages > use Packages to create PKG > use CMAppUtil to create CMMAC > add to SCCM

CMAppUtil command (used to create the SCCM CMMAC file):

sudo ./CMAppUtil -c PackageName.pkg -o ./

sudo  ./CMAppUtil -c PackageName.dmg -o ./

 


What a CMMAC file looks like

You can browse, add, or remove files by using 7zip (something no one tells you)

CMMACPackage
—-Metadata
——-Detection.xml
—-contents
——-script.sh (SCCM command. I use a script because I can add complexity to installation)
 ———-/usr/sbin/installer -pkg “YourApp.pkg” -target “/” -verboseR
——-YourApp.pkg (created using Packages app)
———-YourApp.app (main app created using pyinstaller, update plist with version)
———-script.command (set up for the daemon launch agent + other commands)
———-com.YourApp.plist (launch agent instructions)

 

 

In SCCM, Add CMMAC Package


Step 1 –
Add package to SCCM using CMMAC file (select defaults)

a – Create APP using pyinstaller
b – Create PKG using Packages. Copy from build folder, to CMAppUtil folder
c – Create CMMAC using CMAppUtil
d – Copy CMMAC to SCCM server/Distribution Point
e – Start Application setup in SCCM using Applications Node


Step 2 – Deployment Type

Verify/Update Deployment Type > Programs with SH, APP, or PKG command line
/bin/sh “script.sh”
/usr/sbin/installer -pkg “Package.pkg” -target “/” -verboseR


Step 3 – Distribute Content

a – Wait for content to be fully synced
b – Wait 5 minutes after content has been synced


Step 4 – Set up Deployment
(select defaults)

a – Wait 5 minutes for deployment to sync


Step 5 – Force refresh on client machine
(use Connect To on Config Manager app)

a – Monitor Library > Caches > Microsoft > CCM > Content folder
b – CMMAC should download, extract, and run

 

Some of my Working Files


YourApp.app: Info.plist

Make sure your Info.plist in the APP looks something like this before creating the PKG file

<?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>CFBundleDisplayName</key>
<string>YourApp</string>
<key>CFBundleExecutable</key>
<string>MacOS/YourApp</string>
<key>CFBundleIconFile</key>
<string>icon-windowed.icns</string>
<key>CFBundleIdentifier</key>
<string>com.domain.app.YourApp</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>YourApp</string>
<key>CFBundleVersion</key>
<string>1.0.1</string>
<key>CFBundleShortVersionString</key>
<string>1.0.1</string>
<key>CFBundleGetInfoString</key>
<string>1.0.1</string>
<key>CFBundleSupportedPlatforms</key>
<array>
    <string>MacOSX</string>
</array>
<key>YourAppVersionString</key>
<string>1.0.1</string>
</dict>
</plist>

 

YourApp.app: version.plist

<?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>BuildAliasOf</key>
<string>YourApp</string>
<key>BuildVersion</key>
<string>1.0.1</string>
<key>CFBundleShortVersionString</key>
<string>1.0.1</string>
<key>CFBundleVersion</key>
<string>1.0.1</string>
<key>ProjectName</key>
<string>YourApp</string>
<key>SourceVersion</key>
<string>10100</string>
</dict>
</plist>

 

CMMAC: Detection.xml

The Detection file is what SCCM uses to import package settings into the details of the SCCM Application, which are in turn used to identify if the PKG has been deployed to the Mac. If this isn’t working properly, the PKG will continually be deployed to the Mac—you don’t want that.

<?xml version=”1.0″ encoding=”UTF-8″?>
<CMAppUtil Version=”5.00.8466.1000″ TimeStamp=”2019-03-28 20:49:41 +0000″ MacOSX=”10.13.4″ xmlns=”http://schemas.microsoft.com/SystemCenterConfigurationManager/2011/10/25/CMMAC”>
<Package PackageName=”YourApp.pkg” PackageType=”pkg”>
<DetectionAction Type=”Basic”>
<Property Identifier=”com.domain.app.YourApp” Version=”1.0.1″ Type=”AppBundle”/>
</DetectionAction>
<OptionalPackages>
</OptionalPackages>
<InstallerParams VolumeInfo=”/” RestartAction=”None”/>
</Package>
</CMAppUtil>

 

 

Notes

also see: Mac – Bash – Package-Repackage

 

tags: CMMAC, CMMAC design, SCCM CMMAC scripting, SCCM Development, MrNetTek

Mac – pyinstaller – Create .APP File

email me

PyInstaller reads a Python script written by you. It analyzes your code to discover every other module and library your script needs in order to execute. Then it collects copies of all those files – including the active Python interpreter! – and puts them with your script in a single folder, or optionally in a single executable file, and an APP.

Install

pip3 install pyinstaller


Compile

sudo pyinstaller Notify.py -n Notify --windowed --noconfirm --clean

 

Other packages/dependencies you may need before compiling:

xcode-select –install
ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”
brew install python3
brew link python
brew postinstall python
pip3 install -U py2app

pip3 install Pillow
brew install tcl-tk
pip3 install virtualenv
brew install pyside
pip3 install sip
brew install cartr/qt4/pyqt@4
brew install PyQt5
brew install opencv@2
brew install git

pip3 install opencv-python
pip install python-resize-image

 


Options

If you specify only --onefile under Mac OS X, the output in dist is a UNIX executable myscript. It can be executed from a Terminal command line. Standard input and output work as normal through the Terminal window.

If you also specify --windowed, the dist folder contains two outputs: the UNIX executable myscript and also an OS X application named myscript.app.

As you probably know, an application is a special type of folder. The one built by PyInstaller contains a folder always named Contents. It contains:

  • A folder Frameworks which is empty.
  • A folder MacOS that contains a copy of the same myscript UNIX executable.
  • A folder Resources that contains an icon file.
  • A file Info.plist that describes the app.

PyInstaller builds minimal versions of these elements.

Use the icon= argument to specify a custom icon for the application. (If you do not specify an icon file, PyInstaller supplies a file icon-windowed.icns with the PyInstaller logo.)
Notes

https://pyinstaller.readthedocs.io/en/v3.3.1/operating-mode.html

https://realpython.com/pyinstaller-python/

Mac – Install py2app

email me

py2app is a Python setuptools command which will allow you to make standalone application bundles and plugins from Python scripts. py2app is similar in purpose and design to py2exe for Windows.

From a terminal…

pip3 install -U py2app

 

Other packages/dependencies you may need before compiling

xcode-select –install
ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)”
brew install python3
brew link python
brew postinstall python
pip3 install -U py2app

pip3 install Pillow
brew install tcl-tk
pip3 install virtualenv
brew install pyside
pip3 install sip
brew install cartr/qt4/pyqt@4
brew install PyQt5
brew install opencv@2
pip3 install opencv-python
pip install python-resize-image


Notes

py2app – Create standalone Mac OS X applications with Python

JavaScript – FizzBuzz Example

email me

This is my FizzBuzz delight for today. FizzBuzz has been used as an interview screening device for computer programmers for many years. Writing a program to output the first 100 FizzBuzz numbers is a trivial problem for any would-be computer programmer, so interviewers can easily sort out those with insufficient programming ability.

The objective is to print 1-100; when a number is a multiple of three, print Fizz; when a number is a multiple of five, print Buzz; if the number is a multiple of three AND five, print FizzBuzz; if the number doesn’t meet one of those conditions, simply print the number.

for (var i=1; i &lt;= 20; i++)
{
if (i % 15 == 0)
console.log("FizzBuzz");
else if (i % 3 == 0)
console.log("Fizz");
else if (i % 5 == 0)
console.log("Buzz");
else
console.log(i);
}

C# – FizzBuzz Example

email me

This is my FizzBuzz delight for today. FizzBuzz has been used as an interview screening device for computer programmers for many years. Writing a program to output the first 100 FizzBuzz numbers is a trivial problem for any would-be computer programmer, so interviewers can easily sort out those with insufficient programming ability.

The objective is to print 1-100; when a number is a multiple of three, print Fizz; when a number is a multiple of five, print Buzz; if the number is a multiple of three AND five, print FizzBuzz; if the number doesn’t meet one of those conditions, simply print the number.

* created to print with the numbers 1-100. Remove the i +, if you don’t want to see the extra numbers.

using System;

namespace ConsoleApp
{
class FizzBuzz
{
static void Main(string[] args)
{
// count 1 to 100
for (int i = 1; i &lt;= 100; i++)
{
// multiples of 3 and 5
if (i % 15 == 0)
{
Console.WriteLine(i + " FizzBuzz");
}
// multiple of 3
else if (i % 3 == 0)
{
Console.WriteLine(i + " Fizz");
}
// multiple of 5
else if (i % 5 == 0)
{
Console.WriteLine(i + " Buzz");
}
// everything else
else
{
Console.WriteLine(i);
}
}

// wait
Console.ReadKey();

}
}
}


Output

1
2
3 Fizz
4
5 Buzz
6 Fizz
7
8
9 Fizz
10 Buzz
11
12 Fizz
13
14
15 FizzBuzz
16
17
18 Fizz
19
20 Buzz
21 Fizz
22
23
24 Fizz
25 Buzz
26
27 Fizz
28
29
30 FizzBuzz
31
32
33 Fizz
34
35 Buzz
36 Fizz
37
38
39 Fizz
40 Buzz
41
42 Fizz
43
44
45 FizzBuzz
46
47
48 Fizz
49
50 Buzz
51 Fizz
52
53
54 Fizz
55 Buzz
56
57 Fizz
58
59
60 FizzBuzz
61
62
63 Fizz
64
65 Buzz
66 Fizz
67
68
69 Fizz
70 Buzz
71
72 Fizz
73
74
75 FizzBuzz
76
77
78 Fizz
79
80 Buzz
81 Fizz
82
83
84 Fizz
85 Buzz
86
87 Fizz
88
89
90 FizzBuzz
91
92
93 Fizz
94
95 Buzz
96 Fizz
97
98
99 Fizz
100 Buzz

Windows – Cannot Connect to Network Share or Map Drives

email me

These are some of my troubleshooting notes to fixing the cannot connect to UNCs (Windows cannot access) or map to network shares error. Try one thing at a time, test a UNC, try the next step.


Verify on Client Machine

Enable – Turn on sharing and connect to devices

Start – Workstation service

Install – File and Print Sharing for Microsoft Networks

Install – Client for Microsoft Networks

Check – File and Print Sharing Not blocked in firewall

Enable SMB1 – Control Panel > Programs and Features > Enable SMB 1.0: check SMB 1.0/CIF; uncheck Automatic Removal

Start – Computer Browser service

Enable – NetBIOS over TCP/IP

Start – Netlogon service (if on a domain)


Reg Keys

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
dword: LocalAccountTokenFilterPolicy
value: 1

HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\
dword: EnableLinkedConnections
value: 1

HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters
dword: RequireSecureNegotiate
value: 0

HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation
—delete DependOnService & DependOnGroup

HKLM\SYSTEM\CurrentControlSet\Services\Mup\Parameters
dword: EnableDfsLoopbackTargets
value: 1

HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters
dword: Smb1
value: 1

HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters
dword: Smb2
value: 1

HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order
string: ProviderOrder
value: RDPNP,LanmanWorkstation,webclient


Commands

netsh winsock reset all

nbtstat -RR

net view \\%computername%

sc.exe config lanmanworkstation depend= bowser/mrxsmb10/nsi
sc.exe config mrxsmb20 start= disabled

net use x:\\IP_ADDRESS\ShareName /user:username password

Enable/Disable SMBv1, SMBv2, SMBv3

Python – Write Lines to Text File

email me

Tested in Windows 10, 10.0.17134.556.

import os.path

textFile = "c:\\Users\\Shared\\TextFile.txt"
doesFileExist=os.path.isfile(textFile)
if doesFileExist == True:
file = open(textFile,"w+")#w+ w, a+, r
file.write('Write to textfile 1\n')
file.write('Write to textfile 2\n')
file.write('Write to textfile 3\n')
file.close()

 


Notes

https://docs.python.org/3/

 

Mode Description
‘r’ This is the default mode. It Opens file for reading.
‘w’ This Mode Opens file for writing.
If file does not exist, it creates a new file.
If file exists it truncates the file.
‘x’ Creates a new file. If file already exists, the operation fails.
‘a’ Open file in append mode.
If file does not exist, it creates a new file.
‘t’ This is the default mode. It opens in text mode.
‘b’ This opens in binary mode.
‘+’ This will open a file for reading and writing (updating)

Python – Update Image Dynamically

email me

The update function

# dynamically update image
def update_image():
global photo
# new image
imgPath = "/Users/Shared/Images/image1.png"
doesImageExist=os.path.isfile(imgPath)
if doesImageExist == False:
# default image
imgPath = "/Users/Shared/Images/default/image1.png"
photo = ImageTk.PhotoImage(Image.open(imgPath))
photoBox.config(image=photo)


Part of my widget or form

root = tk.Tk()
root.geometry("350x300")# X Y
root.resizable(0,0)
root.title("Window Title")

root.attributes('-topmost', True)

imgPath = "/Users/Shared/Images/image1.png"
photo = ImageTk.PhotoImage(Image.open(imgPath))
photoBox = tk.Label(root, image = photo)
photoBox.pack()

root.mainloop()


Call the image update

root.after(1000, update_image)