├── ReadMe.md ├── tcctool-mdm.sh └── tcctool.sh /ReadMe.md: -------------------------------------------------------------------------------- 1 | # TCC Tool 2 | version 2.9, 1 February 2023 3 | 4 | _Note: Terminal.app (or the process used to launch this script) will need Full Disk Access_ 5 | 6 | ## What does it do? 7 | Reads the system and user tcc.db and translates the following: 8 | - service name 9 | - client name 10 | - `auth_value` (allowed, denied) 11 | - `auth_reason` (user, system default, MDM) 12 | - `indirect_object_identifier` 13 | - last modified date 14 | - code signing requirement 15 | 16 | Outputs the report in CSV 17 | 18 | ## Usage 19 | `zsh tcctool.sh [-o outputfile]` 20 | 21 | If an output file path is not specified, the report text is sent to stdout. 22 | 23 | ## See also: 24 | [https://www.rainforestqa.com/blog/macos-tcc-db-deep-dive](https://www.rainforestqa.com/blog/macos-tcc-db-deep-dive) 25 | 26 | ## To Do 27 | - List of apps present in TCC.db that are no longer installed. 28 | - Offer to clean missing apps from TCC.db with tccutil 29 | -------------------------------------------------------------------------------- /tcctool-mdm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # ----- Legal: ---- 4 | # Sample scripts are not supported under any N-able support program or service. 5 | # The sample scripts are provided AS IS without warranty of any kind. 6 | # N-able expressly disclaims all implied warranties including, warranties 7 | # of merchantability or of fitness for a particular purpose. 8 | # In no event shall N-able or any other party be liable for damages arising 9 | # out of the use of or inability to use the sample scripts. 10 | # ----- /Legal ---- 11 | 12 | # !!! Terminal or process running this script will need Full Disk Access 13 | 14 | # Reads the /Library/Application Support/com.apple.TCC/MDMOverrides.plist for TCC 15 | # entries set by MDM profiles, and generates a human-readable report. 16 | 17 | # see also: 18 | # https://www.rainforestqa.com/blog/macos-tcc-db-deep-dive 19 | 20 | 21 | if [ -z "${ZSH_VERSION}" ]; then 22 | >&2 echo "ERROR: This script is only compatible with Z shell (/bin/zsh)." 23 | exit 1 24 | fi 25 | 26 | 27 | 28 | # service 29 | typeset -A ServiceArray 30 | ServiceArray[kTCCServiceAddressBook]="Contacts" 31 | ServiceArray[kTCCServiceAppleEvents]="Apple Events" 32 | ServiceArray[kTCCServiceBluetoothAlways]="Bluetooth" 33 | ServiceArray[kTCCServiceCalendar]="Calendar" 34 | ServiceArray[kTCCServiceCamera]="Camera" 35 | ServiceArray[kTCCServiceContactsFull]="Full contacts information" 36 | ServiceArray[kTCCServiceContactsLimited]="Basic contacts information" 37 | ServiceArray[kTCCServiceFileProviderDomain]="Files managed by Apple Events" 38 | ServiceArray[kTCCServiceFileProviderPresence]="See when files managed by client are in use" 39 | ServiceArray[kTCCServiceLocation]="Current location" 40 | ServiceArray[kTCCServiceMediaLibrary]="Apple Music, music and video activity, and media library" 41 | ServiceArray[kTCCServiceMicrophone]="Microphone" 42 | ServiceArray[kTCCServiceMotion]="Motion & Fitness Activity" 43 | ServiceArray[kTCCServicePhotos]="Read Photos" 44 | ServiceArray[kTCCServicePhotosAdd]="Add to Photos" 45 | ServiceArray[kTCCServicePrototype3Rights]="Authorization Test Service Proto3Right" 46 | ServiceArray[kTCCServicePrototype4Rights]="Authorization Test Service Proto4Right" 47 | ServiceArray[kTCCServiceReminders]="Reminders" 48 | ServiceArray[kTCCServiceScreenCapture]="Capture screen contents" 49 | ServiceArray[kTCCServiceSiri]="Use Siri" 50 | ServiceArray[kTCCServiceSpeechRecognition]="Speech Recognition" 51 | ServiceArray[kTCCServiceSystemPolicyDesktopFolder]="Desktop folder" 52 | ServiceArray[kTCCServiceSystemPolicyDeveloperFiles]="Files in Software Development" 53 | ServiceArray[kTCCServiceSystemPolicyDocumentsFolder]="Files in Documents folder" 54 | ServiceArray[kTCCServiceSystemPolicyDownloadsFolder]="Files in Downloads folder" 55 | ServiceArray[kTCCServiceSystemPolicyNetworkVolumes]="Files on a network volume" 56 | ServiceArray[kTCCServiceSystemPolicyRemovableVolumes]="Files on a removable volume" 57 | ServiceArray[kTCCServiceSystemPolicySysAdminFiles]="Administer the computer" 58 | ServiceArray[kTCCServiceWillow]="Home data" 59 | ServiceArray[kTCCServiceSystemPolicyAllFiles]="Full Disk Access" 60 | ServiceArray[kTCCServiceAccessibility]="Control the computer" 61 | ServiceArray[kTCCServicePostEvent]="Send keystrokes" 62 | ServiceArray[kTCCServiceListenEvent]="Monitor input from the keyboard" 63 | ServiceArray[kTCCServiceDeveloperTool]="Run insecure software locally" 64 | ServiceArray[kTCCServiceLiverpool]="Location services" 65 | ServiceArray[kTCCServiceUbiquity]="iCloud" 66 | ServiceArray[kTCCServiceShareKit]="Share features" 67 | ServiceArray[kTCCServiceLinkedIn]="Share via LinkedIn" 68 | ServiceArray[kTCCServiceTwitter]="Share via Twitter" 69 | ServiceArray[kTCCServiceFacebook]="Share via Facebook" 70 | ServiceArray[kTCCServiceSinaWeibo]="Share via Sina Weibo" 71 | ServiceArray[kTCCServiceTencentWeibo]="Share via Tencent Weibo" 72 | 73 | 74 | # MDM profile overrides 75 | 76 | if [ -f "/Library/Application Support/com.apple.TCC/MDMOverrides.plist" ] 77 | then 78 | echo "======== [ MDM TCC Profiles ]" 79 | 80 | FullMDMOverrides=$(plutil -convert xml1 -o - /Library/Application\ Support/com.apple.TCC/MDMOverrides.plist) 81 | 82 | MDMOverrides=$(echo $FullMDMOverrides | xmllint --xpath "/*/dict[*]/key" - | sed 's/<[^>]*>/^/g') 83 | 84 | Index=1 85 | 86 | for Identifier in ${(s:^:)MDMOverrides} 87 | do 88 | printf "--- \n%s\n" $Identifier 89 | 90 | IdentifierXML=$(echo $FullMDMOverrides | xmllint --xpath "/*/dict[*]/dict[$Index]" -) 91 | 92 | AllServiceNames="$(echo $IdentifierXML | xmllint --xpath '/dict[1]/key' - | sed 's/<[^>]*>/\n/g')" 93 | 94 | ServiceIndex=1 95 | 96 | for ServiceName in ${=AllServiceNames} 97 | do 98 | if [ $ServiceName = "kTCCServiceAppleEvents" ] 99 | then 100 | if $(echo $IdentifierXML | xmllint --xpath "/dict[$ServiceIndex]//true" - &> /dev/null) 101 | then 102 | AuthVal="Allowed" 103 | else 104 | AuthVal="Denied" 105 | fi 106 | else 107 | AuthVal="$(echo $IdentifierXML | xmllint --xpath "/dict/dict[$ServiceIndex]/key[1]" - | sed 's/<[^>]*>//g')" 108 | fi 109 | 110 | printf "\t%s:\n" $AuthVal 111 | printf "\t\t%s\n" $ServiceArray[$ServiceName] 112 | ((ServiceIndex++)) 113 | 114 | done 115 | 116 | ((Index++)) 117 | done 118 | 119 | 120 | else 121 | echo "======== [ No MDM TCC Profiles found ]" 122 | fi 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /tcctool.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # version 2.9, 28 November 2022 4 | 5 | # !!! Terminal or process running this script will need Full Disk Access 6 | 7 | # read the tcc.db and translate the following: 8 | # service 9 | # client 10 | # auth_value 11 | # auth_reason 12 | # indirect_object_identifier 13 | # last_modified 14 | 15 | # see also: 16 | # https://www.rainforestqa.com/blog/macos-tcc-db-deep-dive 17 | 18 | 19 | if [ -z "${ZSH_VERSION}" ]; then 20 | >&2 echo "ERROR: This script is only compatible with Z shell (/bin/zsh). Invoke with 'zsh tcctool.sh'" 21 | exit 1 22 | fi 23 | 24 | # Credit to Pico @ Macadmins Slack 25 | if [ ! -r '/Library/Application Support/com.apple.TCC/TCC.db' ]; then 26 | ParentProcess=$(basename $(ps -o command= $(ps -o ppid= $(ps -o ppid= | head -1)))) 27 | # "Terminal" if run manually. Otherwise, the agent process running scripts. 28 | >&2 echo "ERROR: $ParentProcess must have Full Disk Access in order to read the TCC database." 29 | exit 1 30 | fi 31 | 32 | 33 | # Command line options 34 | zparseopts -D -E -F -K -- o:=OutputFile 35 | 36 | 37 | # CSV Columns: 38 | # Source - System, MDM, User 39 | # Username - user shortname 40 | # Client - app name 41 | # ClientID - identifier 42 | # ServiceName - eg "kTCCServiceAddressBook" 43 | # ServiceFriendlyName - eg "Contacts" 44 | # AuthValue - Denied, Unknown, Allowed, Limited 45 | # AuthReason - User Set, System Set, etc 46 | # Timestamp - UNIX epoch time 47 | # FormattedTime - modified time ISO formatted (eg "2022-11-15T13:42:19-07:00") 48 | # CodeSignReq - code signing requirements 49 | # Coming Soon: IsInstalled - is client app installed boolean 0/1 50 | 51 | CSVArrayHeader="Source, Username, Client, ClientID, ServiceName, ServiceFriendlyName, AuthValue, AuthReason, Timestamp, FormattedTime, CodeSignReq" 52 | 53 | typeset -A CSVArray 54 | CSVArray=${CSVArrayHeader} 55 | 56 | 57 | typeset -A RowArray 58 | # each row in tcc.db builds a row in the CSV. RowArray holds the values, then those are echoed to a new line in CSVArray 59 | 60 | 61 | # TCC Translator arrays 62 | 63 | # service 64 | typeset -A ServiceArray 65 | ServiceArray[kTCCServiceAddressBook]="Contacts" 66 | ServiceArray[kTCCServiceAppleEvents]="Apple Events" 67 | ServiceArray[kTCCServiceBluetoothAlways]="Bluetooth" 68 | ServiceArray[kTCCServiceCalendar]="Calendar" 69 | ServiceArray[kTCCServiceCamera]="Camera" 70 | ServiceArray[kTCCServiceContactsFull]="Full contacts information" 71 | ServiceArray[kTCCServiceContactsLimited]="Basic contacts information" 72 | ServiceArray[kTCCServiceFileProviderDomain]="Files managed by Apple Events" 73 | ServiceArray[kTCCServiceFileProviderPresence]="See when files managed by client are in use" 74 | ServiceArray[kTCCServiceLocation]="Current location" 75 | ServiceArray[kTCCServiceMediaLibrary]="Apple Music, music and video activity, and media library" 76 | ServiceArray[kTCCServiceMicrophone]="Microphone" 77 | ServiceArray[kTCCServiceMotion]="Motion & Fitness Activity" 78 | ServiceArray[kTCCServicePhotos]="Read Photos" 79 | ServiceArray[kTCCServicePhotosAdd]="Add to Photos" 80 | ServiceArray[kTCCServicePrototype3Rights]="Authorization Test Service Proto3Right" 81 | ServiceArray[kTCCServicePrototype4Rights]="Authorization Test Service Proto4Right" 82 | ServiceArray[kTCCServiceReminders]="Reminders" 83 | ServiceArray[kTCCServiceScreenCapture]="Capture screen contents" 84 | ServiceArray[kTCCServiceSiri]="Use Siri" 85 | ServiceArray[kTCCServiceSpeechRecognition]="Speech Recognition" 86 | ServiceArray[kTCCServiceSystemPolicyDesktopFolder]="Desktop folder" 87 | ServiceArray[kTCCServiceSystemPolicyDeveloperFiles]="Files in Software Development" 88 | ServiceArray[kTCCServiceSystemPolicyDocumentsFolder]="Files in Documents folder" 89 | ServiceArray[kTCCServiceSystemPolicyDownloadsFolder]="Files in Downloads folder" 90 | ServiceArray[kTCCServiceSystemPolicyNetworkVolumes]="Files on a network volume" 91 | ServiceArray[kTCCServiceSystemPolicyRemovableVolumes]="Files on a removable volume" 92 | ServiceArray[kTCCServiceSystemPolicySysAdminFiles]="Administer the computer" 93 | ServiceArray[kTCCServiceWillow]="Home data" 94 | ServiceArray[kTCCServiceSystemPolicyAllFiles]="Full Disk Access" 95 | ServiceArray[kTCCServiceAccessibility]="Control the computer" 96 | ServiceArray[kTCCServicePostEvent]="Send keystrokes" 97 | ServiceArray[kTCCServiceListenEvent]="Monitor input from the keyboard" 98 | ServiceArray[kTCCServiceDeveloperTool]="Run insecure software locally" 99 | ServiceArray[kTCCServiceLiverpool]="Location services" 100 | ServiceArray[kTCCServiceUbiquity]="iCloud" 101 | ServiceArray[kTCCServiceShareKit]="Share features" 102 | ServiceArray[kTCCServiceLinkedIn]="Share via LinkedIn" 103 | ServiceArray[kTCCServiceTwitter]="Share via Twitter" 104 | ServiceArray[kTCCServiceFacebook]="Share via Facebook" 105 | ServiceArray[kTCCServiceSinaWeibo]="Share via Sina Weibo" 106 | ServiceArray[kTCCServiceTencentWeibo]="Share via Tencent Weibo" 107 | 108 | # auth_reason 109 | typeset -A AuthReasonArray 110 | AuthReasonArray[0]="Inherited/Unknown" 111 | AuthReasonArray[1]="Error" 112 | AuthReasonArray[2]="User Consent" 113 | AuthReasonArray[3]="User Set" 114 | AuthReasonArray[4]="System Set" 115 | AuthReasonArray[5]="Service Policy" 116 | AuthReasonArray[6]="MDM Policy" 117 | AuthReasonArray[7]="Override Policy" 118 | AuthReasonArray[8]="Missing usage string" 119 | AuthReasonArray[9]="Prompt Timeout" 120 | AuthReasonArray[10]="Preflight Unknown" 121 | AuthReasonArray[11]="Entitled" 122 | AuthReasonArray[12]="App Type Policy" 123 | 124 | # auth_value 125 | typeset -A AuthValueArray 126 | AuthValueArray[0]="Denied" 127 | AuthValueArray[1]="Unknown" 128 | AuthValueArray[2]="Allowed" 129 | AuthValueArray[3]="Limited" 130 | 131 | CurrentClient="" 132 | 133 | 134 | processRow() { 135 | TCCRow=$1 136 | RawClient=$(echo $TCCRow | cut -d',' -f1) 137 | 138 | RowArray[Client]=\"$( basename $RawClient)\" 139 | 140 | ClientType=$(echo $TCCRow | cut -d',' -f2) 141 | if [ $ClientType -eq 0 ] 142 | then 143 | Client=$(mdfind "kMDItemCFBundleIdentifier = $RawClient" | head -1) 144 | if [ -z $Client ] 145 | then 146 | Client=$RawClient 147 | fi 148 | else 149 | Client=$RawClient 150 | RowArray[ClientID]="\"$Client\"" 151 | fi 152 | 153 | 154 | ServiceName=$(echo $TCCRow | cut -d',' -f3) 155 | AuthVal=$(echo $TCCRow | cut -d',' -f4) 156 | AuthReason=$(echo $TCCRow | cut -d',' -f5) 157 | DateAuthEpoch=$(echo $TCCRow | cut -d',' -f6) 158 | 159 | DateAuth=$(date -r $DateAuthEpoch +%Y-%m-%dT%H%z) 160 | 161 | RowArray[ServiceName]=$ServiceName 162 | RowArray[Timestamp]=$DateAuthEpoch 163 | RowArray[FormattedTime]=$DateAuth 164 | 165 | CodeSignReqHex=$(echo $TCCRow | cut -d',' -f7) 166 | 167 | if [ $CodeSignReqHex != "NULL" ] 168 | then 169 | CodeSignReqString=$(echo $CodeSignReqHex | xxd -r -p | csreq -r - -t /dev/stdin) 170 | fi 171 | 172 | RowArray[CodeSignReq]=$CodeSignReqString 173 | 174 | if [ "$Client" != "$CurrentClient" ] 175 | then 176 | CurrentClient=$Client 177 | ShortClient=$(basename $Client) # clean up paths a bit 178 | # printf "--- \n\n%s\n" $ShortClient 179 | CurrentAuthVal="" 180 | fi 181 | 182 | if [ "$AuthVal" != "$CurrentAuthVal" ] 183 | then 184 | CurrentAuthVal=$AuthVal 185 | # printf "\t%s:\n" $AuthValueArray[$AuthVal] 186 | fi 187 | 188 | # printf "\t\t%s (%s - %s)\n" $ServiceArray[$ServiceName] $AuthReasonArray[$AuthReason] $DateAuth 189 | 190 | RowArray[ServiceFriendlyName]=\"$ServiceArray[$ServiceName]\" 191 | RowArray[AuthValue]=$AuthValueArray[$AuthVal] 192 | RowArray[AuthReason]=\"$AuthReasonArray[$AuthReason]\" 193 | 194 | } 195 | 196 | 197 | 198 | 199 | # start with the system defaults: 200 | 201 | # echo "======== [System Default Permissions]" 202 | 203 | sqlite3 /Library/Application\ Support/com.apple.tcc/tcc.db -csv -noheader -nullvalue '-' \ 204 | 'select client, client_type, service, auth_value, auth_reason, last_modified, quote(csreq) from access order by client, auth_value' 2>/dev/null \ 205 | | while read -r TCCRow 206 | do 207 | # reset the RowArray 208 | RowArray[Source]="System" 209 | RowArray[Username]="" 210 | RowArray[Client]="" 211 | RowArray[ClientID]="" 212 | RowArray[ServiceName]="" 213 | RowArray[ServiceFriendlyName]="" 214 | RowArray[AuthValue]="" 215 | RowArray[AuthReason]="" 216 | RowArray[Timestamp]="" 217 | RowArray[FormattedTime]="" 218 | RowArray[CodeSignReq]="" 219 | 220 | processRow "$TCCRow" 221 | 222 | RowString="${RowArray[Source]},\ 223 | ${RowArray[Username]},\ 224 | ${RowArray[Client]},\ 225 | ${RowArray[ClientID]},\ 226 | ${RowArray[ServiceName]},\ 227 | ${RowArray[ServiceFriendlyName]},\ 228 | ${RowArray[AuthValue]},\ 229 | ${RowArray[AuthReason]},\ 230 | ${RowArray[Timestamp]},\ 231 | ${RowArray[FormattedTime]},\ 232 | ${RowArray[CodeSignReq]}" 233 | 234 | CSVArray+=(${RowString}) 235 | 236 | done 237 | 238 | # 239 | # printf '%s\n' "${CSVArray[@]}" 240 | # 241 | 242 | # echo "======== [Per-user Permissions Overrides]" 243 | 244 | 245 | # list all Users' home directories (uses dscl in the rare instance they're not in /Users/*) 246 | USERHOMES=$(dscl /Local/Default -list /Users NFSHomeDirectory | grep -v "/var/empty" | awk '$2 ~ /^\// { print $2 }' ) 247 | 248 | for USERHOME in ${=USERHOMES} 249 | do 250 | 251 | if [ -f "${USERHOME}/Library/Application Support/com.apple.tcc/tcc.db" ] 252 | then 253 | 254 | # echo "================ [ ${USERHOME} ]" 255 | UserShortName=$( basename $USERHOME ) 256 | 257 | sqlite3 ${USERHOME}/Library/Application\ Support/com.apple.tcc/tcc.db -csv -noheader -nullvalue '-' \ 258 | 'select client, client_type, service, auth_value, auth_reason, last_modified, quote(csreq) from access order by client, auth_value' 2>/dev/null \ 259 | | while read -r TCCRow 260 | do 261 | # reset the RowArray 262 | RowArray[Source]="User" 263 | RowArray[Username]=$UserShortName 264 | RowArray[Client]="" 265 | RowArray[ClientID]="" 266 | RowArray[ServiceName]="" 267 | RowArray[ServiceFriendlyName]="" 268 | RowArray[AuthValue]="" 269 | RowArray[AuthReason]="" 270 | RowArray[Timestamp]="" 271 | RowArray[FormattedTime]="" 272 | RowArray[CodeSignReq]="" 273 | 274 | processRow "$TCCRow" 275 | 276 | RowString="${RowArray[Source]},\ 277 | ${RowArray[Username]},\ 278 | ${RowArray[Client]},\ 279 | ${RowArray[ClientID]},\ 280 | ${RowArray[ServiceName]},\ 281 | ${RowArray[ServiceFriendlyName]},\ 282 | ${RowArray[AuthValue]},\ 283 | ${RowArray[AuthReason]},\ 284 | ${RowArray[Timestamp]},\ 285 | ${RowArray[FormattedTime]},\ 286 | ${RowArray[CodeSignReq]}" 287 | 288 | CSVArray+=(${RowString}) 289 | 290 | done 291 | 292 | fi 293 | 294 | done 295 | 296 | if [[ ${#OutputFile} -eq 2 ]]; then 297 | echo "Saving tcc.db output to ${OutputFile[-1]}" 298 | 299 | printf '%s\n' "${CSVArray[@]}" > $OutputFile[-1] 300 | else 301 | printf '%s\n' "${CSVArray[@]}" 302 | fi 303 | 304 | 305 | 306 | 307 | 308 | # MDM profile overrides 309 | 310 | if [ -f "/Library/Application Support/com.apple.TCC/MDMOverrides.plist" ] 311 | then 312 | # echo "======== [ MDM TCC Profiles ]" 313 | 314 | FullMDMOverrides=$(plutil -convert xml1 -o - /Library/Application\ Support/com.apple.TCC/MDMOverrides.plist) 315 | 316 | MDMOverrides=$(echo $FullMDMOverrides | xmllint --xpath "/*/dict[*]/key" - | sed 's/<[^>]*>/ /g') 317 | 318 | Index=1 319 | 320 | for Identifier in ${=MDMOverrides} 321 | do 322 | # printf "--- \n%s\n" $Identifier 323 | 324 | IdentifierXML=$(echo $FullMDMOverrides | xmllint --xpath "/*/dict[*]/dict[$Index]" -) 325 | 326 | AllServiceNames="$(echo $IdentifierXML | xmllint --xpath '/dict[1]/key' - | sed 's/<[^>]*>/\n/g')" 327 | 328 | ServiceIndex=1 329 | 330 | for ServiceName in ${=AllServiceNames} 331 | do 332 | if [ $ServiceName = "kTCCServiceAppleEvents" ] 333 | then 334 | if $(echo $IdentifierXML | xmllint --xpath "/dict[$ServiceIndex]//true" - &> /dev/null) 335 | then 336 | AuthVal="Allowed" 337 | else 338 | AuthVal="Denied" 339 | fi 340 | else 341 | AuthVal="$(echo $IdentifierXML | xmllint --xpath "/dict/dict[$ServiceIndex]/key[1]" - | sed 's/<[^>]*>//g')" 342 | fi 343 | 344 | # printf "\t%s:\n" $AuthVal 345 | # printf "\t\t%s\n" $ServiceArray[$ServiceName] 346 | ((ServiceIndex++)) 347 | 348 | # reset the RowArray 349 | RowArray[Source]="MDM" 350 | RowArray[Username]="" 351 | RowArray[Client]="" 352 | RowArray[ClientID]="" 353 | RowArray[ServiceName]=$ServiceName 354 | RowArray[AuthValue]=$AuthVal 355 | RowArray[AuthReason]="MDM" 356 | RowArray[Timestamp]="" 357 | RowArray[FormattedTime]="" 358 | 359 | RowArray[ServiceFriendlyName]=\"$ServiceArray[$ServiceName]\" 360 | RowArray[AuthReason]=\"$AuthReasonArray[$AuthReason]\" 361 | 362 | RowString="${RowArray[Source]},\ 363 | ${RowArray[Username]},\ 364 | ${RowArray[Client]},\ 365 | ${RowArray[ClientID]},\ 366 | ${RowArray[ServiceName]},\ 367 | ${RowArray[ServiceFriendlyName]},\ 368 | ${RowArray[AuthValue]},\ 369 | ${RowArray[AuthReason]},\ 370 | ${RowArray[Timestamp]},\ 371 | ${RowArray[FormattedTime]},\ 372 | ${RowArray[CodeSignReq]}" 373 | 374 | CSVArray+=(${RowString}) 375 | 376 | 377 | 378 | done 379 | 380 | ((Index++)) 381 | done 382 | 383 | 384 | else 385 | # echo "======== [ No MDM TCC Profiles found ]" 386 | fi 387 | 388 | --------------------------------------------------------------------------------