From 5699a096b5c176e9021a22059c58a17fe44469b6 Mon Sep 17 00:00:00 2001 From: Maxime Guillemin | Cloudflow Date: Tue, 15 Oct 2024 16:09:27 +0200 Subject: [PATCH 1/7] Rename and Connect MG Graph implementation --- Main.ps1 | 5 + Scripts/Connect-ToMgGraph.ps1 | 324 +++++++++++++++++++++++++ Scripts/ConnectButton.ps1 | 13 +- Scripts/ConnectEnterpriseAppButton.ps1 | 106 ++++++++ Scripts/Functions.ps1 | 2 + Scripts/RenameButton.ps1 | 185 ++++++++++++++ XML/EnterpriseAppConnectWindow.xaml | 72 ++++++ XML/Main.xaml | 4 +- XML/RenamePopup.xaml | 40 +++ 9 files changed, 747 insertions(+), 4 deletions(-) create mode 100644 Scripts/Connect-ToMgGraph.ps1 create mode 100644 Scripts/ConnectEnterpriseAppButton.ps1 create mode 100644 Scripts/RenameButton.ps1 create mode 100644 XML/EnterpriseAppConnectWindow.xaml create mode 100644 XML/RenamePopup.xaml diff --git a/Main.ps1 b/Main.ps1 index cec6844..edde52e 100644 --- a/Main.ps1 +++ b/Main.ps1 @@ -116,10 +116,12 @@ function Show-Window { # Load UI elements $TenantInfo = $Window.FindName("TenantInfo") $ConnectButton = $Window.FindName("ConnectButton") + $ConnectEnterpriseAppButton = $Window.FindName("ConnectEnterpriseAppButton") $LogoutButton = $Window.FindName("LogoutButton") $RefreshButton = $Window.FindName("RefreshButton") $StatusText = $Window.FindName("StatusText") $PolicyDataGrid = $Window.FindName("PolicyDataGrid") + $RenameButton = $Window.FindName("RenameButton") $DeleteAssignmentButton = $Window.FindName("DeleteAssignmentButton") $AddAssignmentButton = $Window.FindName("AddAssignmentButton") $BackupButton = $Window.FindName("BackupButton") @@ -144,7 +146,9 @@ function Show-Window { # Import external script files . .\Scripts\Functions.ps1 + . .\Scripts\Connect-ToMgGraph.ps1 . .\Scripts\ConnectButton.ps1 + . .\Scripts\ConnectEnterpriseAppButton.ps1 . .\Scripts\LogoutButton.ps1 . .\Scripts\RefreshButton.ps1 . .\Scripts\ConfigurationPoliciesButton.ps1 @@ -161,6 +165,7 @@ function Show-Window { . .\Scripts\Show-SelectionDialog.ps1 . .\Scripts\SearchButton.ps1 . .\Scripts\RemediationScriptsButton.ps1 + . .\Scripts\RenameButton.ps1 . .\Scripts\PlatformScriptsButton.ps1 . .\Scripts\AppConfigButton.ps1 . .\Scripts\MacosScriptsButton.ps1 diff --git a/Scripts/Connect-ToMgGraph.ps1 b/Scripts/Connect-ToMgGraph.ps1 new file mode 100644 index 0000000..ed12efc --- /dev/null +++ b/Scripts/Connect-ToMgGraph.ps1 @@ -0,0 +1,324 @@ +<#PSScriptInfo +.VERSION 1.0.1 +.GUID +.AUTHOR Thiago Beier forked authentication method from Andrew S Taylor Microsoft MVP +.COMPANYNAME +.COPYRIGHT GPL +.TAGS intune endpoint MEM autopilot +.LICENSEURI +.PROJECTURI +.ICONURI +.EXTERNALMODULEDEPENDENCIES +.REQUIREDSCRIPTS +.EXTERNALSCRIPTDEPENDENCIES +.RELEASENOTES +v1.0.1 - Added prerequisites check, added devicecode and interactive logon parameters +#> + +<# +.SYNOPSIS + This script connects to Microsoft Graph using different authentication methods, including Interactive, Device Code, App Secret, Certificate Thumbprint, and specific scopes. + +.DESCRIPTION + This PowerShell script provides three modes of authentication with Microsoft Graph: + - Scopes only: Connect using a specific set of read-only scopes. + - App Secret: Authenticate using client credentials (AppId, AppSecret, and Tenant). + - SSL Certificate: Authenticate using an SSL certificate. + +.PARAMETER devicecode + Executes the script using device code to authenticate. Opens Browser (Default) asks user to authenticate. + +.PARAMETER interactive + Executes the script using interactive only to authenticate. Opens Browser (Default) asks user to authenticate. + +.PARAMETER scopesonly + Executes the script using scopes only to authenticate. + +.PARAMETER scopesonly + Executes the script using scopes only to authenticate. + +.PARAMETER entraapp + Executes the script using App-based authentication with AppId, AppSecret, and Tenant. + +.PARAMETER usessl + Executes the script using certificate-based authentication with AppId, TenantId, and CertificateThumbprint. + +.PARAMETER AppId + The Azure AD Application (client) ID. + +.PARAMETER AppSecret + The client secret for the Azure AD application (required for -entraapp). + +.PARAMETER Tenant + The tenant domain or ID (required for -entraapp). + +.PARAMETER TenantId + The Azure AD Tenant ID (required for -usessl). + +.PARAMETER CertificateThumbprint + The SSL certificate thumbprint (required for -usessl). + +.EXAMPLE + .\script.ps1 -devicecode + Connects using authenticated user consented scopes/permissions. + +.EXAMPLE + .\script.ps1 -interactive + Connects using authenticated user consented scopes/permissions. + +.EXAMPLE + .\script.ps1 -scopesonly + Connects using read-only scopes. + +.EXAMPLE + .\script.ps1 -entraapp -AppId "client-id-or-entra-app-id-here" -AppSecret "password-here" -Tenant "your-tenant-domain-here" + Connects using App-based authentication with client credentials. + +.EXAMPLE + .\script.ps1 -usessl -AppId "client-id-or-entra-app-id-here" -TenantId "your-tenant-id-here" -CertificateThumbprint "your-ssl-certificate-thumbprint-here" + Connects using certificate-based authentication. + +.NOTES + Author: Thiago Beier (thiago.beier@gmail.com) + Social: https://x.com/thiagobeier https://thebeier.com/ https://www.linkedin.com/in/tbeier/ + Date: September 11, 2024 +#> + +param ( + [string]$AppId, + [string]$TenantId, + [string]$AppSecret, + [string]$CertificateThumbprint, + [string]$Tenant, + [string[]]$Scopes, # Array of scopes to be used in authentication + [switch]$scopesonly, # If true, execute the scopes only block + [switch]$entraapp, # If true, execute the entra app block + [switch]$usessl, # If true, execute the SSL certificate block + [switch]$interactive, # If true, execute the interactive block + [switch]$devicecode # If true, execute the device code block +) + +#region PowerShell modules and NuGet +function Install-GraphModules { + #Get NuGet + if (-not (Get-PackageProvider NuGet -ListAvailable -ErrorAction SilentlyContinue)) { + try { + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force:$true | Out-Null + Write-Host "Installed PackageProvider NuGet" + } + catch { + Write-Warning "Error installing provider NuGet, exiting..." + return + } + } + + #Get Graph Authentication modules (and dependencies) + $modules = @{ + 'Microsoft Graph Authentication' = 'Microsoft.Graph.Authentication' + 'MS Graph Groups' = 'Microsoft.Graph.Groups' + 'MS Graph Identity Management' = 'Microsoft.Graph.Identity.DirectoryManagement' + 'MS Graph Users' = 'Microsoft.Graph.Users' + } + + #Set PSGallery as Trusted + Set-PSRepository -Name PSGallery -InstallationPolicy Trusted + + foreach ($module in $modules.GetEnumerator()) { + if (Get-Module -Name $module.value -ListAvailable -ErrorAction SilentlyContinue) { + Import-Module -Name $module.value + } + else { + try { + Install-Module $module.Value -ErrorAction Stop + Write-Host ("Installing and importing PowerShell module {0}" -f $module.value) -ErrorAction Stop + Import-Module -Name $module.value -ErrorAction Stop + } + catch { + Write-Warning ("Error Installing or importing Powershell module {0}, exiting..." -f $module.value) + return + } + } + } +} + +#endregion + +#If -entraapp is provided, enforce that AppId, AppSecret, and Tenant are required +if ($entraapp) { + #Call the function + #Write-Host "Checking NuGet and PowerShell dependencies `n" -ForegroundColor cyan + #Install-GraphModules + + if (-not $AppId) { + throw "Error: The -AppId parameter is required when using -entraapp." + } + if (-not $AppSecret) { + throw "Error: The -AppSecret parameter is required when using -entraapp." + } + if (-not $Tenant) { + throw "Error: The -Tenant parameter is required when using -entraapp." + } +} + +#If -entraapp is provided, enforce that AppId, AppSecret, and Tenant are required +if ($usessl) { + #Call the function + Write-Host "Checking NuGet and PowerShell dependencies `n" -ForegroundColor cyan + Install-GraphModules + + if (-not $AppId) { + throw "Error: The -AppId parameter is required when using -usessl." + } + if (-not $TenantId) { + throw "Error: The -TenantId parameter is required when using -usessl." + } + if (-not $CertificateThumbprint) { + throw "Error: The -CertificateThumbprint parameter is required when using -usessl." + } +} + +#Check for -scopesonly parameter +if ($scopesonly) { + #Call the function + Write-Host "Checking NuGet and PowerShell dependencies `n" -ForegroundColor cyan + Install-GraphModules + + #region scopesReadOnly ask for authentication + $scopesReadOnly = @( + "Chat.ReadWrite.All" + "Directory.Read.All" + "Group.Read.All" + ) + + try { + Connect-MgGraph -Scopes $scopesReadOnly -ErrorAction Stop + Write-Host "This session current permissions `n" -ForegroundColor cyan + Get-MgContext | Select-Object -ExpandProperty Scopes -ErrorAction Stop + Write-Host "`n" + Write-Host "Please run Disconnect-MgGraph to disconnect `n" -ForegroundColor darkyellow + } + catch { + Write-Warning "Error connecting to Microsoft Graph or user aborted, exiting..." + return + } + #endregion +} + +# Check for -entraapp parameter +if ($entraapp) { + #Call the function + Write-Host "Checking NuGet and PowerShell dependencies `n" -ForegroundColor cyan + Install-GraphModules + + #region app secret + #Populate with the App Registration details and Tenant ID to validate manually + #$appid = '' + #$tenantid = '' + #$appsecret = '' + $version = (Get-Module microsoft.graph.authentication | Select-Object -ExpandProperty Version).Major + $body = @{ + grant_type = "client_credentials" + client_id = $AppId + client_secret = $AppSecret + scope = "https://graph.microsoft.com/.default" + } + + $response = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$Tenant/oauth2/v2.0/token" -Body $body + $accessToken = $response.access_token + if ($version -eq 2) { + Write-Host "Version 2 module detected" + $accesstokenfinal = ConvertTo-SecureString -String $accessToken -AsPlainText -Force + } + else { + Write-Host "Version 1 Module Detected" + Select-MgProfile -Name Beta + $accesstokenfinal = $accessToken + } + + try { + Connect-MgGraph -AccessToken $accesstokenfinal -ErrorAction Stop + Write-Host "Connected to tenant $Tenant using app-based authentication" + } + catch { + Write-Warning "Error connecting to tenant $Tenant using app-based authentication, exiting..." + return + } + + #Get-MgContext + Write-Host "This session current permissions `n" -ForegroundColor cyan + Get-MgContext | Select-Object -ExpandProperty Scopes + Write-Host "`n" + Write-Host "Please run Disconnect-MgGraph to disconnect `n" -ForegroundColor darkyellow + #Disconnect-MgGraph + #endregion +} + +#Check for -usessl parameter +if ($usessl) { + #Call the function + Write-Host "Checking NuGet and PowerShell dependencies `n" -ForegroundColor cyan + Install-GraphModules + + try { + #region ssl certificate authentication + Connect-MgGraph -ClientId $AppId -TenantId $TenantId -CertificateThumbprint $CertificateThumbprint -ErrorAction Stop + #Get-MgContext + Write-Host "This session current permissions `n" -ForegroundColor cyan + Get-MgContext | Select-Object -ExpandProperty Scopes -ErrorAction Stop + Write-Host "`n" + #(Get-MgContext).scopes + Write-Host "Please run Disconnect-MgGraph to disconnect `n" -ForegroundColor darkyellow + #Disconnect-MgGraph + } + catch { + Write-Warning "Error connecting to Microsoft Graph or user aborted, exiting..." + return + } + #endregion +} + +#Check for -interactive parameter +if ($interactive) { + #Call the function + Write-Host "Checking NuGet and PowerShell dependencies `n" -ForegroundColor cyan + Install-GraphModules + + try { + Connect-MgGraph -Scopes $Scopes -ErrorAction Stop + Write-Host "This session current permissions `n" -ForegroundColor cyan + Get-MgContext | Select-Object -ExpandProperty Scopes -ErrorAction Stop + Write-Host "`n" + #(Get-MgContext).scopes + Write-Host "Please run Disconnect-MgGraph to disconnect `n" -ForegroundColor darkyellow + } + catch { + Write-Warning "Error connecting to Microsoft Graph or user aborted, exiting..." + return + } +} + +#Check for -devicecode parameter +if ($devicecode) { + #Call the function + Write-Host "Checking NuGet and PowerShell dependencies `n" -ForegroundColor cyan + Install-GraphModules + + try { + #Start Browser + Start-Process https://microsoft.com/devicelogin -ErrorAction Stop + + #Wait for the user to enter the code provided on Screen to authenticate on opened Browser (Default) + Connect-MgGraph -UseDeviceCode -Scopes $Scopes -ErrorAction Stop + + Write-Host "This session current permissions `n" -ForegroundColor cyan + Get-MgContext | Select-Object -ExpandProperty Scopes -ErrorAction Stop + Write-Host "`n" + + #(Get-MgContext).scopes + Write-Host "Please run Disconnect-MgGraph to disconnect `n" -ForegroundColor darkyellow + } + catch { + Write-Warning "Error connecting to Microsoft Graph or user aborted, exiting..." + return + } +} \ No newline at end of file diff --git a/Scripts/ConnectButton.ps1 b/Scripts/ConnectButton.ps1 index 63e94ee..a20cb0a 100644 --- a/Scripts/ConnectButton.ps1 +++ b/Scripts/ConnectButton.ps1 @@ -19,9 +19,15 @@ $ConnectButton.Add_Click({ try { Write-IntuneToolkitLog "Starting connection to Microsoft Graph" -component "Connect-Button" -file "ConnectButton.ps1" - # Connect to Microsoft Graph - Connect-MgGraph -Scopes "User.Read.All", "Directory.Read.All", "DeviceManagementConfiguration.ReadWrite.All, DeviceManagementApps.ReadWrite.All" - Write-IntuneToolkitLog "Successfully connected to Microsoft Graph" -component "Connect-Button" -file "ConnectButton.ps1" + # Use interactive login with specific scopes + $authParams = @{ + interactive = $true + Scopes = @("User.Read.All", "Directory.Read.All", "DeviceManagementConfiguration.ReadWrite.All", "DeviceManagementApps.ReadWrite.All") + } + + # Call Connect-ToMgGraph.ps1 with the interactive login and custom scopes + .\Scripts\Connect-ToMgGraph.ps1 @authParams + Write-IntuneToolkitLog "Successfully connected to Microsoft Graph using interactive login with specified scopes" -component "Connect-Button" -file "ConnectButton.ps1" # Get tenant information $tenant = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/organization" -Method GET @@ -38,6 +44,7 @@ $ConnectButton.Add_Click({ # Update UI elements $StatusText.Text = "Please select a policy type." $PolicyDataGrid.Visibility = "Visible" + $RenameButton.IsEnabled = $true $DeleteAssignmentButton.IsEnabled = $true $AddAssignmentButton.IsEnabled = $true $BackupButton.IsEnabled = $true diff --git a/Scripts/ConnectEnterpriseAppButton.ps1 b/Scripts/ConnectEnterpriseAppButton.ps1 new file mode 100644 index 0000000..957ba29 --- /dev/null +++ b/Scripts/ConnectEnterpriseAppButton.ps1 @@ -0,0 +1,106 @@ +# Attach the click event handler to the existing $ConnectEnterpriseAppButton +$ConnectEnterpriseAppButton.Add_Click({ + + # Define the path to the external XAML file + $XAMLPath = ".\XML\EnterpriseAppConnectWindow.xaml" + + # Load the XAML from the file + if (-not (Test-Path $XAMLPath)) { + Write-IntuneToolkitLog "XAML file not found at path: $XAMLPath" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + $StatusText.Text = "Error: XAML file not found." + return + } + + Write-IntuneToolkitLog "Loading XAML from: $XAMLPath" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + + # Read the XAML content from the file + [xml]$XAML = Get-Content -Path $XAMLPath + + # Load the XAML and show the window + [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') + $reader = [System.Xml.XmlReader]::Create([System.IO.StringReader]$XAML.OuterXml) + $window = [Windows.Markup.XamlReader]::Load($reader) + + Write-IntuneToolkitLog "XAML window loaded successfully" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + + # Get access to the elements within the window + $TenantIDTextBox = $window.FindName("TenantIDTextBox") + $AppIDTextBox = $window.FindName("AppIDTextBox") + $AppSecretTextBox = $window.FindName("AppSecretTextBox") + $SubmitButton = $window.FindName("SubmitButton") + + # Define the click event handler for the Submit button + $SubmitButton.Add_Click({ + # Retrieve values from the textboxes + $TenantID = $TenantIDTextBox.Text + $AppID = $AppIDTextBox.Text + $AppSecret = $AppSecretTextBox.Password + + # Validate inputs + if (-not $TenantID -or -not $AppID -or -not $AppSecret) { + Write-IntuneToolkitLog "Failed: Missing input fields (TenantID, AppID, or AppSecret)" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + $StatusText.Text = "Error: Please fill out all fields." + return + } + + Write-IntuneToolkitLog "User input collected: Tenant ID = $TenantID, App ID = $AppID" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + + # Close the window once values are captured + $window.Close() + + # Log the connection attempt + Write-IntuneToolkitLog "Attempting to connect using Tenant ID: $TenantID, App ID: $AppID" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + $StatusText.Text = "Connecting to Microsoft Graph..." + + try { + # Use the imported Connect-ToMgGraph script to connect with the provided Tenant ID, App ID, and App Secret + $authParams = @{ + entraapp = $true + AppId = $AppID + AppSecret = $AppSecret + Tenant = $TenantID + Scopes = @("User.Read.All", "Directory.Read.All", "DeviceManagementConfiguration.ReadWrite.All", "DeviceManagementApps.ReadWrite.All") + } + + # Call Connect-ToMgGraph.ps1 to authenticate using app credentials + .\Scripts\Connect-ToMgGraph.ps1 @authParams + Write-IntuneToolkitLog "Successfully connected to Microsoft Graph with app credentials" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + + # Update UI elements after successful connection + Write-IntuneToolkitLog "Updating UI elements after successful connection" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + $StatusText.Text = "Please select a policy type." + $PolicyDataGrid.Visibility = "Visible" + $RenameButton.IsEnabled = $true + $DeleteAssignmentButton.IsEnabled = $true + $AddAssignmentButton.IsEnabled = $true + $BackupButton.IsEnabled = $true + $RestoreButton.IsEnabled = $true + $ConfigurationPoliciesButton.IsEnabled = $true + $DeviceConfigurationButton.IsEnabled = $true + $ComplianceButton.IsEnabled = $true + $AdminTemplatesButton.IsEnabled = $true + $ApplicationsButton.IsEnabled = $true + $AppConfigButton.IsEnabled = $true + $MacosScriptsButton.IsEnabled = $true + #$RemediationScriptsButton.IsEnabled = $true + $PlatformScriptsButton.IsEnabled = $true + $ConnectButton.IsEnabled = $false + $LogoutButton.IsEnabled = $true + $SearchFieldComboBox.IsEnabled = $true + $SearchBox.IsEnabled = $true + $SearchButton.IsEnabled = $true + $ExportToCSVButton.IsEnabled = $true + $ExportToMDButton.IsEnabled = $true + + Write-IntuneToolkitLog "UI elements updated successfully" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + + } catch { + # Handle connection errors + Write-IntuneToolkitLog "Failed to connect to Microsoft Graph. Error: $($_.Exception.Message)" -component "ConnectEnterpriseAppButton" -file "ConnectEnterpriseAppButton.ps1" + $StatusText.Text = "Error: Failed to connect to Microsoft Graph. Please try again." + } + }) + + # Show the popup window + $window.ShowDialog() +}) diff --git a/Scripts/Functions.ps1 b/Scripts/Functions.ps1 index f8f48bd..4ac4eeb 100644 --- a/Scripts/Functions.ps1 +++ b/Scripts/Functions.ps1 @@ -295,6 +295,7 @@ function Load-PolicyData { $ExportToCSVButton.IsEnabled = $false $ExportToMDButton.IsEnabled = $false $RefreshButton.IsEnabled = $false + $RenameButton.IsEnabled = $false # Load data synchronously $result = Reload-Grid -type $policyType @@ -332,4 +333,5 @@ function Load-PolicyData { $ExportToCSVButton.IsEnabled = $true $ExportToMDButton.IsEnabled = $true $RefreshButton.IsEnabled = $true + $RenameButton.IsEnabled = $true } \ No newline at end of file diff --git a/Scripts/RenameButton.ps1 b/Scripts/RenameButton.ps1 new file mode 100644 index 0000000..aa3a53a --- /dev/null +++ b/Scripts/RenameButton.ps1 @@ -0,0 +1,185 @@ +<# +.SYNOPSIS +Handles the renaming and updating of descriptions for selected policies in the Intune Toolkit. + +.DESCRIPTION +This script allows users to rename and update the descriptions of selected policies or applications +in the Intune Toolkit. It fetches current policy details, displays a custom popup with the +existing name and description prefilled, and processes the renaming and updating actions. +The script includes error handling, logging, and a refresh of the DataGrid after the updates. + +.NOTES +Author: Maxime Guillemin | CloudFlow +Date: 20/09/2024 + +.EXAMPLE +$RenameButton.Add_Click({ + $selectedPolicies = $PolicyDataGrid.SelectedItems + if ($selectedPolicies.Count -eq 1) { + # Process renaming and description update + } else { + [System.Windows.MessageBox]::Show("Please select exactly one policy/application.") + } +}) +#> + + +# Fetches policy details using a dynamic $select query based on the current policy type +function Get-PolicyDetails { + param ( + [string]$policyId + ) + + # Determine the fields to select for the given policy type + $select = switch ($global:CurrentPolicyType) { + "configurationPolicies" { "id,name,description" } + default { "id,displayName,description" } + } + + # Construct the URL for the Graph API call based on the policy type + $urlGetPolicy = if ($global:CurrentPolicyType -in @("mobileApps", "mobileAppConfigurations")) { + "https://graph.microsoft.com/beta/deviceAppManagement/$($global:CurrentPolicyType)('$policyId')?`$select=$($select)" + } else { + "https://graph.microsoft.com/beta/deviceManagement/$($global:CurrentPolicyType)('$policyId')?`$select=$($select)" + } + + Write-IntuneToolkitLog "Fetching policy details from: $urlGetPolicy" -component "Get-PolicyDetails" + + try { + # Fetch the policy details from Microsoft Graph API + $policyDetails = Invoke-MgGraphRequest -Uri $urlGetPolicy -Method GET + #Write-IntuneToolkitLog "Successfully fetched policy details: $($policyDetails | ConvertTo-Json)" -component "Get-PolicyDetails" + return $policyDetails + } catch { + $errorMessage = "Failed to fetch policy details: $($_.Exception.Message)" + Write-IntuneToolkitLog $errorMessage -component "Get-PolicyDetails" + throw $errorMessage + } +} + +# Displays the Rename Popup with the current policy name and description prefilled +function Show-RenamePopup { + param ( + [string]$currentName, + [string]$currentDescription + ) + + # Path to the XAML file for the Rename Popup UI + $xamlPath = ".\XML\RenamePopup.xaml" + + # Verify if the XAML file exists + if (-not (Test-Path $xamlPath)) { + Write-IntuneToolkitLog "XAML file not found: $xamlPath" -component "RenamePopup" + return $null + } + + # Load the XAML content + [xml]$xaml = Get-Content $xamlPath + $reader = (New-Object System.Xml.XmlNodeReader $xaml) + $Window = [Windows.Markup.XamlReader]::Load($reader) + + # Assign controls from the XAML to variables + $RenameButton = $Window.FindName("RenameButton") + $NewPolicyNameTextBox = $Window.FindName("NewPolicyNameTextBox") + $NewPolicyDescriptionTextBox = $Window.FindName("NewPolicyDescriptionTextBox") + + # Pre-fill the text boxes with current name and description + $NewPolicyNameTextBox.Text = $currentName + $NewPolicyDescriptionTextBox.Text = $currentDescription + + # Initialize script-level variables to store new policy details + $script:newPolicyName = $null + $script:newPolicyDescription = $null + + # Define the action for the Rename button click + $RenameButton.Add_Click({ + $script:newPolicyName = $NewPolicyNameTextBox.Text.Trim() + $script:newPolicyDescription = $NewPolicyDescriptionTextBox.Text.Trim() + if ($script:newPolicyName -and $script:newPolicyDescription) { + Write-IntuneToolkitLog "New name: $script:newPolicyName, New description: $script:newPolicyDescription" -component "RenamePopup" + $Window.Close() + } else { + [System.Windows.MessageBox]::Show("Please enter both a valid name and description.", "Error", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Warning) + } + }) + + # Display the Rename Popup window + $Window.ShowDialog() | Out-Null + return @{Name=$script:newPolicyName; Description=$script:newPolicyDescription} +} + +# Logic for the Rename Button click event +$RenameButton.Add_Click({ + Write-IntuneToolkitLog "RenameButton clicked" -component "Rename-Button" -file "RenameButton.ps1" + + try { + # Ensure only one policy is selected + $selectedPolicies = $PolicyDataGrid.SelectedItems + if ($selectedPolicies.Count -eq 1) { + $selectedPolicy = $selectedPolicies[0] + $policyId = $selectedPolicy.PolicyId + + Write-IntuneToolkitLog "Selected policy: $($selectedPolicy | ConvertTo-Json)" -component "Rename-Button" + + # Fetch detailed policy information + $policyDetails = Get-PolicyDetails -policyId $policyId + Write-IntuneToolkitLog "Fetched policy details: $($policyDetails | ConvertTo-Json)" -component "Rename-Button" + + # Determine the correct property for the policy name based on policy type + $currentName = switch ($global:CurrentPolicyType) { + "configurationPolicies" { $policyDetails.name } + default { $policyDetails.displayName } + } + $currentDescription = $policyDetails.description + + # Display the Rename Popup with current name and description prefilled + $newPolicyInfo = Show-RenamePopup -currentName $currentName -currentDescription $currentDescription + + # Proceed if there are valid changes to the name or description + if ($newPolicyInfo.Name -and $newPolicyInfo.Description -and ($newPolicyInfo.Name -ne $currentName -or $newPolicyInfo.Description -ne $currentDescription)) { + # Determine the property to update (name or displayName) + $propertyToUpdate = switch ($global:CurrentPolicyType) { + "configurationPolicies" { "name" } + default { "displayName" } + } + + # Update the policy details with the new name and description + $policyDetails.$propertyToUpdate = $newPolicyInfo.Name + $policyDetails.description = $newPolicyInfo.Description + + # Convert the updated policy details to JSON format + $body = $policyDetails | ConvertTo-Json + + Write-IntuneToolkitLog "Sending PATCH request with updated body: $($body)" -component "Rename-Button" + + # Construct the PATCH URL for renaming + $urlRename = if ($global:CurrentPolicyType -in @("mobileApps", "mobileAppConfigurations")) { + "https://graph.microsoft.com/beta/deviceAppManagement/$($global:CurrentPolicyType)('$($selectedPolicy.PolicyId)')" + } else { + "https://graph.microsoft.com/beta/deviceManagement/$($global:CurrentPolicyType)('$($selectedPolicy.PolicyId)')" + } + + try { + # Send the PATCH request to update the policy name and description + Invoke-MgGraphRequest -Uri $urlRename -Method PATCH -Body $body -ContentType "application/json" + Write-IntuneToolkitLog "Renamed policy/application and updated description: $($selectedPolicy.PolicyId)" -component "Rename-Button" -file "RenameButton.ps1" + } catch { + $errorMessage = "Failed to rename or update description: $($_.Exception.Message)" + Write-IntuneToolkitLog $errorMessage -component "Rename-Button" + [System.Windows.MessageBox]::Show($errorMessage, "Error", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Error) + } + + # Refresh the DataGrid to reflect the changes + Load-PolicyData -policyType $global:CurrentPolicyType -loadingMessage "Loading $($global:CurrentPolicyType)..." -loadedMessage "$($global:CurrentPolicyType) loaded." + } else { + [System.Windows.MessageBox]::Show("No changes made or same name/description entered.") + } + } else { + [System.Windows.MessageBox]::Show("Please select exactly one policy/application.") + } + } catch { + $errorMessage = "Failed to rename policy/application. Error: $($_.Exception.Message)" + [System.Windows.MessageBox]::Show($errorMessage, "Error") + Write-IntuneToolkitLog $errorMessage -component "Rename-Button" -file "RenameButton.ps1" + } +}) diff --git a/XML/EnterpriseAppConnectWindow.xaml b/XML/EnterpriseAppConnectWindow.xaml new file mode 100644 index 0000000..2a8148b --- /dev/null +++ b/XML/EnterpriseAppConnectWindow.xaml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +