3.9.2
This commit is contained in:
@@ -10,7 +10,7 @@ This module will also document some objects based on PowerShell functions
|
||||
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'1.6.1'
|
||||
'1.6.2'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -2744,6 +2744,28 @@ function Invoke-CDDocumentConditionalAccess
|
||||
EntityKey = "excludeDevices"
|
||||
})
|
||||
}
|
||||
|
||||
if($obj.conditions.devices.deviceFilter)
|
||||
{
|
||||
if($obj.conditions.devices.deviceFilter.mode -eq "include")
|
||||
{
|
||||
$filterMode = "included"
|
||||
}
|
||||
else
|
||||
{
|
||||
$filterMode = "included"
|
||||
}
|
||||
|
||||
#AzureCA.PolicyBlade.Conditions.DeviceAttributes.AssignmentFilter.Blade
|
||||
#AzureCA.PolicyBlade.Conditions.DeviceAttributes.Blade.title
|
||||
Add-CustomSettingObject ([PSCustomObject]@{
|
||||
Name = Get-LanguageString "AzureCA.PolicyBlade.Conditions.DeviceAttributes.Blade.AppliesTo.$filterMode"
|
||||
Value = $obj.conditions.devices.deviceFilter.rule
|
||||
Category = $category
|
||||
SubCategory = Get-LanguageString "AzureCA.PolicyBlade.Conditions.DeviceAttributes.Blade.title"
|
||||
EntityKey = "includeDevices"
|
||||
})
|
||||
}
|
||||
|
||||
###################################################
|
||||
# Grant
|
||||
|
||||
@@ -10,7 +10,7 @@ This module is for the Endpoint Manager/Intune View. It manages Export/Import/Co
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.9.1'
|
||||
'3.9.2'
|
||||
}
|
||||
|
||||
function Invoke-InitializeModule
|
||||
@@ -73,6 +73,21 @@ function Invoke-InitializeModule
|
||||
SubPath = "EndpointManager"
|
||||
}) "EndpointManager"
|
||||
|
||||
Add-SettingsObject (New-Object PSObject -Property @{
|
||||
Title = "Save Encryption File"
|
||||
Key = "EMSaveEncryptionFile"
|
||||
Type = "Boolean"
|
||||
Description = "Save encryption file when uploading an app. This can then be used to when downloading the app file."
|
||||
SubPath = "EndpointManager"
|
||||
}) "EndpointManager"
|
||||
|
||||
Add-SettingsObject (New-Object PSObject -Property @{
|
||||
Title = "App download folder"
|
||||
Key = "EMIntuneAppDownloadFolder"
|
||||
Type = "Folder"
|
||||
Description = "Folder where app packages will be downloaded and where encryption files will be saved"
|
||||
SubPath = "EndpointManager"
|
||||
}) "EndpointManager"
|
||||
|
||||
$viewPanel = Get-XamlObject ($global:AppRootFolder + "\Xaml\EndpointManagerPanel.xaml") -AddVariables
|
||||
|
||||
@@ -314,7 +329,7 @@ function Invoke-InitializeModule
|
||||
PostFileImportCommand = { Start-PostFileImportAdministrativeTemplate @args }
|
||||
PreImportCommand = { Start-PreImportAdministrativeTemplate @args }
|
||||
LoadObject = { Start-LoadAdministrativeTemplate @args }
|
||||
PropertiesToRemove = @("definitionValues")
|
||||
PropertiesToRemove = @("definitionValues","policyConfigurationIngestionType")
|
||||
Permissons=@("DeviceManagementConfiguration.ReadWrite.All")
|
||||
Icon="DeviceConfiguration"
|
||||
GroupId = "DeviceConfiguration"
|
||||
@@ -454,8 +469,10 @@ function Invoke-InitializeModule
|
||||
PreDeleteCommand = { Start-PreDeleteApplications @args }
|
||||
PostExportCommand = { Start-PostExportApplications @args }
|
||||
PostListCommand = { Start-PostListApplications @args }
|
||||
ExportExtension = { Add-ScriptExportExtensions @args }
|
||||
ExportExtension = { Add-ScriptExportApplications @args }
|
||||
PostGetCommand = { Start-PostGetApplications @args }
|
||||
PostImportCommand = { Start-PostImportApplications @args }
|
||||
PostFilesImportCommand = { Start-PostFilesImportApplications @args }
|
||||
GroupId = "Apps"
|
||||
ScopeTagsReturnedInList = $false
|
||||
})
|
||||
@@ -1985,24 +2002,41 @@ function local:Start-ImportApp
|
||||
|
||||
if($appType -eq "microsoft.graph.win32LobApp")
|
||||
{
|
||||
Copy-Win32LOBPackage $packageFile $obj
|
||||
$fileEncryptionInfo = Copy-Win32LOBPackage $packageFile $obj
|
||||
}
|
||||
elseif($appType -eq "microsoft.graph.windowsMobileMSI")
|
||||
{
|
||||
Copy-MSILOB $packageFile $obj
|
||||
$fileEncryptionInfo = Copy-MSILOB $packageFile $obj
|
||||
}
|
||||
elseif($appType -eq "microsoft.graph.iosLOBApp")
|
||||
{
|
||||
Copy-iOSLOB $packageFile $obj
|
||||
$fileEncryptionInfo = Copy-iOSLOB $packageFile $obj
|
||||
}
|
||||
elseif($appType -eq "microsoft.graph.androidLOBApp")
|
||||
{
|
||||
Copy-AndroidLOB $packageFile $obj
|
||||
$fileEncryptionInfo = Copy-AndroidLOB $packageFile $obj
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Unsupported application type $appType. File will not be uploaded" 2
|
||||
}
|
||||
|
||||
if((Get-SettingValue "EMSaveEncryptionFile") -eq $true)
|
||||
{
|
||||
#$fileEncryptionInfo = $fileEncryptionInfo | where { $null -ne $_.fileEncryptionInfo }
|
||||
if($fileEncryptionInfo)
|
||||
{
|
||||
$jsonEncryptionInfo = $fileEncryptionInfo.fileEncryptionInfo | ConvertTo-Json -Depth 10
|
||||
|
||||
$pkgPath = Get-SettingValue "EMIntuneAppDownloadFolder" (Get-SettingValue "EMIntuneAppPackages")
|
||||
if($pkgPath -and [IO.Directory]::Exists($pkgPath))
|
||||
{
|
||||
$obj = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$($obj.id)" -ODataMetadata "Minimal"
|
||||
$fullPath = $pkgPath + "\$($obj.displayName)_$($obj.id)_$($obj.committedContentVersion).json"
|
||||
$jsonEncryptionInfo | Out-File -FilePath $fullPath -Force -Encoding utf8
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Start-PreUpdateApplication
|
||||
@@ -2092,6 +2126,69 @@ function Add-DetailExtensionApplications
|
||||
$tmp.Children.Insert($index, $btnUpload)
|
||||
}
|
||||
|
||||
$btnDownload = New-Object System.Windows.Controls.Button
|
||||
$btnDownload.Content = 'Download'
|
||||
$btnDownload.Name = 'btnDownloadAppfile'
|
||||
$btnDownload.Margin = "0,0,5,0"
|
||||
$btnDownload.Width = "100"
|
||||
|
||||
$btnDownload.Add_Click({
|
||||
Write-Status "Download file"
|
||||
$obj = $global:dgObjects.SelectedItem.Object
|
||||
#$obj = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$($obj.id)"
|
||||
|
||||
$pkgPath = Get-SettingValue "EMIntuneAppDownloadFolder" (Get-SettingValue "EMIntuneAppPackages")
|
||||
|
||||
$dlgSave = [System.Windows.Forms.SaveFileDialog]::new()
|
||||
$dlgSave.InitialDirectory = $pkgPath
|
||||
$dlgSave.FileName = ($obj.FileName + ".encrypted")
|
||||
if($dlgSave.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK -and $dlgSave.Filename)
|
||||
{
|
||||
Start-DownloadAppContent $obj $dlgSave.FileName
|
||||
|
||||
if([IO.File]::Exists($dlgSave.FileName))
|
||||
{
|
||||
$fullPath = $pkgPath + "\$($obj.displayName)_$($obj.id)_$($obj.committedContentVersion).json"
|
||||
if([IO.File]::Exists($fullPath) -eq $false)
|
||||
{
|
||||
if(([System.Windows.MessageBox]::Show("Could not find decryption file for $($obj.displayName)`nApp Id: $($obj.id)`nContent version $($obj.committedContentVersion)`n`nDo you want to browse for the file?", "Encryption file not found", "YesNo", "Warning")) -eq "Yes")
|
||||
{
|
||||
$of = [System.Windows.Forms.OpenFileDialog]::new()
|
||||
$of.InitialDirectory = $pkgPath
|
||||
$of.DefaultExt = "*.json"
|
||||
$of.Filter = "Json (*.json)|*.*"
|
||||
$of.Multiselect = $false
|
||||
|
||||
if($of.ShowDialog() -eq "OK")
|
||||
{
|
||||
$fullPath = $of.FileName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if([IO.File]::Exists($fullPath))
|
||||
{
|
||||
Write-Status "Decrypting file"
|
||||
$encryptionInfo = ConvertFrom-Json (Get-Content -Path $fullPath -Raw)
|
||||
$destination = $pkgPath + "\$($obj.FileName)"
|
||||
Start-DecryptFile $dlgSave.Filename $destination $encryptionInfo.encryptionKey $encryptionInfo.initializationVector
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Decryption file for $($obj.displayName) not found. Skipping decryption" 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Status ""
|
||||
})
|
||||
|
||||
$tmp = $form.FindName($buttonPanel)
|
||||
if($tmp)
|
||||
{
|
||||
$tmp.Children.Insert($index, $btnDownload)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function Start-PreImportAssignmentsApplications
|
||||
@@ -2177,6 +2274,42 @@ function Start-PostExportApplications
|
||||
Write-LogError "Failed to export scripts" $_.Exception
|
||||
}
|
||||
}
|
||||
|
||||
Save-Setting "Intune" "ExportAppFile" $global:chkExportApplicationFile.IsChecked
|
||||
if($global:chkExportApplicationFile.IsChecked)
|
||||
{
|
||||
$encryptioSource = Get-SettingValue "EMIntuneAppDownloadFolder" (Get-SettingValue "EMIntuneAppPackages")
|
||||
$pkgPath = $path
|
||||
|
||||
if($pkgPath)
|
||||
{
|
||||
Write-Status "Download file"
|
||||
|
||||
$exportFile = $pkgPath + "\$($obj.FileName).encrypted"
|
||||
$encryptionFile = $encryptioSource + "\$($obj.displayName)_$($obj.id)_$($obj.committedContentVersion).json"
|
||||
if($encryptionFile -and [IO.File]::Exists($encryptionFile))
|
||||
{
|
||||
Start-DownloadAppContent $obj $exportFile
|
||||
|
||||
if([IO.File]::Exists($exportFile))
|
||||
{
|
||||
Write-Status "Decrypting file"
|
||||
$encryptionInfo = ConvertFrom-Json (Get-Content -Path $encryptionFile -Raw)
|
||||
$destination = $pkgPath + "\$($obj.FileName)"
|
||||
Start-DecryptFile $exportFile $destination $encryptionInfo.encryptionKey $encryptionInfo.initializationVector
|
||||
}
|
||||
|
||||
try { [IO.File]::Delete($exportFile) }
|
||||
catch {
|
||||
Write-LogError "Filed to delete exported encrypted file" $_.Exception
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Cound not file encryption file `"$($obj.displayName)_$($obj.id)_$($obj.committedContentVersion).json`""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Start-PostListApplications
|
||||
@@ -2204,26 +2337,174 @@ function Start-PostListApplications
|
||||
$objList
|
||||
}
|
||||
|
||||
function Add-ScriptExportApplications
|
||||
{
|
||||
param($form, $buttonPanel, $index = 0)
|
||||
|
||||
Add-ScriptExportExtensions $form $buttonPanel $index
|
||||
|
||||
$ctrl = $form.FindName("chkExportApplicationFile")
|
||||
if(-not $ctrl)
|
||||
{
|
||||
$xaml = @"
|
||||
<StackPanel $($global:wpfNS) Orientation="Horizontal" Margin="0,0,5,0">
|
||||
<Label Content="Export application file" />
|
||||
<Rectangle Style="{DynamicResource InfoIcon}" ToolTip="Export the application file. Note: Application file will only be exported if ecryption file is found." />
|
||||
</StackPanel>
|
||||
"@
|
||||
$label = [Windows.Markup.XamlReader]::Parse($xaml)
|
||||
|
||||
$global:chkExportApplicationFile = [System.Windows.Controls.CheckBox]::new()
|
||||
$global:chkExportApplicationFile.IsChecked = ((Get-Setting "Intune" "ExportAppFile" "false") -eq "true")
|
||||
$global:chkExportApplicationFile.VerticalAlignment = "Center"
|
||||
$global:chkExportApplicationFile.Name = "chkExportApplicationFile"
|
||||
|
||||
@($label, $global:chkExportApplicationFile)
|
||||
}
|
||||
}
|
||||
|
||||
function Start-PostGetApplications {
|
||||
param($obj, $objectType)
|
||||
|
||||
$relationships = (Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$($obj.Id)/relationships?`$filter=targetType%20eq%20microsoft.graph.mobileAppRelationshipType%27child%27").value
|
||||
$dependencyApps = @()
|
||||
$supersededApps = @()
|
||||
foreach ($rel in $relationships) {
|
||||
if ($rel."@odata.type" -eq "#microsoft.graph.mobileAppDependency") {
|
||||
$dependencyApps += "$($rel.targetDisplayName)|!|$($rel.targetDisplayVersion)|!|$($rel.targetId)|!|$($rel.dependencyType)"
|
||||
if($obj.Object.dependentAppCount -is [Int] -and ($obj.Object.dependentAppCount -gt 0 -or $obj.Object.supersededAppCount -gt 0)) {
|
||||
$relationships = (Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$($obj.Id)/relationships?`$filter=targetType%20eq%20microsoft.graph.mobileAppRelationshipType%27child%27").value
|
||||
$dependencyApps = @()
|
||||
$supersededApps = @()
|
||||
foreach ($rel in $relationships) {
|
||||
if ($rel."@odata.type" -eq "#microsoft.graph.mobileAppDependency") {
|
||||
$dependencyApps += "$($rel.targetDisplayName)|!|$($rel.targetDisplayVersion)|!|$($rel.targetId)|!|$($rel.dependencyType)"
|
||||
}
|
||||
elseif ($rel."@odata.type" -eq "#microsoft.graph.mobileAppSupersedence") {
|
||||
$supersededApps += "$($rel.targetDisplayName)|!|$($rel.targetDisplayVersion)|!|$($rel.targetId)|!|$($rel.supersedenceType)"
|
||||
}
|
||||
}
|
||||
elseif ($rel."@odata.type" -eq "#microsoft.graph.mobileAppSupersedence") {
|
||||
$supersededApps += "$($rel.targetDisplayName)|!|$($rel.targetDisplayVersion)|!|$($rel.targetId)|!|$($rel.supersedenceType)"
|
||||
if ($dependencyApps.Count -gt 0) {
|
||||
$obj.Object | Add-Member -MemberType NoteProperty -Name "#CustomRefDependency" -Value ($dependencyApps -join "|*|")
|
||||
}
|
||||
|
||||
if ($supersededApps.Count -gt 0) {
|
||||
$obj.Object | Add-Member -MemberType NoteProperty -Name "#CustomRefSupersedence" -Value ($supersededApps -join "|*|")
|
||||
}
|
||||
}
|
||||
if ($dependencyApps.Count -gt 0) {
|
||||
$obj.Object | Add-Member -MemberType NoteProperty -Name "#CustomRefDependency" -Value ($dependencyApps -join "|*|")
|
||||
}
|
||||
}
|
||||
|
||||
function Start-PostImportApplications
|
||||
{
|
||||
param($obj, $objectType, $file)
|
||||
|
||||
#$tmpObj = Get-GraphObjectFromFile $file
|
||||
}
|
||||
|
||||
function Start-PostFilesImportApplications
|
||||
{
|
||||
param($objType, $importedObjects, $importedFiles)
|
||||
|
||||
$refObjects = $importedFiles | Where { $null -ne $_.Object."#CustomRefDependency" -or $null -ne $_.Object."#CustomRefSupersedence" }
|
||||
|
||||
if ($supersededApps.Count -gt 0) {
|
||||
$obj.Object | Add-Member -MemberType NoteProperty -Name "#CustomRefSupersedence" -Value ($supersededApps -join "|*|")
|
||||
if(($refObjects | measure).Count -gt 0)
|
||||
{
|
||||
Write-Log "Applicetions with Depnedency or Supersedence detected"
|
||||
foreach($file in $refObjects)
|
||||
{
|
||||
Add-ApplicationReferences $file.ImportedObject $file.Object
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function local:Add-ApplicationReferences
|
||||
{
|
||||
param($obj, $fileObj)
|
||||
|
||||
if($fileObj."#CustomRefDependency" -or $fileObj."#CustomRefSupersedence")
|
||||
{
|
||||
Write-Log "Adding app references for $($obj.displayName)"
|
||||
|
||||
$depAppsInfo = $fileObj."#CustomRefDependency"
|
||||
$supAppsInfo = $fileObj."#CustomRefSupersedence"
|
||||
|
||||
$releationShips = [PSCustomObject]@{
|
||||
relationships = @()
|
||||
}
|
||||
|
||||
if($depAppsInfo)
|
||||
{
|
||||
foreach($depApp in ($depAppsInfo -split "[|][*][|]"))
|
||||
{
|
||||
$appName, $appVer, $appId, $appType = $depApp -split "[|][!][|]"
|
||||
if(-not $appName -or -not $appVer)
|
||||
{
|
||||
Write-Log "Could not get Name and Version from string: $appApp" 2
|
||||
continue
|
||||
}
|
||||
$tmpApps = (Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps?`$filter=displayName eq '$appName'").value
|
||||
if(-not $tmpApps)
|
||||
{
|
||||
Write-Log "No application found with name $appName" 2
|
||||
continue
|
||||
}
|
||||
$tmpApp = $tmpApps | Where displayVersion -eq $appVer
|
||||
if(-not $tmpApp)
|
||||
{
|
||||
Write-Log "No $appName application found with version $appVer" 2
|
||||
continue
|
||||
}
|
||||
elseif(-not ($tmpApp | measure).Count -gt 1)
|
||||
{
|
||||
Write-Log "Multiple $appName application found with version $appVer" 2
|
||||
continue
|
||||
}
|
||||
Write-Log "Add $appName ($appVer) to Dependency list"
|
||||
$releationShips.relationships += [PSCustomObject]@{
|
||||
"@odata.type" = "#microsoft.graph.mobileAppDependency"
|
||||
targetId = $tmpApp.Id
|
||||
dependencyType = $appType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($supAppsInfo)
|
||||
{
|
||||
foreach($suppApp in ($supAppsInfo -split "[|][*][|]"))
|
||||
{
|
||||
$appName, $appVer, $appId, $appType = $suppApp -split "[|][!][|]"
|
||||
if(-not $appName -or -not $appVer)
|
||||
{
|
||||
Write-Log "Could not get Name and Version from string: $appApp" 2
|
||||
continue
|
||||
}
|
||||
$tmpApps = (Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps?`$filter=displayName eq '$appName'").value
|
||||
if(-not $tmpApps)
|
||||
{
|
||||
Write-Log "No application found with name $appName" 2
|
||||
continue
|
||||
}
|
||||
$tmpApp = $tmpApps | Where displayVersion -eq $appVer
|
||||
if(-not $tmpApp)
|
||||
{
|
||||
Write-Log "No $appName application found with version $appVer" 2
|
||||
continue
|
||||
}
|
||||
elseif(-not ($tmpApp | measure).Count -gt 1)
|
||||
{
|
||||
Write-Log "Multiple $appName application found with version $appVer" 2
|
||||
continue
|
||||
}
|
||||
Write-Log "Add $appName ($appVer) to Supersedence list"
|
||||
$releationShips.relationships += [PSCustomObject]@{
|
||||
"@odata.type" = "#microsoft.graph.mobileAppSupersedence"
|
||||
targetId = $tmpApp.Id
|
||||
supersedenceType = $appType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($releationShips.relationships.Count -gt 0)
|
||||
{
|
||||
$json = Update-JsonForEnvironment (ConvertTo-Json $releationShips -Depth 20)
|
||||
|
||||
Write-Log "Update app references"
|
||||
Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$($obj.Id)/updateRelationships" -Method "POST" -Body $json
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3515,10 +3796,13 @@ function Start-PreImportADMXFiles
|
||||
return
|
||||
}
|
||||
|
||||
$bytes = [IO.File]::ReadAllBytes($admxFile)
|
||||
#$bytes = [IO.File]::ReadAllBytes($admxFile)
|
||||
$bytes = Get-ASCIIBytes ([IO.File]::ReadAllText($admxFile))
|
||||
$obj.content = [Convert]::ToBase64String($bytes)
|
||||
|
||||
$bytes = [IO.File]::ReadAllBytes($admlFile)
|
||||
#$bytes = [IO.File]::ReadAllBytes($admlFile)
|
||||
$bytes = Get-ASCIIBytes ([IO.File]::ReadAllText($admlFile))
|
||||
|
||||
$obj.groupPolicyUploadedLanguageFiles += [PSCustomObject]@{
|
||||
fileName = [io.path]::GetFileName($admlFile)
|
||||
content = [Convert]::ToBase64String($bytes)
|
||||
|
||||
@@ -10,7 +10,7 @@ This module manages Application objects in Intune e.g. uploading application fil
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.9.1'
|
||||
'3.9.2'
|
||||
}
|
||||
|
||||
#########################################################################################
|
||||
@@ -94,26 +94,33 @@ function Copy-MSILOB
|
||||
|
||||
$tmpFile = [IO.Path]::GetTempFileName()
|
||||
|
||||
$msiInfo = Get-MSIFileInformation $msiFile @("ProductName", "ProductCode", "ProductVersion", "ProductLanguage")
|
||||
$msiInfo = Get-MSIFileInformation $msiFile @("ProductName", "ProductCode", "ProductVersion", "ProductLanguage", "UpgradeCode", "ALLUSERS")
|
||||
|
||||
if(-not $msiInfo) { return }
|
||||
|
||||
$fileEncryptionInfo = New-IntuneEncryptedFile $msiFile $tmpFile
|
||||
|
||||
[xml]$manifestXML = '<MobileMsiData MsiExecutionContext="Any" MsiRequiresReboot="false" MsiUpgradeCode="" MsiIsMachineInstall="true" MsiIsUserInstall="false" MsiIncludesServices="false" MsiContainsSystemRegistryKeys="false" MsiContainsSystemFolders="false"></MobileMsiData>'
|
||||
$manifestXML.MobileMsiData.MsiUpgradeCode = $msiInfo["ProductCode"]
|
||||
|
||||
$manifestXML.MobileMsiData.MsiUpgradeCode = $msiInfo["UpgradeCode"]
|
||||
if($msiInfo["ALLUSERS"] -eq 1)
|
||||
{
|
||||
$manifestXML.MobileMsiData.MsiExecutionContext = "System"
|
||||
}
|
||||
|
||||
$appFileBody = @{
|
||||
"@odata.type" = "#microsoft.graph.mobileAppContentFile"
|
||||
name = [IO.Path]::GetFileName($msiFile)
|
||||
size = (Get-Item $msiFile).Length
|
||||
sizeEncrypted = (Get-Item $tmpFile).Length
|
||||
manifest = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($manifestXML.OuterXml))
|
||||
isDependency = $false
|
||||
}
|
||||
|
||||
Add-FileToIntuneApp $appId $appType $tmpFile $appFileBody
|
||||
|
||||
Remove-Item $tmpFile -Force
|
||||
|
||||
$fileEncryptionInfo
|
||||
}
|
||||
|
||||
function Copy-iOSLOB
|
||||
@@ -149,6 +156,8 @@ function Copy-iOSLOB
|
||||
Add-FileToIntuneApp $appId $appType $tmpFile $appFileBody
|
||||
|
||||
Remove-Item $tmpFile -Force
|
||||
|
||||
$fileEncryptionInfo
|
||||
}
|
||||
|
||||
function Copy-AndroidLOB
|
||||
@@ -185,6 +194,8 @@ function Copy-AndroidLOB
|
||||
Add-FileToIntuneApp $appId $appType $tmpFile $appFileBody
|
||||
|
||||
Remove-Item $tmpFile -Force
|
||||
|
||||
$fileEncryptionInfo
|
||||
}
|
||||
|
||||
function Copy-Win32LOBPackage
|
||||
@@ -235,7 +246,7 @@ function Copy-Win32LOBPackage
|
||||
|
||||
$fileBody = @{
|
||||
"@odata.type" = "#microsoft.graph.mobileAppContentFile"
|
||||
name = $DetectionXML.ApplicationInfo.FileName
|
||||
name = "IntunePackage.intunewin"
|
||||
size = [int64]$DetectionXML.ApplicationInfo.UnencryptedContentSize
|
||||
sizeEncrypted = (Get-Item $tmpIntunewinFile).Length
|
||||
manifest = $null
|
||||
@@ -245,46 +256,59 @@ function Copy-Win32LOBPackage
|
||||
Add-FileToIntuneApp $appId $appType $tmpIntunewinFile $fileBody
|
||||
|
||||
# Remove extracted inintunewin file
|
||||
Remove-Item $tmpIntunewinPath -Force -Recurse
|
||||
Remove-Item $tmpIntunewinPath -Force -Recurse
|
||||
|
||||
$fileEncryptionInfo
|
||||
}
|
||||
|
||||
function Add-FileToIntuneApp
|
||||
{
|
||||
param($appId, $appType, $appFile, $fileBody)
|
||||
|
||||
$contentVersion = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions" -HttpMethod POST -Content "{}"
|
||||
$contentVersion = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions" -HttpMethod POST -Content "{}" -ODataMetadata "Minimal"
|
||||
$contentVersionId = $contentVersion.id
|
||||
$fileObj = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files" -HttpMethod POST -Content (ConvertTo-Json $fileBody -Depth 5)
|
||||
$fileObj = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files" -HttpMethod POST -Content (ConvertTo-Json $fileBody -Depth 5) -ODataMetadata "Minimal"
|
||||
|
||||
if(-not $fileObj)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
Write-Log "File object created. ID: $($fileObj.id)"
|
||||
|
||||
# Wait for Azure storage URI
|
||||
$fileObj = Wait-IntuneFileState "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)" "AzureStorageUriRequest"
|
||||
if(-not $fileObj)
|
||||
{
|
||||
Write-Log "No File Object returned from commit. Upload failed" 3
|
||||
return
|
||||
}
|
||||
|
||||
# Upload file
|
||||
Send-IntuneFileToAzureStorage $fileObj.azureStorageUri $appFile "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)"
|
||||
Send-IntuneFileToAzureStorage $fileObj.azureStorageUri $appFile "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)" | Out-Null
|
||||
|
||||
# Commit the file
|
||||
$reponse = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)/commit" -HttpMethod POST -Content (ConvertTo-Json $fileEncryptionInfo -Depth 5)
|
||||
Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)/commit" -HttpMethod POST -Content (ConvertTo-Json $fileEncryptionInfo -Depth 5) | Out-Null
|
||||
|
||||
Wait-IntuneFileState "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)" "CommitFile"
|
||||
Wait-IntuneFileState "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVersionId/files/$($fileObj.Id)" "CommitFile" | Out-Null
|
||||
|
||||
$fiUpload = [IO.FileInfo]$appFile
|
||||
# Commit the content version
|
||||
$commitAppBody = @{
|
||||
"@odata.type" = "#$appType"
|
||||
committedContentVersion = $contentVersionId
|
||||
fileName = $fiUpload.Name
|
||||
}
|
||||
|
||||
$reponse = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId" -HttpMethod PATCH -Content (ConvertTo-Json $commitAppBody -Depth 5)
|
||||
# if($fileBody.Name) {
|
||||
# $fileUploadName = $fileBody.Name
|
||||
# }
|
||||
# else {
|
||||
$fiUpload = [IO.FileInfo]$appFile
|
||||
$fileUploadName = $fiUpload.Name
|
||||
# }
|
||||
$commitAppBody.Add("fileName",$fileUploadName)
|
||||
|
||||
Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId" -HttpMethod PATCH -Content (ConvertTo-Json $commitAppBody -Depth 5) | Out-Null
|
||||
Write-Log "Upload finished for file $fileUploadName version $contentVersionId"
|
||||
}
|
||||
|
||||
function Wait-IntuneFileState
|
||||
@@ -318,7 +342,7 @@ function Wait-IntuneFileState
|
||||
return
|
||||
}
|
||||
|
||||
Start-Sleep -s 5
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
|
||||
if($succes -eq $false)
|
||||
@@ -635,4 +659,102 @@ function New-IntuneEncryptedFile
|
||||
$fileEncryptionInfo.fileEncryptionInfo = $encryptionInfo
|
||||
|
||||
$fileEncryptionInfo
|
||||
}
|
||||
|
||||
function Start-DecryptFile
|
||||
{
|
||||
param($sourceFile, $targetFile, $encryptionKey, $initializationVector)
|
||||
|
||||
if([IO.File]::Exists($targetFile))
|
||||
{
|
||||
$fi = [IO.FileInfo]$targetFile
|
||||
$newName = $fi.Name + "_$((Get-Date).ToString("yyyyMMdd_HHmm"))" + $fi.Extension
|
||||
$targetFile = $fi.DirectoryName + "\$newName"
|
||||
Write-Log "Target file exists. Changing target file to $targetFile" 2
|
||||
}
|
||||
|
||||
$bufferBlockSize = 1024 * 4
|
||||
|
||||
try
|
||||
{
|
||||
$aes = [System.Security.Cryptography.Aes]::Create()
|
||||
|
||||
$buffer = New-Object byte[] $bufferBlockSize
|
||||
$bytesRead = 0
|
||||
|
||||
$targetStream = [System.IO.File]::Open($targetFile, [System.IO.FileMode]::Create, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
|
||||
|
||||
try
|
||||
{
|
||||
$sourceStream = [System.IO.File]::Open($sourceFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::None)
|
||||
|
||||
$decryptor = $aes.CreateDecryptor([Convert]::FromBase64String($encryptionKey), [Convert]::FromBase64String($initializationVector))
|
||||
|
||||
$decryptoStream = New-Object System.Security.Cryptography.CryptoStream -ArgumentList @($targetStream, $decryptor, [System.Security.Cryptography.CryptoStreamMode]::Write)
|
||||
|
||||
$sourceStream.Seek(48L, [System.IO.SeekOrigin]::Begin)
|
||||
|
||||
while (($bytesRead = $sourceStream.Read($buffer, 0, $bufferBlockSize)) -gt 0)
|
||||
{
|
||||
$decryptoStream.Write($buffer, 0, $bytesRead)
|
||||
$decryptoStream.Flush()
|
||||
}
|
||||
$decryptoStream.FlushFinalBlock()
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ($null -ne $decryptoStream) { $decryptoStream.Dispose() }
|
||||
if ($null -ne $targetStream) { $targetStream.Dispose() }
|
||||
if ($null -ne $decryptor) { $decryptor.Dispose() }
|
||||
if ($null -ne $sourceStream) { $sourceStream.Dispose() }
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ($null -ne $sourceStream) { $sourceStream.Dispose() }
|
||||
if ($null -ne $aes) { $aes.Dispose() }
|
||||
}
|
||||
}
|
||||
|
||||
function Start-DownloadAppContent
|
||||
{
|
||||
param($obj, $destinationFile)
|
||||
# Not use but kept for reference. File can be download but it will be encrypted
|
||||
|
||||
if([IO.File]::Exists($destinationFile))
|
||||
{
|
||||
try { [IO.File]::Delete($encryptionFile) }
|
||||
catch {}
|
||||
}
|
||||
|
||||
$appId = $obj.Id
|
||||
|
||||
$appInfo = Invoke-GraphRequest -Url "$($global:graphURL)/deviceAppManagement/mobileApps/$appId"
|
||||
|
||||
$appType = $appInfo.'@odata.type'.Trim('#')
|
||||
|
||||
#$contentVersions = Invoke-GraphRequest -Url "$($global:graphURL)/deviceAppManagement/mobileApps/$appId/$appType/contentVersions"
|
||||
#$contentVerId = $contentVersions.Value[0].id
|
||||
|
||||
$contentVerId = $appInfo.committedContentVersion
|
||||
|
||||
$contentFiles = Invoke-GraphRequest "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVerId/files"
|
||||
|
||||
$contentFile = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVerId/files/$($contentFiles.value[-1].Id)" -NoError
|
||||
|
||||
if(-not $contentFile)
|
||||
{
|
||||
foreach($file in $contentFiles.value)
|
||||
{
|
||||
if($contentFiles.value[-1].Id -eq $file.id) { continune }
|
||||
|
||||
# NOT happy about this. file objects are not always returned in the order of upload.
|
||||
$contentFile = Invoke-GraphRequest -Url "/deviceAppManagement/mobileApps/$appId/$appType/contentVersions/$contentVerId/files/$($file.Id)" -NoError
|
||||
if($contentFile)
|
||||
{
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Start-DownloadFile $contentFile.azureStorageUri $destinationFile
|
||||
}
|
||||
@@ -10,7 +10,7 @@ This module manages Authentication for the application with MSAL. It is also res
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.9.1'
|
||||
'3.9.2'
|
||||
}
|
||||
|
||||
$global:msalAuthenticator = $null
|
||||
@@ -158,6 +158,10 @@ function Clear-MSALCurentUserVaiables
|
||||
{
|
||||
$global:MSALTenantId = $null
|
||||
$global:MSALGraphEnvironment = $null
|
||||
|
||||
$script:jwtAccessToken = $null
|
||||
$script:jwtIdToken = $null
|
||||
|
||||
}
|
||||
|
||||
function Get-MSALCurrentApp
|
||||
@@ -223,14 +227,23 @@ function Get-MSALUserInfo
|
||||
if($global:MSALToken)
|
||||
{
|
||||
Write-Log "Get current user"
|
||||
$tmpMe = MSGraph\Invoke-GraphRequest -Url "ME" -SkipAuthentication -ODataMetadata "Skip"
|
||||
if($null -ne $tmpMe -and $tmpMe.creationType -ne "Invitation")
|
||||
|
||||
if($script:jwtAccessToken.Payload.idtyp -ne "app")
|
||||
{
|
||||
### Only get user info from home tenant
|
||||
$global:Me = $tmpMe
|
||||
Write-Log "Get profile picture"
|
||||
$global:profilePhoto = "$($env:LOCALAPPDATA)\CloudAPIPowerShellManagement\$($global:Me.Id).jpeg"
|
||||
MSGraph\Invoke-GraphRequest "me/photos/48x48/`$value" -OutFile $global:profilePhoto -SkipAuthentication -NoError | Out-Null
|
||||
$tmpMe = MSGraph\Invoke-GraphRequest -Url "ME" -SkipAuthentication -ODataMetadata "Skip"
|
||||
if($null -ne $tmpMe -and $tmpMe.creationType -ne "Invitation")
|
||||
{
|
||||
### Only get user info from home tenant
|
||||
$global:Me = $tmpMe
|
||||
Write-Log "Get profile picture"
|
||||
$global:profilePhoto = "$($env:LOCALAPPDATA)\CloudAPIPowerShellManagement\$($global:Me.Id).jpeg"
|
||||
MSGraph\Invoke-GraphRequest "me/photos/48x48/`$value" -OutFile $global:profilePhoto -SkipAuthentication -NoError | Out-Null
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$global:profilePhoto = $null
|
||||
$global:me = $script:jwtAccessToken.Payload.app_displayname
|
||||
}
|
||||
|
||||
Write-Log "Get organization info"
|
||||
@@ -834,6 +847,42 @@ function Connect-MSALUser
|
||||
|
||||
Write-LogDebug "Authenticate"
|
||||
|
||||
if($global:MainAppStarted -eq $false)
|
||||
{
|
||||
$script:AppLogin = (Get-SettingValue "GraphAzureAppLogin") -or ($global:TenantId -and $global:AzureAppId -and ($global:ClientSecret -or $global:ClientCert))
|
||||
}
|
||||
|
||||
if($script:AppLogin)
|
||||
{
|
||||
if($global:MSALToken -and $global:MSALToken.ExpiresOn.LocalDateTime.Ticks -gt ((Get-Date).AddMinutes(-5)).Ticks)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
# Get login info for silent job from settings
|
||||
if(-not $global:AzureAppId) { $global:AzureAppId = Get-SettingValue "GraphAzureAppId" -TenantID $global:TenantId }
|
||||
if(-not $global:ClientSecret -and -not $global:ClientCert) { $global:ClientSecret = Get-SettingValue "GraphAzureAppSecret" -TenantID $global:TenantId }
|
||||
if(-not $global:ClientSecret -and -not $global:ClientCert) { $global:ClientCert = Get-SettingValue "GraphAzureAppCert" -TenantID $global:TenantId }
|
||||
|
||||
if($global:AzureAppId -and $global:ClientSecret -and $global:TenantId)
|
||||
{
|
||||
Connect-MSALClientApp $global:AzureAppId $global:TenantId -secret $global:ClientSecret
|
||||
}
|
||||
elseif($global:AzureAppId -and $global:ClientCert -and $global:TenantId)
|
||||
{
|
||||
Connect-MSALClientApp $global:AzureAppId $global:TenantId -certificate $global:ClientCert
|
||||
}
|
||||
else
|
||||
{
|
||||
Write-Log "Azure AppId, Tenant Id and Sercret/Cert must be specified for App logins" 3
|
||||
}
|
||||
|
||||
Invoke-MSALAuthenticationUpdated $global:MSALToken
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if($ShowMenu -eq $true -and ((Get-SettingValue "AzureADLoginMenu") -eq $true))
|
||||
{
|
||||
if((Show-MSALLoginMenu) -eq $false) { return }
|
||||
@@ -1167,7 +1216,8 @@ function Connect-MSALUser
|
||||
Save-Setting "" "LastLoggedOnUser" $authResult.Account.UserName
|
||||
Save-Setting "" "LastLoggedOnUserId" $authResult.Account.HomeAccountId.ObjectId
|
||||
}
|
||||
|
||||
Invoke-MSALAuthenticationUpdated $authResult
|
||||
<#
|
||||
Write-LogDebug "User, tenant or app has changed"
|
||||
Get-MSALUserInfo
|
||||
if($authResult)
|
||||
@@ -1175,9 +1225,26 @@ function Connect-MSALUser
|
||||
Invoke-MSALCheckObjectViewAccess $authResult
|
||||
}
|
||||
Invoke-ModuleFunction "Invoke-GraphAuthenticationUpdated"
|
||||
#>
|
||||
}
|
||||
}
|
||||
|
||||
function local:Invoke-MSALAuthenticationUpdated
|
||||
{
|
||||
param($authResult)
|
||||
|
||||
Write-LogDebug "User, tenant or app has changed"
|
||||
$script:jwtAccessToken = Get-JWTtoken $global:MSALToken.AccessToken
|
||||
$script:jwtIdToken = Get-JWTtoken $global:MSALToken.IdToken
|
||||
|
||||
Get-MSALUserInfo
|
||||
if($authResult)
|
||||
{
|
||||
Invoke-MSALCheckObjectViewAccess $authResult
|
||||
}
|
||||
Invoke-ModuleFunction "Invoke-GraphAuthenticationUpdated"
|
||||
}
|
||||
|
||||
function Start-MSALConsentPrompt
|
||||
{
|
||||
param([switch]$PassThru, $authToken)
|
||||
@@ -1254,6 +1321,22 @@ function Invoke-MSALCheckObjectViewAccess
|
||||
Set-XamlProperty $script:userEllipsGrid "lnkRequestConsent" "Visibility" "Visible"
|
||||
}
|
||||
|
||||
$accessToken = $null
|
||||
if($authToken)
|
||||
{
|
||||
$accessToken = Get-JWTtoken $authToken.AccessToken
|
||||
}
|
||||
|
||||
$curPermissions = $null
|
||||
if($accessToken.Payload.idtyp -eq "app")
|
||||
{
|
||||
$curPermissions = $accessToken.Payload.roles
|
||||
}
|
||||
elseif($accessToken.Payload.scp)
|
||||
{
|
||||
$curPermissions = $accessToken.Payload.scp.Split(" ")
|
||||
}
|
||||
|
||||
foreach($viewObjInfo in ($global:viewObjects | Where { $_.ViewInfo.AuthenticationID -eq "MSAL" }))
|
||||
{
|
||||
$viewObjInfo = $global:viewObjects | Where { $_.ViewInfo.Id -eq $global:EMViewObject.Id }
|
||||
@@ -1262,10 +1345,8 @@ function Invoke-MSALCheckObjectViewAccess
|
||||
{
|
||||
if($authToken)
|
||||
{
|
||||
$accessToken = Get-JWTtoken $authToken.AccessToken
|
||||
if($accessToken.Payload.scp)
|
||||
if($curPermissions)
|
||||
{
|
||||
$curPermissions = $accessToken.Payload.scp.Split(" ")
|
||||
foreach($viewItem in $viewObjInfo.ViewItems)
|
||||
{
|
||||
$full = 0
|
||||
@@ -1400,7 +1481,7 @@ function Disconnect-MSALUser
|
||||
$logout = $true
|
||||
if(-not $global:MSALToken.Account) { return }
|
||||
$user = $global:MSALToken.Account # Logout current user
|
||||
$global:MSALToken = $null
|
||||
$global:MSALToken = $null
|
||||
Clear-MSALCurentUserVaiables # Only clear variables for current user
|
||||
$msg = "Do you want to remove the token from the cache?"
|
||||
$title = "Remove token?"
|
||||
@@ -1563,6 +1644,10 @@ function Get-MSALProfileEllipse
|
||||
{
|
||||
$initials = "$($global:me.userPrincipalName[0])".ToUpper()
|
||||
}
|
||||
elseif($script:jwtAccessToken.Payload.idtyp -eq "app")
|
||||
{
|
||||
$initials = "APP"
|
||||
}
|
||||
|
||||
$grd = Get-MSALUserPhotoEllips -size $size -fontSize $fontSize -Color $Color
|
||||
|
||||
@@ -1586,8 +1671,15 @@ function Get-MSALProfileEllipse
|
||||
$global:grdProfileInfo.Tag = $grd
|
||||
$grd.Tag = $global:grdProfileInfo
|
||||
Set-XamlProperty $global:grdProfileInfo "txtOrganization" "Text" $global:Organization.displayName
|
||||
Set-XamlProperty $global:grdProfileInfo "txtUsername" "Text" $global:me.displayName
|
||||
Set-XamlProperty $global:grdProfileInfo "txtLogonName" "Text" $global:me.userPrincipalName
|
||||
if($script:jwtAccessToken.Payload.idtyp -eq "app")
|
||||
{
|
||||
Set-XamlProperty $global:grdProfileInfo "txtUsername" "Text" "App Login"
|
||||
}
|
||||
else
|
||||
{
|
||||
Set-XamlProperty $global:grdProfileInfo "txtUsername" "Text" $global:me.displayName
|
||||
Set-XamlProperty $global:grdProfileInfo "txtLogonName" "Text" $global:me.userPrincipalName
|
||||
}
|
||||
|
||||
$global:tokenInfo = Get-JWTtoken $global:MSALToken.AccessToken
|
||||
if($global:tokenInfo)
|
||||
@@ -1606,11 +1698,16 @@ function Get-MSALProfileEllipse
|
||||
$tmpObj.SetValue([System.Windows.Controls.Grid]::RowProperty,1)
|
||||
$tmpObj.SetValue([System.Windows.Controls.Grid]::RowSpanProperty,2)
|
||||
}
|
||||
|
||||
|
||||
if($tmpObj)
|
||||
{
|
||||
$profileGrid.Children.Add($tmpObj) | Out-Null
|
||||
}
|
||||
|
||||
if($script:jwtAccessToken.Payload.idtyp -eq "app")
|
||||
{
|
||||
$tmpObj.Visibility = "Collapsed"
|
||||
}
|
||||
|
||||
$global:grdProfileInfo.Add_Loaded({param($obj, $e)
|
||||
$point = $obj.Tag.TransformToAncestor($window).Transform([System.Windows.Point]::new(0,0));
|
||||
@@ -1618,134 +1715,137 @@ function Get-MSALProfileEllipse
|
||||
[System.Windows.Controls.Canvas]::SetTop($obj,($point.Y + $obj.Tag.ActualHeight))
|
||||
})
|
||||
|
||||
#########################################################################################################
|
||||
### Show / Hide consent button
|
||||
#########################################################################################################
|
||||
$script:userEllipsGrid = $tmpObj
|
||||
if(($script:missingPermissions | measure).Count -eq 0)
|
||||
if($script:jwtAccessToken.Payload.idtyp -ne "app")
|
||||
{
|
||||
Set-XamlProperty $script:userEllipsGrid "lnkRequestConsent" "Visibility" "Collapsed"
|
||||
}
|
||||
Add-XamlEvent $script:userEllipsGrid "lnkRequestConsent" "add_Click" {
|
||||
Start-MSALConsentPrompt
|
||||
}
|
||||
|
||||
$otherLogins = $global:grdProfileInfo.FindName("grdCachedAccounts")
|
||||
|
||||
#########################################################################################################
|
||||
### Add cached users
|
||||
#########################################################################################################
|
||||
if((Get-SettingValue "SortAccountList") -eq $true)
|
||||
{
|
||||
$accounts = $global:MSALAccounts | Sort -Property Username
|
||||
}
|
||||
else
|
||||
{
|
||||
$accounts = $global:MSALAccounts
|
||||
}
|
||||
|
||||
foreach($account in $accounts)
|
||||
{
|
||||
# Skip current logged on user
|
||||
if($global:MSALToken.Account.Username -eq $Account.Username -or
|
||||
$global:MSALToken.Account.HomeAccountId.ObjectId -eq $Account.HomeAccountId.ObjectId) { continue }
|
||||
|
||||
Add-CachedUser $account $otherLogins
|
||||
}
|
||||
|
||||
#########################################################################################################
|
||||
### Add login with another user
|
||||
#########################################################################################################
|
||||
$grdAccount = [System.Windows.Controls.Grid]::new()
|
||||
$cd = [System.Windows.Controls.ColumnDefinition]::new()
|
||||
$grdAccount.ColumnDefinitions.Add($cd)
|
||||
$cd = [System.Windows.Controls.ColumnDefinition]::new()
|
||||
$cd.Width = [double]::NaN
|
||||
$grdAccount.ColumnDefinitions.Add($cd)
|
||||
|
||||
$icon = Get-XamlObject ($global:AppRootFolder + "\Xaml\Icons\Logon.xaml")
|
||||
$icon.Width = 24
|
||||
$icon.Height = 24
|
||||
$icon.Margin = "0,0,5,0"
|
||||
$grdAccount.Children.Add($icon) | Out-Null
|
||||
|
||||
$lbObj = [Windows.Markup.XamlReader]::Parse("<TextBlock $wpfNS>Sign in with a different account</TextBlock>")
|
||||
$lbObj.SetValue([System.Windows.Controls.Grid]::ColumnProperty,1)
|
||||
#$lbObj.Style = $window.TryFindResource("HoverUnderlineStyle")
|
||||
$grdAccount.Children.Add($lbObj) | Out-Null
|
||||
|
||||
$lnkButton = [System.Windows.Controls.Button]::new()
|
||||
$lnkButton.Content = $grdAccount
|
||||
$lnkButton.Style = $window.TryFindResource("LinkButton")
|
||||
$lnkButton.Margin = "0,5,0,0"
|
||||
$lnkButton.Cursor = "Hand"
|
||||
$lnkButton.Tag = $account
|
||||
$lnkButton.add_Click({
|
||||
Write-Status "Logging in..."
|
||||
Hide-Popup
|
||||
Connect-MSALUser -Interactive -ShowMenu
|
||||
if($global:curObjectType)
|
||||
#########################################################################################################
|
||||
### Show / Hide consent button
|
||||
#########################################################################################################
|
||||
$script:userEllipsGrid = $tmpObj
|
||||
if(($script:missingPermissions | measure).Count -eq 0)
|
||||
{
|
||||
Show-GraphObjects
|
||||
Set-XamlProperty $script:userEllipsGrid "lnkRequestConsent" "Visibility" "Collapsed"
|
||||
}
|
||||
Write-Status ""
|
||||
})
|
||||
Add-XamlEvent $script:userEllipsGrid "lnkRequestConsent" "add_Click" {
|
||||
Start-MSALConsentPrompt
|
||||
}
|
||||
|
||||
$otherLogins = $global:grdProfileInfo.FindName("grdCachedAccounts")
|
||||
|
||||
$otherLogins = $global:grdProfileInfo.FindName("grdLoginAccount")
|
||||
|
||||
Add-GridObject $otherLogins $lnkButton
|
||||
|
||||
$otherLogins = $global:grdProfileInfo.FindName("grdTenantAccounts")
|
||||
|
||||
if(($script:AccessableTenants | measure).Count -gt 1)
|
||||
{
|
||||
#########################################################################################################
|
||||
### Add switch to another tenant
|
||||
### Add cached users
|
||||
#########################################################################################################
|
||||
$lbObj = [Windows.Markup.XamlReader]::Parse("<TextBlock $wpfNS><Bold>Tenants:</Bold></TextBlock>")
|
||||
$lbObj.Margin = "0,5,0,0"
|
||||
|
||||
Add-GridObject $otherLogins $lbObj
|
||||
foreach($tenant in $script:AccessableTenants)
|
||||
if((Get-SettingValue "SortAccountList") -eq $true)
|
||||
{
|
||||
try
|
||||
$accounts = $global:MSALAccounts | Sort -Property Username
|
||||
}
|
||||
else
|
||||
{
|
||||
$accounts = $global:MSALAccounts
|
||||
}
|
||||
|
||||
foreach($account in $accounts)
|
||||
{
|
||||
# Skip current logged on user
|
||||
if($global:MSALToken.Account.Username -eq $Account.Username -or
|
||||
$global:MSALToken.Account.HomeAccountId.ObjectId -eq $Account.HomeAccountId.ObjectId) { continue }
|
||||
|
||||
Add-CachedUser $account $otherLogins
|
||||
}
|
||||
|
||||
#########################################################################################################
|
||||
### Add login with another user
|
||||
#########################################################################################################
|
||||
$grdAccount = [System.Windows.Controls.Grid]::new()
|
||||
$cd = [System.Windows.Controls.ColumnDefinition]::new()
|
||||
$grdAccount.ColumnDefinitions.Add($cd)
|
||||
$cd = [System.Windows.Controls.ColumnDefinition]::new()
|
||||
$cd.Width = [double]::NaN
|
||||
$grdAccount.ColumnDefinitions.Add($cd)
|
||||
|
||||
$icon = Get-XamlObject ($global:AppRootFolder + "\Xaml\Icons\Logon.xaml")
|
||||
$icon.Width = 24
|
||||
$icon.Height = 24
|
||||
$icon.Margin = "0,0,5,0"
|
||||
$grdAccount.Children.Add($icon) | Out-Null
|
||||
|
||||
$lbObj = [Windows.Markup.XamlReader]::Parse("<TextBlock $wpfNS>Sign in with a different account</TextBlock>")
|
||||
$lbObj.SetValue([System.Windows.Controls.Grid]::ColumnProperty,1)
|
||||
#$lbObj.Style = $window.TryFindResource("HoverUnderlineStyle")
|
||||
$grdAccount.Children.Add($lbObj) | Out-Null
|
||||
|
||||
$lnkButton = [System.Windows.Controls.Button]::new()
|
||||
$lnkButton.Content = $grdAccount
|
||||
$lnkButton.Style = $window.TryFindResource("LinkButton")
|
||||
$lnkButton.Margin = "0,5,0,0"
|
||||
$lnkButton.Cursor = "Hand"
|
||||
$lnkButton.Tag = $account
|
||||
$lnkButton.add_Click({
|
||||
Write-Status "Logging in..."
|
||||
Hide-Popup
|
||||
Connect-MSALUser -Interactive -ShowMenu
|
||||
if($global:curObjectType)
|
||||
{
|
||||
$lbObj = [Windows.Markup.XamlReader]::Parse("<TextBlock $wpfNS HorizontalAlignment=`"Stretch`"><Bold>$($tenant.DisplayName)</Bold><LineBreak/>$($tenant.defaultDomain)<LineBreak/>$($tenant.tenantId)</TextBlock>")
|
||||
|
||||
if($tenant.tenantId -ne $global:MSALToken.TenantId)
|
||||
{
|
||||
$lbObj.Style = $window.TryFindResource("HoverUnderlineStyleWithBackground")
|
||||
$lbObj.HorizontalAlignment = "Stretch"
|
||||
$lnkButton = [System.Windows.Controls.Button]::new()
|
||||
$lnkButton.Content = $lbObj
|
||||
$lnkButton.HorizontalAlignment = "Stretch"
|
||||
$lnkButton.Style = $window.TryFindResource("ContentButton")
|
||||
$lnkButton.Margin = "0,5,0,0"
|
||||
$lnkButton.Cursor = "Hand"
|
||||
$lnkButton.Tag = $tenant
|
||||
$lnkButton.add_Click({
|
||||
Write-Status "Logging in to $($this.Tag.DisplayName)"
|
||||
# Set authority to selected tenant
|
||||
$global:MSALTenantId = $this.Tag.tenantId
|
||||
Hide-Popup
|
||||
Connect-MSALUser -Account ($global:MSALAccounts | Where UserName -eq $global:MSALToken.Account.Username)
|
||||
|
||||
if($global:curObjectType)
|
||||
{
|
||||
Show-GraphObjects
|
||||
}
|
||||
Write-Status ""
|
||||
})
|
||||
Add-GridObject $otherLogins $lnkButton
|
||||
}
|
||||
else
|
||||
{
|
||||
$lbObj.Background = $window.TryFindResource("SelectedRowBackgroundColor")
|
||||
$lbObj.Margin = "0,5,0,0"
|
||||
Add-GridObject $otherLogins $lbObj
|
||||
}
|
||||
Show-GraphObjects
|
||||
}
|
||||
Write-Status ""
|
||||
})
|
||||
|
||||
$otherLogins = $global:grdProfileInfo.FindName("grdLoginAccount")
|
||||
|
||||
Add-GridObject $otherLogins $lnkButton
|
||||
|
||||
$otherLogins = $global:grdProfileInfo.FindName("grdTenantAccounts")
|
||||
|
||||
if(($script:AccessableTenants | measure).Count -gt 1)
|
||||
{
|
||||
#########################################################################################################
|
||||
### Add switch to another tenant
|
||||
#########################################################################################################
|
||||
$lbObj = [Windows.Markup.XamlReader]::Parse("<TextBlock $wpfNS><Bold>Tenants:</Bold></TextBlock>")
|
||||
$lbObj.Margin = "0,5,0,0"
|
||||
|
||||
Add-GridObject $otherLogins $lbObj
|
||||
foreach($tenant in $script:AccessableTenants)
|
||||
{
|
||||
try
|
||||
{
|
||||
$lbObj = [Windows.Markup.XamlReader]::Parse("<TextBlock $wpfNS HorizontalAlignment=`"Stretch`"><Bold>$($tenant.DisplayName)</Bold><LineBreak/>$($tenant.defaultDomain)<LineBreak/>$($tenant.tenantId)</TextBlock>")
|
||||
|
||||
if($tenant.tenantId -ne $global:MSALToken.TenantId)
|
||||
{
|
||||
$lbObj.Style = $window.TryFindResource("HoverUnderlineStyleWithBackground")
|
||||
$lbObj.HorizontalAlignment = "Stretch"
|
||||
$lnkButton = [System.Windows.Controls.Button]::new()
|
||||
$lnkButton.Content = $lbObj
|
||||
$lnkButton.HorizontalAlignment = "Stretch"
|
||||
$lnkButton.Style = $window.TryFindResource("ContentButton")
|
||||
$lnkButton.Margin = "0,5,0,0"
|
||||
$lnkButton.Cursor = "Hand"
|
||||
$lnkButton.Tag = $tenant
|
||||
$lnkButton.add_Click({
|
||||
Write-Status "Logging in to $($this.Tag.DisplayName)"
|
||||
# Set authority to selected tenant
|
||||
$global:MSALTenantId = $this.Tag.tenantId
|
||||
Hide-Popup
|
||||
Connect-MSALUser -Account ($global:MSALAccounts | Where UserName -eq $global:MSALToken.Account.Username)
|
||||
|
||||
if($global:curObjectType)
|
||||
{
|
||||
Show-GraphObjects
|
||||
}
|
||||
Write-Status ""
|
||||
})
|
||||
Add-GridObject $otherLogins $lnkButton
|
||||
}
|
||||
else
|
||||
{
|
||||
$lbObj.Background = $window.TryFindResource("SelectedRowBackgroundColor")
|
||||
$lbObj.Margin = "0,5,0,0"
|
||||
Add-GridObject $otherLogins $lbObj
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1807,7 +1907,12 @@ function Get-MSALProfileEllipse
|
||||
{
|
||||
Show-GraphObjects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($script:jwtAccessToken.Payload.idtyp -eq "app")
|
||||
{
|
||||
Set-XamlProperty $tmpObj "lnkLogout" "Visibility" "Collapsed"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-LogError "Failed to create profile information object. Error: " $_.Exception
|
||||
@@ -1983,12 +2088,21 @@ function Get-MSALMissingScopes
|
||||
|
||||
$script:missingPermissions = @()
|
||||
|
||||
if($script:jwtAccessToken.Payload.idtyp -eq "app")
|
||||
{
|
||||
$curScopes = $script:jwtAccessToken.Payload.roles
|
||||
}
|
||||
else
|
||||
{
|
||||
$curScopes = $authToken.Scopes
|
||||
}
|
||||
|
||||
foreach($scope in $reqScopes)
|
||||
{
|
||||
$tmpScope = $scope.Split('/')[-1]
|
||||
if($tmpScope -eq ".default") { continue }
|
||||
if($authToken.Scopes -contains $tmpScope) { continue }
|
||||
if(($authToken.Scopes -like "*/$tmpScope")) { continue }
|
||||
if($curScopes -contains $tmpScope) { continue }
|
||||
if(($curScopes -like "*/$tmpScope")) { continue }
|
||||
$arrTemp = $tmpScope.Split(".")
|
||||
if($arrTemp[1] -eq "Read")
|
||||
{
|
||||
@@ -2012,6 +2126,9 @@ function Show-MSALDecodedToken {
|
||||
$tokenData,
|
||||
$title
|
||||
)
|
||||
|
||||
if(-not $tokenData.Header) { return }
|
||||
|
||||
$tokenArr = @()
|
||||
foreach($prop in ($tokenData.Header | GM | Where MemberType -eq NoteProperty))
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ This module manages Microsoft Grap fuctions like calling APIs, managing graph ob
|
||||
#>
|
||||
function Get-ModuleVersion
|
||||
{
|
||||
'3.9.1'
|
||||
'3.9.2'
|
||||
}
|
||||
|
||||
$global:MSGraphGlobalApps = @(
|
||||
@@ -183,6 +183,14 @@ function Invoke-InitializeModule
|
||||
Description = "Certificate for Azure App"
|
||||
}) "GraphSilent"
|
||||
|
||||
Add-SettingsObject (New-Object PSObject -Property @{
|
||||
Title = "Login with App in UI (Preview)"
|
||||
Key = "GraphAzureAppLogin"
|
||||
Type = "Boolean"
|
||||
DefaultValue = $false
|
||||
Description = "Login with specified app in the UI. Note: Change will require app restart"
|
||||
}) "GraphSilent"
|
||||
|
||||
Add-SettingsObject (New-Object PSObject -Property @{
|
||||
Title = "Refresh Objects after copy"
|
||||
Key = "RefreshObjectsAfterCopy"
|
||||
@@ -214,6 +222,15 @@ function Invoke-InitializeModule
|
||||
DefaultValue = $false
|
||||
Description = "Expand assignments when listing objects. This can be used in custom columns based on assignment info"
|
||||
}) "GraphGeneral"
|
||||
|
||||
Add-SettingsObject (New-Object PSObject -Property @{
|
||||
Title = "Use Graph 1.0 (Not Recommended)"
|
||||
Key = "UseGraphV1"
|
||||
Type = "Boolean"
|
||||
DefaultValue = $false
|
||||
Description = "This will use production verionof graph, v1.0. Note: Thot officially supported since this can have unpredicted results. Some parts will require Beta version of Graph."
|
||||
}) "GraphGeneral"
|
||||
|
||||
}
|
||||
|
||||
function Get-GraphAppInfo
|
||||
@@ -270,7 +287,7 @@ function Invoke-SettingsUpdated
|
||||
|
||||
function Initialize-GraphSettings
|
||||
{
|
||||
|
||||
$script:defaultVersion = ""
|
||||
}
|
||||
|
||||
function Invoke-GraphRequest
|
||||
@@ -297,7 +314,7 @@ function Invoke-GraphRequest
|
||||
$ODataMetadata = "full", # full, minimal, none or skip
|
||||
|
||||
[ValidateSet("beta","v1.0")]
|
||||
$GraphVersion = "beta",
|
||||
$GraphVersion = "",
|
||||
|
||||
[switch]
|
||||
$AllPages,
|
||||
@@ -317,6 +334,22 @@ function Invoke-GraphRequest
|
||||
Connect-MSALUser
|
||||
}
|
||||
|
||||
if(-not $GraphVersion)
|
||||
{
|
||||
if(-not $script:defaultVersion)
|
||||
{
|
||||
if((Get-SettingValue "UseGraphV1") -eq $true)
|
||||
{
|
||||
$script:defaultVersion = "v1.0"
|
||||
}
|
||||
else
|
||||
{
|
||||
$script:defaultVersion = "beta"
|
||||
}
|
||||
}
|
||||
$GraphVersion = $script:defaultVersion
|
||||
}
|
||||
|
||||
$params = @{}
|
||||
|
||||
$requestId = [Guid]::NewGuid().guid
|
||||
@@ -1684,6 +1717,7 @@ function Show-GraphImportForm
|
||||
|
||||
$importedObjectsCurType = 0
|
||||
$navigationPropObjects = @()
|
||||
$arrImportedObjects = @()
|
||||
foreach ($fileObj in $filesToImport)
|
||||
{
|
||||
if($allowUpdate -and $global:cbImportType.SelectedValue -ne "alwaysImport" -and (Reset-GraphObject $fileObj $global:dgObjects.ItemsSource))
|
||||
@@ -1699,9 +1733,15 @@ function Show-GraphImportForm
|
||||
ImportedObject = $importedObj
|
||||
}
|
||||
}
|
||||
$arrImportedObjects += $importedObj
|
||||
$importedObjectsCurType++
|
||||
}
|
||||
|
||||
if($global:curObjectType.PostFilesImportCommand)
|
||||
{
|
||||
& $global:curObjectType.PostFilesImportCommand $global:curObjectType $arrImportedObjects $filesToImport
|
||||
}
|
||||
|
||||
if($importedObjectsCurType -gt 0 -and $global:LoadedDependencyObjects -is [HashTable] -and $global:LoadedDependencyObjects.ContainsKey($global:curObjectType.Id))
|
||||
{
|
||||
Write-Log "Remove $($global:curObjectType.Title) from dependency cache"
|
||||
@@ -1870,7 +1910,6 @@ function Show-GraphBulkImportForm
|
||||
|
||||
function Start-GraphObjectImport
|
||||
{
|
||||
|
||||
Write-Status "Import objects" -Block
|
||||
Write-Log "****************************************************************"
|
||||
Write-Log "Start bulk import"
|
||||
@@ -1920,6 +1959,8 @@ function Start-GraphObjectImport
|
||||
|
||||
$importedObjectsCurType = 0
|
||||
|
||||
$arrImportedObjects = @()
|
||||
|
||||
foreach ($fileObj in @($filesToImport))
|
||||
{
|
||||
$objName = Get-GraphObjectName $fileObj.Object $item.ObjectType
|
||||
@@ -1943,11 +1984,17 @@ function Start-GraphObjectImport
|
||||
ImportedObject = $importedObj
|
||||
}
|
||||
}
|
||||
$arrImportedObjects = $importedObj
|
||||
|
||||
$importedObjects++
|
||||
$importedObjectsCurType++
|
||||
}
|
||||
|
||||
if($item.ObjectType.PostFilesImportCommand)
|
||||
{
|
||||
& $item.ObjectType.PostFilesImportCommand $item.ObjectType $arrImportedObjects $filesToImport
|
||||
}
|
||||
|
||||
if($importedObjectsCurType -gt 0 -and $global:LoadedDependencyObjects -is [HashTable] -and $global:LoadedDependencyObjects.ContainsKey($item.ObjectType.Id))
|
||||
{
|
||||
Write-Log "Remove $($item.ObjectType.Title) from dependency cache"
|
||||
@@ -2243,6 +2290,11 @@ function Import-GraphFile
|
||||
Import-GraphObjectAssignment $newObj $file.ObjectType $objClone.Assignments $file.FileInfo.FullName | Out-Null
|
||||
}
|
||||
|
||||
if($newObj)
|
||||
{
|
||||
$file | Add-Member -NotePropertyName "ImportedObject" -NotePropertyValue $newObj
|
||||
}
|
||||
|
||||
if($PassThru -eq $true -and $newObj)
|
||||
{
|
||||
$newObj
|
||||
|
||||
Reference in New Issue
Block a user