From d3a3ddef79503ea1464c39f13dd3e8b67c3be4f2 Mon Sep 17 00:00:00 2001 From: Mikael Karlsson <43226266+Micke-K@users.noreply.github.com> Date: Sun, 18 Jul 2021 16:57:46 +1000 Subject: [PATCH] 3.1.8 --- Documentation/Strings-cs.json | Bin 2397440 -> 2397798 bytes Documentation/Strings-de.json | Bin 2542156 -> 2542504 bytes Documentation/Strings-en.json | Bin 2277060 -> 2277408 bytes Documentation/Strings-es.json | Bin 2555466 -> 2555838 bytes Documentation/Strings-fr.json | Bin 2559176 -> 2559548 bytes Documentation/Strings-hu.json | Bin 2518624 -> 2518992 bytes Documentation/Strings-it.json | Bin 2554880 -> 2555248 bytes Documentation/Strings-ja.json | Bin 1899916 -> 1900252 bytes Documentation/Strings-ko.json | Bin 1868666 -> 1868998 bytes Documentation/Strings-nl.json | Bin 2489160 -> 2489522 bytes Documentation/Strings-pl.json | Bin 2514094 -> 2514462 bytes Documentation/Strings-pt.json | Bin 2277060 -> 2277408 bytes Documentation/Strings-ru.json | Bin 2470556 -> 2470928 bytes Documentation/Strings-sv.json | Bin 2345538 -> 2345888 bytes Documentation/Strings-tr.json | Bin 2393258 -> 2393612 bytes Documentation/Strings-zh-chs.json | Bin 2277060 -> 2277408 bytes Documentation/Strings-zh-cht.json | Bin 2277060 -> 2277408 bytes Documentation/Strings-zh-hans.json | Bin 1698534 -> 1698856 bytes Documentation/Strings-zh-hant.json | Bin 1717270 -> 1717592 bytes Documentation/Strings-zh.json | Bin 2277060 -> 2277408 bytes Extensions/Documentation.psm1 | 29 ++- Extensions/DocumentationWord.psm1 | 6 +- Extensions/EndpointManager.psm1 | 291 +++++++++++++++++++++++++--- Extensions/EndpointManagerInfo.psm1 | 14 +- Extensions/MSALAuthentication.psm1 | 98 +++++++--- Extensions/MSGraph.psm1 | 26 +-- README.md | 16 +- ReleaseNotes.md | 13 ++ Xaml/Icons/Bin.xaml | 5 + 29 files changed, 423 insertions(+), 75 deletions(-) create mode 100644 Xaml/Icons/Bin.xaml diff --git a/Documentation/Strings-cs.json b/Documentation/Strings-cs.json index 48352b00b619e66e13a390aeb014815b4307b25c..0a5703c85476c582382841a59ff9848fcead2c29 100644 GIT binary patch delta 253 zcmZozHHq=rnn{ceEsQNpEzB(}Ev#GE^vb3?RI*!4-*B0oZTf*A5w7WK`AP!QA2_jb zO*au$6qtNMLvQ+n+3af5c@DGiwNEHx17da{<^W<&Am##MZXo6XVqPEyso@7=0U#Cx zVj&p1Y)J_1&fs@d;|dC CBVGdl delta 180 zcmWm0y9$C(0DxgEGrL)t-EVf9jELxo8hZlAa5x|>L1#HH;1rF5uh3&Og=@Qmf5Qjg z_oa;J32DfRtV&bXQtJ?E=V*a4~>~ mAx0Quf+=R0V}T`BSZ77(r+fX0#7PBioF_gu5LupVufG!SQ&htM diff --git a/Documentation/Strings-de.json b/Documentation/Strings-de.json index 868877fe412bc65b89a6e67cc846f45339cd8864..7c7846058c26b57c1827110c15a1a185d265eda3 100644 GIT binary patch delta 285 zcmX>zWj5oA>9ZLdS{PfHT9{i{T3ENR1>BiFVGo2?0f9MhS^R7H4w7%~~E z7*ZMXfMm(^i`!XEr*B~6P@A4NO@(8+0;e!b`-VGgK+F!r96-zo#9Tnk4a7V^%nQVP zK+F%s0zfPX#6mzU48$TpEDFS8Kr9Z#5@sUtM$kzwpy=iW%luL}|E;y)P+{PM81y diff --git a/Documentation/Strings-en.json b/Documentation/Strings-en.json index bfc02a543f217e8f28dff2bbc8ce8dec5cb7b402..1866929d5564bd075be937ff780e1ff103be4ae0 100644 GIT binary patch delta 263 zcmX@|w~KK>LpNhX3u6mY3v&xg3+ooP7aG&&NwZl^t72!FF7QEtYxnz6ii>ZOhIz`hCOUN)9=O0IlyH(!LmSShfa4~t>_DOb9>5HIUtq?VukG~ JUlkKh0s!3jR1E+C delta 162 zcmZ3`(9L+{Zx>@j3u6mY3v&xg3+ooP7aEgme(OzdaA8+#-=N6`#Oy%K0mPg@%mu{U zK+FTgyg40S50orKA)!%0|J2(kZwvx$Mh z7!o!vfU$4^j9df+KN+6o&AUEK`RX*~zTzsPqAI4eg;;N>qiZkpoaZqbMO}lw99Pm$ ztz=PpZcF-ng_;12Ro zfFj(3v-GwP!7-3Vopv+Z)Y|Nw^=bTDu9E+4HqKE$N7iF+Y(U+QRFbUFtxx=j9E$J} NN>B!ODZ)zA`vV%JU*!M* delta 187 zcmWm8ISztA06@_U%D8Wc`-X~E#=;X=cqyqUCZRPkaSATL0ZiyT0I`GG+CymcQ@rjM z|M3e&@(G2ngfdh?6_t6HNvbbrvA_C;rC)DpYe$;RAj%|4D5HWZYN(@uylJ704lHzG qqlZ2Q7-EDmCYWM|ITmo@C|lmX`uKDY`9$EQE3DyS6MO0QwEF{B@LEIw diff --git a/Documentation/Strings-fr.json b/Documentation/Strings-fr.json index 75a50c12d1dea50333963e86eaae400c11a27e9c..083b6092d561a2d89f42bb221dbf9c71a22cedff 100644 GIT binary patch delta 277 zcmXBOu};EJ7{&2kxGh*)xE2b6R0LZF2GYR?fR!-v04(kuy0|gigrtK3hkhaH;N-3` zz~BQg`T$KIfsqa45r^M&a+3dkov^PYVROl3PUa<->ZdkN=@8}p)vQQCQ{(jWZX2}m zJ&Tq{E~xK#f_5V|pkS?9izwBg07bX}9WKED6G~790ax(%sX!HMs6idBp#e>}ffhJ$ z3vIZA&NfQBZ;ZZQYQuAwI#r&OfI26)LBX>nXerELJmEb4u=T=uR32$mwkz>#)u6e_ ZbUN(ahyVC6>n_6V9$fIYVb+@tegRD~bl3m@ delta 182 zcmWm0ISRr+06o)g#X!K;Vhl`~BM52iY&?R6A!qOgev1e1 zVXb(@R-Coa1zpr7?cC+=bA9habBrC=oW9aChi6cUR3=fz|5embM*~f?&_)Mcxah${ j9|H_A!Wa`wF~b}SETOPURpwtrl;l3v2(U?$e0vLj>~c$3 diff --git a/Documentation/Strings-hu.json b/Documentation/Strings-hu.json index a1095ec6871d8e884e2dba2fa59c46ace04adfcb..cf0f366ff667a2fdebd6ce044e53815c28060e5a 100644 GIT binary patch delta 249 zcmaEGXC~u?`!g9ES{PfHT9{i{T3ENRIh>rflFe$m!(Szy=_Q&fV$&!5X5*Xu<-6VF z0w$sMhLdbS%nrmHK+FlmTtLhX#5_RE3&eat%n!r@Kr9HvAU(oBECR%$Kr9Bt;y^3` z#F9WP1;o-oECa-{Kr9Eu@<6Pxz2T&yN9XhgZ+SbgYdBLGLKrF;3K&wSGn%l8PhSwu z%riaaD~sKm+eSi$ns56+a*oxH@(2<%{40i`d=t5iVBJ?wIwE+*rBs&I06Sphnm37>S7qagoK2_H0C9+vpQ&C z#j#U8gdTuHN7Bg$2Zw9AmwUWh@k*&-7`H(n35h4VD<} zHu!9?(;&}ax?KQ^$uw_PwP`KV9McmF*;v}GtXYAW4T#x+m;;D8ftU-3xq+AmhnSYo@CwdAS=)Ajzd+8`Xl8p2S?P%!}1oLn7}gGfk|k3oj0q<^ng}rj`l^?tU$~L z#Oy!};&TEq7Z7s;F%J;)0x=&D^8>K}5DNma5D*Iku?P^00lREd YVle{Q(_V2`5{RXMSbBTKU6~X`0Fa3~d;kCd diff --git a/Documentation/Strings-ko.json b/Documentation/Strings-ko.json index ae3b73345e28804d60d16e0f4e69280077a2fe6f..a7260d893b3bdda1b7d3a35fce8e67d6e0607f69 100644 GIT binary patch delta 222 zcmey>R&=bhsG)_ig{g(Pg=Gt?gVyvPzgeuN{gC3D?$XCAG`;68KNqX$wY!IuCKod4 zPrq=DS*X21ixr63fS4VKIe?fGh`E568;E&;m=}oofS4bM1%Ox(h(Y>U6`cEaKA*7BF#4uR9{_ m0CE~DSZey*qr$S&3l2ybL7Y{QC)r++Ck4dP+bi;9m_z{!eol`7 delta 159 zcmX@sS@f%|sG)_ig{g(Pg=Gt?gVyx2MQmCV6IiBSxW+6r{hk(^$n*!Nr8wHzv{`|e z4T#x+7{uoUVlE)&24Wr{<^^ItAm#^R0U#CxVj&D6g=t>!>ePKAi&p>OjMi%-Bf%@EwZE`gajN2 z1QNA7;0l(I;Pw*)@u=6lrz=V88%d6kL_|b|ES@&XPe9d0wBwFdZ?%)DuUVWX$GT2F zw+yfS*Cl(eH0S3)ff%R|hi6Ct2Mv;t0v*y|KnAjqgFF=A1zw>DC3u5(C_@FR+jC(0 zJ#x7E`B;h#*o@h1N~4cRiw?TprznMQY|O)p2{ovLwGA%~KlcwL C+HZLP delta 178 zcmXBNxd{S67(ii0*YUnx9dFkomBq{^tYr%@F!*5$Fc`1}2Llnd1F@6oKrr)(!52Q> z{bSYZZPiJX8$Kr8^nfq*~EC$5lKr8{ol0Ym4#L_@41H`gGEC!P zubXu?mMNH`u}$ge9MdE|x%+EuC-2PmlKAWNgd@tr6%|nxHBlE0(G)Gw79HVJ=5iLsc7shEkmSO{M%Q-YaEBHumyCzmK(<<_}PZc7yIKEV$&azEDq diff --git a/Documentation/Strings-pt.json b/Documentation/Strings-pt.json index bfc02a543f217e8f28dff2bbc8ce8dec5cb7b402..1866929d5564bd075be937ff780e1ff103be4ae0 100644 GIT binary patch delta 263 zcmX@|w~KK>LpNhX3u6mY3v&xg3+ooP7aG&&NwZl^t72!FF7QEtYxnz6ii>ZOhIz`hCOUN)9=O0IlyH(!LmSShfa4~t>_DOb9>5HIUtq?VukG~ JUlkKh0s!3jR1E+C delta 162 zcmZ3`(9L+{Zx>@j3u6mY3v&xg3+ooP7aEgme(OzdaA8+#-=N6`#Oy%K0mPg@%mu{U zK+FTgygm?E*^_PjF3F@DO#Fo_9i- zhc$$ulA&PwMIUC#=?6;K{3a%_ARG;ol#$Y(KCf2UXmSISP`k`}MIcrJVr3v!*)FqQ HwSgG`?CDle delta 177 zcmW;FISRr+7(ihr(Wv9T@0u8GEj(FHAXEI1c0foPJ9B_3gf!Nk!dCDYJ}tiT@$4s4 z*LS9jDDlLXid65x!rJr%Z^nS*M7RDB)7UmX~7S=6nH38Gt1j}1Y7kJCUGTq^$BG2?=+)6^zGk~ng zPu`eKKd_!fXu4eiyGZ+m05%|I2VxE&<^*CcAm#>Q9w6ogVm=_|2Vwyr76f7;AQlE< zkX}(B76W2&AeI1PNg$R2Vrd|j0b*GomfLonoX(i%z(*-^$^09|7R5BDWq)xAY zr6@lA-%~cO>Fbo(tR^O~Ae_q$Rl^XSWw)AlE&n>F>wGB8xvaM8ZJTN0)7^! zJa;M7`%}k gSYU}2*4SW+0J}0t!|En+pbxpJLl{J*srT9E2PCRD!~g&Q diff --git a/Documentation/Strings-tr.json b/Documentation/Strings-tr.json index 6846c4e9c8a88a197fef23648fd82b7fd376aa2b..316cd22a375b65be250d340ad0f7b72d5ae8424c 100644 GIT binary patch delta 254 zcmZ40If=2SXA)yW3u6mY3v&xg3+ooPfV}DFa@nk=fAA4una;ODiEFw`vXTI=5`zyz zCPOhpB10ZS@#LBBt*5PH5o({1#|FggK+FNeoIuP4#N0s41H`;Q%m>8$Kr8^nfNy)MGoORfibF12Ev9vf`3MW1bU*kzA>1{`q65yzZx${FWeFyxXe buHU7OK56kis@?4&1M4E|Hr8!lF7|(a12{@A diff --git a/Documentation/Strings-zh-chs.json b/Documentation/Strings-zh-chs.json index bfc02a543f217e8f28dff2bbc8ce8dec5cb7b402..1866929d5564bd075be937ff780e1ff103be4ae0 100644 GIT binary patch delta 263 zcmX@|w~KK>LpNhX3u6mY3v&xg3+ooP7aG&&NwZl^t72!FF7QEtYxnz6ii>ZOhIz`hCOUN)9=O0IlyH(!LmSShfa4~t>_DOb9>5HIUtq?VukG~ JUlkKh0s!3jR1E+C delta 162 zcmZ3`(9L+{Zx>@j3u6mY3v&xg3+ooP7aEgme(OzdaA8+#-=N6`#Oy%K0mPg@%mu{U zK+FTgygLpNhX3u6mY3v&xg3+ooP7aG&&NwZl^t72!FF7QEtYxnz6ii>ZOhIz`hCOUN)9=O0IlyH(!LmSShfa4~t>_DOb9>5HIUtq?VukG~ JUlkKh0s!3jR1E+C delta 162 zcmZ3`(9L+{Zx>@j3u6mY3v&xg3+ooP7aEgme(OzdaA8+#-=N6`#Oy%K0mPg@%mu{U zK+FTgygFEi59tr!QL}W-)!j8$Ool`>G^(rhmy`7MdQH!N)dP;eg3> zhYhT1)7LFxu-%K+Fcj>_E%`#GF9P1;pGy3{t}j#C$-^55xjMEC|FxKr9Tz zB0wyFEi59tC#$^Do2a{@6J5OV`D y4-oSLF&_}~1F--Q3j#4nuP_jc0I}%yUAx6r#7$ge)LtMh4#X1M3#27?TmS%W6F9X1 diff --git a/Documentation/Strings-zh-hant.json b/Documentation/Strings-zh-hant.json index 404e2bc46cbd9095b3d02e065221f0d641b7f52f..661e0bc62c494d42d55ce5952b2554c1d196c2e9 100644 GIT binary patch delta 215 zcmbRCBICxZjD{A*7N!>FEi4u%r;F`nv6_BiG9SzIf`bxV(^EEx3$b@3b7jZP=$`)Y zyQJY{1ty{CS_fD~rmt&d=4+pIk_CuaftU@5*@2h?h&h3n3y8Uamc~j1pwsES_J?A delta 129 zcmccdDr4GFEi4u%Coa;PtiU8Rz3%|4Nc)GAEI`Z(#B4y!4#XTl%n8I? zK+FxqJV49~#C$-^55xjMEC|FxKr9TzATva_e>f>-G-2|RHAWz#+g+l?fmmX@OSGiO FEdZr~Kwkg= diff --git a/Documentation/Strings-zh.json b/Documentation/Strings-zh.json index bfc02a543f217e8f28dff2bbc8ce8dec5cb7b402..1866929d5564bd075be937ff780e1ff103be4ae0 100644 GIT binary patch delta 263 zcmX@|w~KK>LpNhX3u6mY3v&xg3+ooP7aG&&NwZl^t72!FF7QEtYxnz6ii>ZOhIz`hCOUN)9=O0IlyH(!LmSShfa4~t>_DOb9>5HIUtq?VukG~ JUlkKh0s!3jR1E+C delta 162 zcmZ3`(9L+{Zx>@j3u6mY3v&xg3+ooP7aEgme(OzdaA8+#-=N6`#Oy%K0mPg@%mu{U zK+FTgyg function Get-ModuleVersion { - '3.1.7' + '3.1.8' } function Invoke-InitializeModule @@ -83,7 +83,7 @@ function Invoke-InitializeModule Description = "Manages Intune environments. This view can be used for copying objects in an Intune environment. It can also be used for backing up an entire Intune environment and cloning the Intune environment into another tenant." ID="IntuneGraphAPI" ViewPanel = $viewPanel - ItemChanged = { Show-GraphObjects; Write-Status ""} + ItemChanged = { Show-GraphObjects; Invoke-ModuleFunction "Invoke-GraphObjectsChanged"; Write-Status ""} Deactivating = { Invoke-EMDeactivateView } Activating = { Invoke-EMActivatingView } Authentication = (Get-MSALAuthenticationObject) @@ -172,9 +172,11 @@ function Invoke-InitializeModule PostGetCommand = { Start-PostGetIntuneBranding @args } PostExportCommand = { Start-PostExportIntuneBranding @args } PreDeleteCommand = { Start-PreDeleteIntuneBranding @args } + PreUpdateCommand = { Start-PreUpdateIntuneBranding @args } Permissons=@("DeviceManagementApps.ReadWrite.All") Icon = "Branding" SkipRemoveProperties = @('Id') # Id is removed by PreImport. Required for default profile + PropertiesToRemoveForUpdate = @('isDefaultProfile','disableClientTelemetry') GroupId = "TenantAdmin" }) @@ -212,10 +214,12 @@ function Invoke-InitializeModule PreReplaceCommand = { Start-PreReplaceEnrollmentRestrictions @args } # Note: Uses same PreReplaceCommand as restrictions PostReplaceCommand = { Start-PostReplaceEnrollmentRestrictions @args } # Note: Uses same PostReplaceCommand as restrictions PreFilesImportCommand = { Start-PreFilesImportEnrollmentRestrictions @args } # Note: Uses same PreFilesImportCommand as restrictions + #PreUpdateCommand = { Start-PreUpdateEnrollmentRestrictions @args } # Note: Uses same PreUpdateCommand as restrictions QUERYLIST = "`$filter=endsWith(id,'Windows10EnrollmentCompletionPageConfiguration')" Permissons=@("DeviceManagementServiceConfig.ReadWrite.All") SkipRemoveProperties = @('Id') AssignmentsType = "enrollmentConfigurationAssignments" + PropertiesToRemoveForUpdate = @('priority') GroupId = "WinEnrollment" }) @@ -231,6 +235,8 @@ function Invoke-InitializeModule PreReplaceCommand = { Start-PreReplaceEnrollmentRestrictions @args } PostReplaceCommand = { Start-PostReplaceEnrollmentRestrictions @args } PreFilesImportCommand = { Start-PreFilesImportEnrollmentRestrictions @args } + #PreUpdateCommand = { Start-PreUpdateEnrollmentRestrictions @args } + PropertiesToRemoveForUpdate = @('priority') Permissons=@("DeviceManagementServiceConfig.ReadWrite.All") SkipRemoveProperties = @('Id') AssignmentsType = "enrollmentConfigurationAssignments" @@ -292,6 +298,8 @@ function Invoke-InitializeModule Icon="CustomAttributes" GroupId = "CustomAttributes" # MacOS Settings DetailExtension = { Add-ScriptExtensions @args } + PropertiesToRemoveForUpdate = @('customAttributeName','customAttributeType','displayName') + #PreUpdateCommand = { Start-PreUpdateMacCustomAttributes @args } }) Add-ViewItem (New-Object PSObject -Property @{ @@ -316,8 +324,10 @@ function Invoke-InitializeModule PreImportCommand = { Start-PreImportAppProtection @args } PostImportCommand = { Start-PostImportAppProtection @args } PreImportAssignmentsCommand = { Start-PreImportAssignmentsAppProtection @args } + PreUpdateCommand = { Start-PreUpdateAppProtection @args } ExportFullObject = $true PropertiesToRemove = @('exemptAppLockerFiles') + PropertiesToRemoveForUpdate = @("protectedAppLockerFiles","version") # ToDo: !!! Add support for protectedAppLockerFiles? Permissons=@("DeviceManagementApps.ReadWrite.All") Dependencies = @("Applications") GroupId = "AppProtection" @@ -334,6 +344,7 @@ function Invoke-InitializeModule PreImportCommand = { Start-PreImportAppProtection @args } PostImportCommand = { Start-PostImportAppProtection @args } PreImportAssignmentsCommand = { Start-PreImportAssignmentsAppProtection @args } + PreUpdateCommand = { Start-PreUpdateAppConfigurationApp @args } Permissons=@("DeviceManagementApps.ReadWrite.All") Dependencies = @("Applications") Icon = "AppConfiguration" @@ -369,6 +380,7 @@ function Invoke-InitializeModule Expand="categories,assignments" # ODataMetadata is set to minimal so assignments can't be autodetected ODataMetadata="minimal" # categories property not supported with ODataMetadata full PostFileImportCommand = { Start-PostFileImportApplications @args } + PreUpdateCommand = { Start-PreUpdateApplication @args } GroupId = "Apps" }) @@ -392,6 +404,7 @@ function Invoke-InitializeModule Expand = "Items" PreImportAssignmentsCommand = { Start-PreImportAssignmentsPolicySets @args } PreImportCommand = { Start-PreImportPolicySets @args } + PreUpdateCommand = { Start-PreUpdatePolicySets @args } Permissons=@("DeviceManagementConfiguration.ReadWrite.All") ImportOrder = 2000 # Policy Sets reference other objects so make sure it is imported last Dependencies = @("Applications","AppConfiguration","AppProtection","AutoPilot","EnrollmentRestrictions","EnrollmentStatusPage","DeviceConfiguration","AdministrativeTemplates","SettingsCatalog","CompliancePolicies") @@ -407,6 +420,7 @@ function Invoke-InitializeModule #ExportFullObject = $false Permissons=@("DeviceManagementConfiguration.ReadWrite.All") GroupId = "WinUpdatePolicies" + PropertiesToRemoveForUpdate = @('version','qualityUpdatesPauseStartDate','featureUpdatesPauseStartDate','qualityUpdatesWillBeRolledBack','featureUpdatesWillBeRolledBack') }) Add-ViewItem (New-Object PSObject -Property @{ @@ -416,6 +430,8 @@ function Invoke-InitializeModule API = "/deviceManagement/windowsFeatureUpdateProfiles" Permissons=@("DeviceManagementConfiguration.ReadWrite.All") GroupId = "WinFeatureUpdates" + PropertiesToRemoveForUpdate = @('deployableContentDisplayName','endOfSupportDate') + #PreUpdateCommand = { Start-PreUpdateFeatureUpdates @args } }) Add-ViewItem (New-Object PSObject -Property @{ @@ -426,6 +442,7 @@ function Invoke-InitializeModule Permissons=@("DeviceManagementConfiguration.ReadWrite.All") Icon = "UpdatePolicies" GroupId = "WinQualityUpdates" + PropertiesToRemoveForUpdate = @('releaseDateDisplayName','deployableContentDisplayName') }) # Locations are not FULLY supported @@ -459,6 +476,7 @@ function Invoke-InitializeModule Expand="Settings" Icon="DeviceConfiguration" PostExportCommand = { Start-PostExportSettingsCatalog @args } + PreUpdateCommand = { Start-PreUpdateSettingsCatalog @args } GroupId = "DeviceConfiguration" }) @@ -474,6 +492,7 @@ function Invoke-InitializeModule Permissons=@("DeviceManagementRBAC.ReadWrite.All") ImportOrder = 20 #expand=roleassignments + PropertiesToRemoveForUpdate = @('isBuiltInRoleDefinition','isBuiltIn','roleAssignments') ### !!! ToDo: Add support for roleAssignments GroupId = "TenantAdmin" }) @@ -500,6 +519,7 @@ function Invoke-InitializeModule PreImportCommand = { Start-PreImportNotifications @args } PostFileImportCommand = { Start-PostFileImportNotifications @args } PostCopyCommand = { Start-PostCopyNotifications @args } + PropertiesToRemoveForUpdate = @('defaultLocale','localizedNotificationMessages') ### !!! ToDo: Add support for localizedNotificationMessages GroupId = "CompliancePolicies" }) @@ -527,6 +547,7 @@ function Invoke-InitializeModule ViewID = "IntuneGraphAPI" API = "/deviceManagement/appleUserInitiatedEnrollmentProfiles" Permissons=@("DeviceManagementServiceConfig.ReadWrite.All") + PropertiesToRemoveForUpdate = @('platform') GroupId = "AppleEnrollment" }) @@ -538,6 +559,7 @@ function Invoke-InitializeModule Permissons=@("DeviceManagementConfiguration.ReadWrite.All") ImportOrder = 15 GroupId = "TenantAdmin" + PropertiesToRemoveForUpdate = @('platform') }) Add-ViewItem (New-Object PSObject -Property @{ @@ -547,10 +569,13 @@ function Invoke-InitializeModule QUERYLIST = "`$filter=isGlobalScript%20eq%20false" # Looks like filters are not working for deviceHealthScripts API = "/deviceManagement/deviceHealthScripts" PreDeleteCommand = { Start-PreDeleteDeviceHealthScripts @args } + PreImportCommand = { Start-PreImportDeviceHealthScripts @args } + PreUpdateCommand = { Start-PreUpdateDeviceHealthScripts @args } Permissons=@("DeviceManagementConfiguration.ReadWrite.All") GroupId = "EndpointAnalytics" Icon = "Report" AssignmentsType = "deviceHealthScriptAssignments" + PropertiesToRemoveForUpdate = @('version','isGlobalScript','highestAvailableVersion') }) } @@ -694,6 +719,7 @@ function Set-EMViewPanel # ToDo: Move this to view view object $txtFilter = $this.Parent.FindName("txtFilter") if($txtFilter) { $txtFilter.Text = "" } + Show-GraphObjects Write-Status "" }) @@ -783,7 +809,7 @@ function Start-PostExportEndpointSecurity param($obj, $objectType, $path) $settings = Invoke-GraphRequest -Url "$($objectType.API)/$($obj.id)/settings" - $settingsJson = "{ `"settings`": $((ConvertTo-Json $settings.value -Depth 10 ))`n}" + $settingsJson = "{ `"settings`": $((ConvertTo-Json $settings.value -Depth 20 ))`n}" $fileName = "$path\$((Remove-InvalidFileNameChars (Get-GraphObjectName $obj $objectType)))_Settings.json" $settingsJson | Out-File -LiteralPath $fileName -Force } @@ -796,7 +822,7 @@ function Start-PostFileImportEndpointSecurity if($settings) { Start-GraphPreImport $settings - Invoke-GraphRequest -Url "$($objectType.API)/$($obj.id)/updateSettings" -Body ($settings | ConvertTo-Json -Depth 10) -Method "POST" + Invoke-GraphRequest -Url "$($objectType.API)/$($obj.id)/updateSettings" -Body ($settings | ConvertTo-Json -Depth 20) -Method "POST" } } @@ -824,7 +850,7 @@ function Start-PostCopyEndpointSecurity if($settings) { $settingsObj = New-object PSObject @{ "Settings" = $settings.Value } - Invoke-GraphRequest -Url "$($objectType.API)/$($objNew.id)/updateSettings" -Body ($settingsObj | ConvertTo-Json -Depth 10) -Method "POST" + Invoke-GraphRequest -Url "$($objectType.API)/$($objNew.id)/updateSettings" -Body ($settingsObj | ConvertTo-Json -Depth 20) -Method "POST" } } @@ -861,7 +887,7 @@ function Start-PreUpdateEndpointSecurity $tmpObj = [PSCustomObject]@{ settings = $curValues } - $json = ConvertTo-Json $tmpObj -Depth 10 + $json = ConvertTo-Json $tmpObj -Depth 20 # Set all existing values to null # Note: This will not remove them from the configured list just set them Not Configured @@ -874,7 +900,7 @@ function Start-PreUpdateEndpointSecurity } Start-GraphPreImport $tmpObj.settings - $json = ConvertTo-Json $tmpObj -Depth 10 + $json = ConvertTo-Json $tmpObj -Depth 20 Invoke-GraphRequest -Url $strAPI -Content $json -HttpMethod "POST" | Out-Null Remove-Property $obj "templateId" @@ -897,7 +923,7 @@ function Start-PostFileImportDeviceConfiguration $privacyObj = [PSCustomObject]@{ windowsPrivacyAccessControls = $tmpObj.privacyAccessControls } - $json = $privacyObj | ConvertTo-Json -Depth 10 + $json = $privacyObj | ConvertTo-Json -Depth 20 $ret = Invoke-GraphRequest -Url "deviceManagement/deviceConfigurations('$($obj.Id)')/windowsPrivacyAccessControls" -Body $json -Method "POST" } } @@ -914,7 +940,7 @@ function Start-PostCopyDeviceConfiguration $privacyObj = [PSCustomObject]@{ windowsPrivacyAccessControls = $objCopyFrom.privacyAccessControls } - $json = $privacyObj | ConvertTo-Json -Depth 10 + $json = $privacyObj | ConvertTo-Json -Depth 20 $ret = Invoke-GraphRequest -Url "deviceManagement/deviceConfigurations('$($objNew.Id)')/windowsPrivacyAccessControls" -Body $json -Method "POST" } } @@ -949,7 +975,7 @@ function Start-PreUpdateCompliancePolicies deviceComplianceScheduledActionForRules = $obj.scheduledActionsForRule } - $json = ConvertTo-Json $tmpObj -Depth 10 + $json = ConvertTo-Json $tmpObj -Depth 20 Invoke-GraphRequest -Url $strAPI -Content $json -HttpMethod "POST" | Out-Null Remove-Property $obj "scheduledActionsForRule" @@ -994,7 +1020,7 @@ function Start-PreImportIntuneBranding # Create a new profile with basic info # Patch the profile with all the info - $global:brandingClone = $obj | ConvertTo-Json -Depth 10 | ConvertFrom-Json + $global:brandingClone = $obj | ConvertTo-Json -Depth 20 | ConvertFrom-Json foreach($prop in ($obj.PSObject.Properties | Where {$_.Name -notin @("profileName","profileDescription","roleScopeTagIds")})) #"customPrivacyMessage" { @@ -1014,7 +1040,7 @@ function Start-PostImportIntuneBranding { Remove-Property $global:brandingClone $prop } - $json = ($global:brandingClone | ConvertTo-Json -Depth 10) + $json = ($global:brandingClone | ConvertTo-Json -Depth 20) $ret = Invoke-GraphRequest -Url "$($objectType.API)/$($obj.Id)" -Body $json -Method "PATCH" } @@ -1057,6 +1083,19 @@ function Start-PreDeleteIntuneBranding } } +function Start-PreUpdateIntuneBranding +{ + param($obj, $objectType, $curObject, $fromObj) + + if($curObject.Object.isDefaultProfile) + { + foreach($prop in @("profileName","isDefaultProfile","disableClientTelemetry","profileDescription")) + { + Remove-Property $obj $prop + } + } +} + #endregion #region Azure Branding functions @@ -1305,7 +1344,7 @@ function Start-PostImportAppProtection $tmp = $newObject."@odata.type".Split('.')[-1] $objectClass = Get-GraphObjectClassName $tmp - Invoke-GraphRequest -Url "/deviceAppManagement/$objectClass/$($obj.Id)/targetApps" -Content "{ apps: $(ConvertTo-Json $global:ImportObjectInfo.Apps -Depth 10)}" -HttpMethod POST | Out-Null + Invoke-GraphRequest -Url "/deviceAppManagement/$objectClass/$($obj.Id)/targetApps" -Content "{ apps: $(ConvertTo-Json $global:ImportObjectInfo.Apps -Depth 20)}" -HttpMethod POST | Out-Null } catch {} } @@ -1322,6 +1361,71 @@ function Start-PreImportAssignmentsAppProtection @{"API"="/deviceAppManagement/$($global:ImportObjectClass)/$($obj.Id)/assign"} } } + +function Start-PreUpdateAppConfigurationApp +{ + param($obj, $objectType, $curObject, $fromObj) + + if($obj.Apps) + { + try + { + Write-Log "Update App Configuruation Apps" + + $json = [PSCustomObject]@{ apps = @($obj.Apps) } | ConvertTo-Json -Depth 10 + $objectClass = 'targetedManagedAppConfigurations' #!!!Get-GraphObjectClassName $obj + + Invoke-GraphRequest -Url "/deviceAppManagement/$objectClass/$($curObject.Object.Id)/targetApps" -Content $json -HttpMethod POST | Out-Null + } + catch {} + } + + Remove-Property $obj "apps" +} + +function Start-PreUpdateAppProtection +{ + param($obj, $objectType, $curObject, $fromObj) + + if($curObject.Object.'@OData.Type' -eq "#microsoft.graph.windowsInformationProtectionPolicy") + { + $api = "/deviceAppManagement/windowsInformationProtectionPolicies/$($curObject.Object.Id)" + } + elseif($curObject.Object.'@OData.Type' -eq "#microsoft.graph.mdmWindowsInformationProtectionPolicy") + { + $api = "/deviceAppManagement/mdmWindowsInformationProtectionPolicies/$($curObject.Object.Id)" + } + elseif($curObject.Object.'@OData.Type' -eq "#microsoft.graph.iosManagedAppProtection") + { + $api = "/deviceAppManagement/iosManagedAppProtections/$($curObject.Object.Id)" + } + elseif($curObject.Object.'@OData.Type' -eq "#microsoft.graph.androidManagedAppProtection") + { + $api = "/deviceAppManagement/androidManagedAppProtections/$($curObject.Object.Id)" + } + else + { + return (Start-PreUpdateAppConfigurationApp $obj $objectType $curObject $fromObj) + } + + if($obj.Apps) + { + try + { + Write-Log "Update App Protection Apps" + + $json = [PSCustomObject]@{ apps = @($obj.Apps) } | ConvertTo-Json -Depth 10 + + Invoke-GraphRequest -Url "$api/targetApps" -Content $json -HttpMethod POST | Out-Null + } + catch {} + + Remove-Property $obj "apps" + } + + @{ "API" = $api } + +} #endregion #region App Configuration @@ -1397,6 +1501,26 @@ function Start-PostFileImportApplications Write-Log "Unsupported application type $appType. File will not be uploaded" 2 } } + +function Start-PreUpdateApplication +{ + param($obj, $objectType, $curObject, $fromObj) + + if($curObject.Object.'@OData.type' -eq "#microsoft.graph.windowsMobileMSI") + { + Remove-Property $obj "useDeviceContext" + } + elseif($curObject.Object.'@OData.type' -eq "#microsoft.graph.officeSuiteApp") + { + Remove-Property $obj "officeConfigurationXml" + Remove-Property $obj "officePlatformArchitecture" + Remove-Property $obj "developer" + Remove-Property $obj "owner" + Remove-Property $obj "publisher" + } + + Remove-Property $obj "appStoreUrl" +} #endregion #region Group Policy/Administrative Templates functions @@ -1459,7 +1583,7 @@ function Import-GPOSetting Start-GraphPreImport $setting # Import each setting for the Administrative Template profile - Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations/$($obj.id)/definitionValues" -Content (ConvertTo-Json $setting -Depth 10) -HttpMethod POST | Out-Null + Invoke-GraphRequest -Url "/deviceManagement/groupPolicyConfigurations/$($obj.id)/definitionValues" -Content (ConvertTo-Json $setting -Depth 20) -HttpMethod POST | Out-Null } } } @@ -1471,7 +1595,7 @@ function Start-PostExportAdministrativeTemplate # Collect and save all the settings of the Administrative Templates profile $settings = Get-GPOObjectSettings $obj $fileName = "$path\$((Remove-InvalidFileNameChars (Get-GraphObjectName $obj $objectType)))_Settings.json" - ConvertTo-Json $settings -Depth 10 | Out-File -LiteralPath $fileName -Force + ConvertTo-Json $settings -Depth 20 | Out-File -LiteralPath $fileName -Force } function Start-PostCopyAdministrativeTemplate @@ -1573,6 +1697,58 @@ function Start-PreImportPolicySets } } +function Start-PreUpdatePolicySets +{ + param($obj, $objectType, $curObject, $fromObj) + + Start-PreImportPolicySets $obj $objectType + + $curObject = Get-GraphObject $curObject.Object $objectType + + # Update ref object in the json + # Used when importing in a different environment + $jsonObj = ConvertTo-Json $obj -Depth 15 + $updateObj = Update-JsonForEnvironment $jsonObj | ConvertFrom-Json + + $addedItems = @() + $updatedItems = @() + $deletedItems = @() + + foreach($item in $updateObj.items) + { + if(($curObject.Object.items | Where payloadId -eq $item.payloadId)) + { + $updatedItems += $item + } + else + { + $addedItems += $item + } + } + + foreach($item in $curObject.Object.items) + { + if(-not ($updateObj.Items | Where payloadId -eq $item.payloadId)) + { + $deletedItems += $item.id + } + } + + $updateItemObj = [PSCustomObject]@{ + addedPolicySetItems = $addedItems + deletedPolicySetItems = $deletedItems + updatedPolicySetItems = $updatedItems + } + + Write-Log "Update Policy Set items. Add: $($addedItems.Count), Update: $($updatedItems.Count), Delete: $($deletedItems.Count)" + + $updateApi = "/deviceAppManagement/policySets/$($curObject.Object.Id)/update" + $json = $updateItemObj | ConvertTo-Json -Depth 15 + + Invoke-GraphRequest -Url $updateApi -HttpMethod "POST" -Content $json + Remove-Property $obj "items" +} + function Update-EMPolicySetAssignment { param($assignment, $sourceObject, $newObject, $objectType) @@ -1595,8 +1771,8 @@ function Update-EMPolicySetAssignment $api = "/deviceAppManagement/policySets/$($assignment.SourceId)/update" - $curItemClone = $curItem | ConvertTo-Json -Depth 10 | ConvertFrom-Json - $newItem = $curItem | ConvertTo-Json -Depth 10 | ConvertFrom-Json + $curItemClone = $curItem | ConvertTo-Json -Depth 20 | ConvertFrom-Json + $newItem = $curItem | ConvertTo-Json -Depth 20 | ConvertFrom-Json $newItem.payloadId = $newObject.Id if($newItem.guidedDeploymentTags -is [String] -and [String]::IsNullOrEmpty($newItem.guidedDeploymentTags)) { @@ -1617,7 +1793,7 @@ function Update-EMPolicySetAssignment $update.Add('updatedPolicySetItems', @()) $update.Add('deletedPolicySetItems',@($curItemClone.Id)) - $json = $update | ConvertTo-Json -Depth 10 + $json = $update | ConvertTo-Json -Depth 20 Write-Log "Update PolicySet $($psObj.displayName) - Replace: $((Get-GraphObjectName $newObject $objectType))" @@ -1668,7 +1844,7 @@ function Start-PostExportRoleDefinitions if($roleAssignmentsArr.Count -gt 0) { $tmpObj.RoleAssignments = $roleAssignmentsArr - $tmpObj | ConvertTo-Json -Depth 10 | Out-File -LiteralPath $fileName + $tmpObj | ConvertTo-Json -Depth 20 | Out-File -LiteralPath $fileName } } } @@ -1711,7 +1887,7 @@ function Start-PostFileImportRoleDefinitions } # This will update GroupIds - $json = Update-JsonForEnvironment (ConvertTo-Json $roleAssignmentObj -Depth 10) + $json = Update-JsonForEnvironment (ConvertTo-Json $roleAssignmentObj -Depth 20) Write-Log "Import Role Assignments" Invoke-GraphRequest -Url "/deviceManagement/roleAssignments" -Body $json -Method "POST" @@ -1728,6 +1904,13 @@ function Start-PostExportSettingsCatalog Add-EMAssignmentsToExportFile $obj $objectType $path } +function Start-PreUpdateSettingsCatalog +{ + param($obj, $objectType, $curObject, $fromObj) + + @{"Method"="PUT"} +} + #endregion #region Notification functions @@ -1749,7 +1932,7 @@ function Start-PostFileImportNotifications foreach($localizedNotificationMessage in $tmpObj.localizedNotificationMessages) { Start-GraphPreImport $localizedNotificationMessage $objectType - Invoke-GraphRequest -Url "$($objectType.API)/$($obj.id)/localizedNotificationMessages" -Body ($localizedNotificationMessage | ConvertTo-Json -Depth 10) -Method "POST" + Invoke-GraphRequest -Url "$($objectType.API)/$($obj.id)/localizedNotificationMessages" -Body ($localizedNotificationMessage | ConvertTo-Json -Depth 20) -Method "POST" } } @@ -1760,7 +1943,7 @@ function Start-PostCopyNotifications foreach($localizedNotificationMessage in $objCopyFrom.localizedNotificationMessages) { Start-GraphPreImport $localizedNotificationMessage $objectType - Invoke-GraphRequest -Url "$($objectType.API)/$($objNew.id)/localizedNotificationMessages" -Body ($localizedNotificationMessage | ConvertTo-Json -Depth 10) -Method "POST" + Invoke-GraphRequest -Url "$($objectType.API)/$($objNew.id)/localizedNotificationMessages" -Body ($localizedNotificationMessage | ConvertTo-Json -Depth 20) -Method "POST" } } #endregion @@ -1851,7 +2034,7 @@ function Start-PostReplaceEnrollmentRestrictions $priority = [PSCustomObject]@{ priority = $sourceObj.Priority } - $json = $priority | ConvertTo-Json -Depth 10 + $json = $priority | ConvertTo-Json -Depth 20 Write-Log "Update priority for $($obj.displayName) to $($sourceObj.Priority)" Invoke-GraphRequest $api -HttpMethod "POST" -Content $json @@ -1864,6 +2047,13 @@ function Start-PreFilesImportEnrollmentRestrictions $filesToImport | sort-object -property @{e={$_.Object.priority}} } +function Start-PreUpdateEnrollmentRestrictions +{ + param($obj, $objectType, $curObject, $fromObj) + + Remove-Property $obj "priority" +} + #endregion #region ScopeTags @@ -1916,12 +2106,32 @@ function Start-PreDeleteDeviceHealthScripts { param($obj, $objectType) - if($obj.IsGlobal -eq $true) + if($obj.isGlobalScript -eq $true) { @{ "Delete" = $false } } } +function Start-PreImportDeviceHealthScripts +{ + param($obj, $objectType, $file, $assignments) + + if($obj.isGlobalScript -eq $true) + { + @{ "Import" = $false } + } +} + +function Start-PreUpdateDeviceHealthScripts +{ + param($obj, $objectType, $curObject, $fromObj) + + if($curObject.Object.isGlobalScript -eq $true) + { + @{ "Import" = $false } + } +} + #endregion #region Generic functions @@ -1944,7 +2154,7 @@ function Save-EMDefaultPolicy # Clean up from old version of the script that used the wrong name for Default policies try { [IO.File]::Delete($oldFile) | Out-Null } Catch {} } - $obj | ConvertTo-Json -Depth 10 | Out-File -LiteralPath "$path\$((Remove-InvalidFileNameChars $fileName)).json" + $obj | ConvertTo-Json -Depth 20 | Out-File -LiteralPath "$path\$((Remove-InvalidFileNameChars $fileName)).json" } } catch {} @@ -2003,7 +2213,7 @@ function Add-EMAssignmentsToExportFile { $tmpObj.Assignments = $assignments } - ConvertTo-Json $tmpObj -Depth 10 | Out-File -LiteralPath $fileName -Force + ConvertTo-Json $tmpObj -Depth 20 | Out-File -LiteralPath $fileName -Force } } @@ -2033,7 +2243,7 @@ function Add-EMAssignmentsToObject Remove-Property $assignment.target $prop.Name } - $json = Update-JsonForEnvironment ($assignment | ConvertTo-Json -Depth 10) + $json = Update-JsonForEnvironment ($assignment | ConvertTo-Json -Depth 20) Invoke-GraphRequest -Url $api -Body $json -Method "POST" | Out-Null } @{"Import"=$false} @@ -2041,4 +2251,31 @@ function Add-EMAssignmentsToObject #endregion +#region Mac Custom Scripts + +function Start-PreUpdateMacCustomAttributes +{ + param($obj, $objectType, $curObject, $fromObj) + + foreach($prop in @('customAttributeName','customAttributeType','displayName')) + { + Remove-Property $obj $prop + } +} + +#endregion + +#region Mac Feature Updates +function Start-PreUpdateFeatureUpdates +{ + param($obj, $objectType, $curObject, $fromObj) + + foreach($prop in @('deployableContentDisplayName','endOfSupportDate')) + { + Remove-Property $obj $prop + } +} +#endregion + + Export-ModuleMember -alias * -function * \ No newline at end of file diff --git a/Extensions/EndpointManagerInfo.psm1 b/Extensions/EndpointManagerInfo.psm1 index bf94e44..1109e8b 100644 --- a/Extensions/EndpointManagerInfo.psm1 +++ b/Extensions/EndpointManagerInfo.psm1 @@ -10,7 +10,7 @@ This module is for the Endpoint Info View. It shows read-only objects in Intune #> function Get-ModuleVersion { - '3.1.2' + '3.1.3' } function Invoke-InitializeModule @@ -21,7 +21,7 @@ function Invoke-InitializeModule Description = "Displays read-only information in Intune." ID = "EMInfoGraphAPI" ViewPanel = $viewPanel - ItemChanged = { Show-GraphObjects; Write-Status ""} + ItemChanged = { Show-GraphObjects; Invoke-ModuleFunction "Invoke-GraphObjectsChanged"; Write-Status ""} Activating = { Invoke-EMInfoActivatingView } Authentication = (Get-MSALAuthenticationObject) Authenticate = { Invoke-EMInfoAuthenticateToMSAL } @@ -37,7 +37,7 @@ function Invoke-InitializeModule Id = "BaselineTemplates" ViewID = "EMInfoGraphAPI" API = "/deviceManagement/templates" - ShowButtons = @("View") + ShowButtons = @("Export","View") Permissons=@("DeviceManagementConfiguration.ReadWrite.All") Icon="EndpointSecurity" }) @@ -48,7 +48,7 @@ function Invoke-InitializeModule ViewID = "EMInfoGraphAPI" ViewProperties = @("bindStatus", "lastAppSyncDateTime", "ownerUserPrincipalName") API = "/deviceManagement/androidManagedStoreAccountEnterpriseSettings" - ShowButtons = @("View") + ShowButtons = @("Export","View") Permissons=@("DeviceManagementConfiguration.ReadWrite.All") }) @@ -57,7 +57,7 @@ function Invoke-InitializeModule Id = "AndroidEnrolmentProfiles" ViewID = "EMInfoGraphAPI" API = "deviceManagement/androidDeviceOwnerEnrollmentProfiles" - ShowButtons = @("View") + ShowButtons = @("Export","View") Permissons=@("DeviceManagementConfiguration.ReadWrite.All") Icon = "AndroidCOWP" }) @@ -68,7 +68,7 @@ function Invoke-InitializeModule ViewID = "EMInfoGraphAPI" ViewProperties = @("appleId", "state", "appleId", "id") API = "/deviceAppManagement/vppTokens" - ShowButtons = @("View") + ShowButtons = @("Export","View") Permissons=@("DeviceManagementConfiguration.ReadWrite.All") }) @@ -78,7 +78,7 @@ function Invoke-InitializeModule ViewID = "EMInfoGraphAPI" ViewProperties = @("tokenName", "appleIdentifier", "tokenExpirationDateTime", "id") API = "/deviceManagement/depOnboardingSettings/?`$top=100" - ShowButtons = @("View") + ShowButtons = @("Export","View") Permissons=@("DeviceManagementServiceConfig.ReadWrite.All") }) diff --git a/Extensions/MSALAuthentication.psm1 b/Extensions/MSALAuthentication.psm1 index 77deae8..ea341be 100644 --- a/Extensions/MSALAuthentication.psm1 +++ b/Extensions/MSALAuthentication.psm1 @@ -10,7 +10,7 @@ This module manages Authentication for the application with MSAL. It is also res #> function Get-ModuleVersion { - '3.0.3' + '3.0.4' } $global:msalAuthenticator = $null @@ -837,33 +837,57 @@ function Connect-MSALUser function Disconnect-MSALUser { - param($user, [switch]$force) + param($user, [switch]$force, [switch]$PassThru) + $logout = $false + $userLoggedOut = $false if(-not $user) { + $logout = $true if(-not $global:MSALToken.Account) { return } $user = $global:MSALToken.Account # Logout current user $global:MSALToken = $null - Clear-MSALCurentUserVaiables # Only clear variables for current user + Clear-MSALCurentUserVaiables # Only clear variables for current user + $msg = "Do you want to remove the token from the cache?" + $title = "Remove token?" + } + else + { + $msg = "Are you sure you want to forget user $($user.UserName)?" #!!! + $title = "Forget user?" } # ToDo: Clear browser cache if($user -and $global:MSALApp -and (Get-SettingValue "CacheMSALToken")) { - if($force -eq $true -or [System.Windows.MessageBox]::Show("Do you want to remove the token from the cache?", "Clear cache?", "YesNo", "Question") -eq "Yes") + if($force -eq $true -or [System.Windows.MessageBox]::Show($msg, $title, "YesNo", "Question") -eq "Yes") { try { [void]$global:MSALApp.RemoveAsync($user).GetAwaiter().GetResult() + if($logout -eq $false) + { + Write-Log "User $($user.UserName) removed from cache" + } + $userLoggedOut = $true } catch { - Write-LogError "Failed to remove token from cache" $_.Exception + Write-LogError "Failed to remove $($user.UserName) from cache" $_.Exception } } } - Get-MSALUserInfo + + if($logout) + { + Get-MSALUserInfo + } + + if($PassThru -eq $true) + { + $userLoggedOut + } } function Get-MSALProfileEllipse @@ -1079,7 +1103,7 @@ function Get-MSALProfileEllipse try { ######################################################################################################### - ### Build Profile Info forcurrent user + ### Build Profile Info for current user ######################################################################################################### $global:grdProfileInfo = $null @@ -1132,35 +1156,33 @@ function Get-MSALProfileEllipse try { $grdAccount = [System.Windows.Controls.Grid]::new() + $cd = [System.Windows.Controls.ColumnDefinition]::new() - $grdAccount.ColumnDefinitions.Add($cd) + $grdAccount.ColumnDefinitions.Add($cd) # Login + $cd = [System.Windows.Controls.ColumnDefinition]::new() $cd.Width = [double]::NaN - $grdAccount.ColumnDefinitions.Add($cd) + $grdAccount.ColumnDefinitions.Add($cd) # Forget + + $grdLogin = [System.Windows.Controls.Grid]::new() + $cd = [System.Windows.Controls.ColumnDefinition]::new() + $grdLogin.ColumnDefinitions.Add($cd) + $cd = [System.Windows.Controls.ColumnDefinition]::new() + $cd.Width = [double]::NaN + $grdLogin.ColumnDefinitions.Add($cd) $icon = Get-XamlObject ($global:AppRootFolder + "\Xaml\Icons\LoggedOnUser.xaml") $icon.Width = 24 $icon.Height = 24 $icon.Margin = "0,0,5,0" - $grdAccount.Children.Add($icon) | Out-Null + $grdLogin.Children.Add($icon) | Out-Null $lbObj = [Windows.Markup.XamlReader]::Parse("$($account.UserName)$($account.HomeAccountId.TenantId)") $lbObj.SetValue([System.Windows.Controls.Grid]::ColumnProperty,1) - $grdAccount.Children.Add($lbObj) | Out-Null - - # Forget user - # Cannot be added to Grid since that is for logging on with user - # Need to rebuild the - #$icon = Get-XamlObject ($global:AppRootFolder + "\Xaml\Icons\Trash.xaml") - #$icon.Width = 24 - #$icon.Height = 24 - #$icon.Margin = "0,0,5,0" - #$icon.SetValue([System.Windows.Controls.Grid]::ColumnProperty,1) - #$icon.HorizontalAlignment = "Right" - #$grdAccount.Children.Add($icon) | Out-Null + $grdLogin.Children.Add($lbObj) | Out-Null $lnkButton = [System.Windows.Controls.Button]::new() - $lnkButton.Content = $grdAccount + $lnkButton.Content = $grdLogin $lnkButton.Style = $window.TryFindResource("LinkButton") $lnkButton.Margin = "0,5,0,0" $lnkButton.Cursor = "Hand" @@ -1177,8 +1199,36 @@ function Get-MSALProfileEllipse } Write-Status "" }) + + $grdAccount.Children.Add($lnkButton) | Out-Null + + # Add Forget user icon + $icon = Get-XamlObject ($global:AppRootFolder + "\Xaml\Icons\Bin.xaml") + $icon.Width = 16 + $icon.Height = 16 + $icon.Margin = "5,5,0,0" - AddGridObject $otherLogins $lnkButton + $lnkButton = [System.Windows.Controls.Button]::new() + $lnkButton.ToolTip = "Forget" + $lnkButton.Content = $icon + $lnkButton.Style = $window.TryFindResource("LinkButton") + $lnkButton.Margin = "0,5,0,0" + $lnkButton.Cursor = "Hand" + $lnkButton.Tag = $account + $lnkButton.SetValue([System.Windows.Controls.Grid]::ColumnProperty,1) + $lnkButton.add_Click({ + Write-Status "Logging out $($this.Tag.UserName)" + if((Disconnect-MSALUser $this.Tag -PassThru)) + { + $this.Parent.Parent.Children.Remove($this.Parent) + } + + Write-Status "" + }) + + $grdAccount.Children.Add($lnkButton) | Out-Null + + AddGridObject $otherLogins $grdAccount } catch {} } diff --git a/Extensions/MSGraph.psm1 b/Extensions/MSGraph.psm1 index 06b3cf2..657029c 100644 --- a/Extensions/MSGraph.psm1 +++ b/Extensions/MSGraph.psm1 @@ -10,7 +10,7 @@ This module manages Microsoft Grap fuctions like calling APIs, managing graph ob #> function Get-ModuleVersion { - '3.1.3' + '3.1.4' } $global:MSGraphGlobalApps = @( @@ -40,7 +40,7 @@ function Invoke-InitializeModule Value = "replace" }, [PSCustomObject]@{ - Name = "Update (Experimental)" + Name = "Update (Preview)" Value = "update" } ) @@ -1338,7 +1338,7 @@ function Import-GraphFile try { # Clone the object to keep original values - $objClone = $file.Object | ConvertTo-Json -Depth 10 | ConvertFrom-Json + $objClone = $file.Object | ConvertTo-Json -Depth 20 | ConvertFrom-Json if($objectType.PreFileImportCommand) { @@ -1391,13 +1391,18 @@ function Reset-GraphObjet $objectType = $fileObj.ObjectType # Clone the object before removing properties - $obj = $fileObj.Object | ConvertTo-Json -Depth 10 | ConvertFrom-Json + $obj = $fileObj.Object | ConvertTo-Json -Depth 20 | ConvertFrom-Json Start-GraphPreImport $obj $objectType Remove-Property $obj "Assignments" Remove-Property $obj "isAssigned" if($global:cbImportType.SelectedValue -eq "update") { + foreach($prop in $objectType.PropertiesToRemoveForUpdate) + { + Remove-Property $obj $prop + } + $params = @{} $strAPI = (?? $objectType.APIPATCH $objectType.API) + "/$($curObject.Object.Id)" $method = "PATCH" @@ -1409,7 +1414,7 @@ function Reset-GraphObjet if($ret.ContainsKey("Import") -and $ret["Import"] -eq $false) { # Import handled manually - return $false + return $true } if($ret.ContainsKey("API")) @@ -1429,7 +1434,7 @@ function Reset-GraphObjet } } - $json = ConvertTo-Json $obj -Depth 10 + $json = ConvertTo-Json $obj -Depth 15 if($true) #$global:MigrationTableCacheId -ne $global:Organization.Id) { # Call Update-JsonForEnvironment before importing the object @@ -1527,7 +1532,7 @@ function Import-GraphObjectAssignment if(($assignments | measure).Count -eq 0) { return } $preConfig = $null - $clonedAssignments = $assignments | ConvertTo-Json -Depth 10 | ConvertFrom-Json + $clonedAssignments = $assignments | ConvertTo-Json -Depth 20 | ConvertFrom-Json if($objectType.PreImportAssignmentsCommand) { @@ -1575,7 +1580,7 @@ function Import-GraphObjectAssignment $htAssignments = @{} $htAssignments.Add((?? $objectType.AssignmentsType "assignments"), @($ObjectAssignments)) - $json = $htAssignments | ConvertTo-Json -Depth 10 + $json = $htAssignments | ConvertTo-Json -Depth 20 if($CopyAssignments -ne $true) { $json = Update-JsonForEnvironment $json @@ -2139,8 +2144,7 @@ function Export-GraphObject Remove-Property $obj "Assignments" } - #$obj | ConvertTo-Json -Depth 10 | Out-File ([IO.Path]::Combine($exportFolder, (Remove-InvalidFileNameChars "$((Get-GraphObjectName $obj $objectType)).json"))) - $obj | ConvertTo-Json -Depth 10 | Out-File -LiteralPath ([IO.Path]::Combine($exportFolder, (Remove-InvalidFileNameChars "$((Get-GraphObjectName $obj $objectType)).json"))) + $obj | ConvertTo-Json -Depth 20 | Out-File -LiteralPath ([IO.Path]::Combine($exportFolder, (Remove-InvalidFileNameChars "$((Get-GraphObjectName $obj $objectType)).json"))) -Force if($objectType.PostExportCommand) { @@ -2186,7 +2190,7 @@ function Import-GraphObject Write-Log "Import $($objectType.Title) object $((Get-GraphObjectName $obj $objectType))" # Clone the object before removing properties - $objClone = $obj | ConvertTo-Json -Depth 10 | ConvertFrom-Json + $objClone = $obj | ConvertTo-Json -Depth 20 | ConvertFrom-Json Start-GraphPreImport $obj $objectType diff --git a/README.md b/README.md index 3785451..d58393e 100644 --- a/README.md +++ b/README.md @@ -39,17 +39,27 @@ The script can import the exported json files in multiple ways. * **Always import:** The script will try to import the file. It will not check if it exists. This is the default behavior + * **Skip if object exists:** The script will look if there is an existing object with the same name and type. It will not import the file if existing object is detected + * **Replace (Preview):** If a existing object is detected, the script will + * Import the file without assignments * Copy assignments from the existing object * Run PostReplace commands - Priority will be set for Enrollment Restrictions etc. * Update PolicySets object(s) to use the new imported object (detected by policySet assignments) * Delete the original object -* **Update (Experimental):** This will update the existing object. - Note: This is not fully implemented yet. It only works on a few object types + +* **Update (Preview):** This will update the existing object. + + Update does not support all the properties that import does and object types behaves differently during update e.g. Settings for Endpoint Security objects will not be cleared. There is no API for removing settings only adding. If a settings does not in the import file, the existing setting will be set to Not Configured. Settings Catalog replaces the whole settings property during update. + + This has been tested with all supported object types *except* Import Scripts (Shell), Android OEM Config and Apple Enrollment Types. + + + Each application type works differently. Update functionality has been tested on Win32, Windows MSI LoB, iOS Store, Microsoft Store and Microsoft 365 (Windows and MacOS). -**WARNING:** Use Replace with caution! Replace will delete the existing object after the imported object is updated but could cause issues in the environment if something in the process goes wrong. Verify the process in a test environment before using this! +**WARNING:** Use Replace with caution! Replace will delete the existing object after a new object is imported and the assignments are copied, but it could cause issues in the environment if something in the process goes wrong. Replacing single objects can break references e.g. replacing an Application can break AutoPilot profiles, App Protection and App Configuration policies. Verify the process in a test environment before using this! **Recommendation:** Backup all policies before running Replace/Update. diff --git a/ReleaseNotes.md b/ReleaseNotes.md index e3aa204..7fb74d9 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,5 +1,18 @@ # Release Notes +## 3.1.8 - 2021-07-18 + +**New features** + +- Forget cached users - Forget a user by clicking on the bin icon in the user information. This will remove the user from the cached file. It will not remove it from the browser cache. +- Update existing profiles during import is moved to preview. + **Important:** See the Import section in the [Readme](README.md#Import) file for more information + This is based on the feature request in [Issue 17](https://github.com/Micke-K/IntuneManagement/issues/17) + +**Fixes** + +* Fixed a bug when exporting Settings Catalog. When exporting settings based on key/value pairs, some parts were not converted to json objects. Import worked but not the update. Depth parameter was increased in the ConvertTo-Json functions. + ## 3.1.7 - 2021-07-12 **New features** diff --git a/Xaml/Icons/Bin.xaml b/Xaml/Icons/Bin.xaml new file mode 100644 index 0000000..7beab1a --- /dev/null +++ b/Xaml/Icons/Bin.xaml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file