# main.ps1 (clean, gated execution) # Ensure MSAL.PS is installed if (-not (Get-Module -ListAvailable -Name MSAL.PS)) { try { Write-Host "[INFO] Installing MSAL.PS..." Install-Module -Name MSAL.PS -Scope CurrentUser -Force -AllowClobber } catch { [System.Windows.MessageBox]::Show("MSAL.PS install failed: $($_.Exception.Message)", "Error") exit 1 } } Import-Module MSAL.PS Write-Host "[INFO] MSAL.PS module loaded." # Load config and tool modules . "$PSScriptRoot\config.ps1" . "$PSScriptRoot\tools\autotask.ps1" . "$PSScriptRoot\tools\dattormm.ps1" Write-Host "[INFO] Config and tool modules loaded." # Load WPF assemblies Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName PresentationCore Add-Type -AssemblyName WindowsBase Add-Type -AssemblyName System.Xaml # Load WPF XAML UI [xml]$xaml = Get-Content "$PSScriptRoot\InstaProvision.xaml" $reader = (New-Object System.Xml.XmlNodeReader $xaml) $window = [Windows.Markup.XamlReader]::Load($reader) if (-not $window) { Write-Host "[ERROR] Failed to load XAML window." exit 1 } Write-Host "[INFO] WPF window loaded." # Map named UI controls $CompanyNameBox = $window.FindName("CompanyNameBox") $PhoneBox = $window.FindName("PhoneBox") $SelectAllBox = $window.FindName("SelectAllBox") $AutotaskBox = $window.FindName("AutotaskBox") $DattoBox = $window.FindName("DattoBox") $LoginBtn = $window.FindName("LoginBtn") $SubmitBtn = $window.FindName("SubmitBtn") $StatusBlock = $window.FindName("StatusBlock") $phoneLabel = $window.FindName("PhoneLabel") $PhoneBox.Visibility = 'Collapsed' $phoneLabel.Visibility = 'Collapsed' $SubmitBtn.Visibility = 'Collapsed' $AutotaskBox.Add_Checked({ $PhoneBox.Visibility = 'Visible' $phoneLabel.Visibility = 'Visible' }) $AutotaskBox.Add_Unchecked({ $PhoneBox.Visibility = 'Collapsed' $phoneLabel.Visibility = 'Collapsed' }) Write-Host "[INFO] UI control references assigned." # Global tool credentials $script:toolCredentials = $null # Select All logic $SelectAllBox.Add_Checked({ $AutotaskBox.IsChecked = $true $DattoBox.IsChecked = $true Write-Host "[UI] Select All: Checked" }) $SelectAllBox.Add_Unchecked({ $AutotaskBox.IsChecked = $false $DattoBox.IsChecked = $false Write-Host "[UI] Select All: Unchecked" }) # Login logic $LoginBtn.Add_Click({ Write-Host "[LOGIN] Initiating login..." try { $auth = Get-MSALToken -ClientId $CLIENT_ID -TenantId $TENANT_ID -Scopes $SCOPES -Interactive Write-Host "[LOGIN] Token acquired." if (-not $auth.AccessToken) { Write-Host "[ERROR] No access token returned." [System.Windows.MessageBox]::Show("Login failed.", "Auth") return } $tenantId = $auth.TenantId Write-Host "[DEBUG] Tenant ID: $tenantId" if (-not $tenantId) { [System.Windows.MessageBox]::Show("Tenant ID missing from login result.", "Auth Error") return } $headers = @{ "Content-Type" = "application/json" } $body = @{ tenant_id = $tenantId } | ConvertTo-Json -Depth 2 Write-Host "[DEBUG] Webhook payload: $body" $response = Invoke-RestMethod -Uri $WEBHOOK_URL -Method POST -Headers $headers -Body $body if (-not $response) { Write-Host "[ERROR] Webhook returned no data." [System.Windows.MessageBox]::Show("Unauthorized tenant or empty webhook response.", "Auth") return } if ($response -is [pscustomobject]) { $script:toolCredentials = @{} foreach ($property in $response.PSObject.Properties) { $script:toolCredentials[$property.Name] = $property.Value } } else { Write-Host "[ERROR] Webhook returned non-object data: $response" [System.Windows.MessageBox]::Show("Invalid webhook response.", "Auth") return } $SubmitBtn.Visibility = 'Visible' $LoginBtn.Visibility = 'Collapsed' Write-Host "[INFO] Login and webhook successful." [System.Windows.MessageBox]::Show("Login successful.", "Auth") } catch { Write-Host "[ERROR] Login/Webhook exception: $($_.Exception.Message)" [System.Windows.MessageBox]::Show("Login/Webhook error: $($_.Exception.Message)", "Error") } }) # Submit logic $SubmitBtn.Add_Click({ $company = $CompanyNameBox.Text.Trim() $phone = $PhoneBox.Text.Trim() $StatusBlock.Text = "" Write-Host "[SUBMIT] Provisioning start" Write-Host "[DEBUG] Company: $company" Write-Host "[DEBUG] Phone: $phone" Write-Host "[DEBUG] Autotask selected: $($AutotaskBox.IsChecked)" Write-Host "[DEBUG] Datto selected: $($DattoBox.IsChecked)" Write-Host "[DEBUG] Tool Credentials: $($script:toolCredentials | ConvertTo-Json -Depth 4)" if (-not $company) { [System.Windows.MessageBox]::Show("Please enter a company name.", "Missing Info") return } if ($AutotaskBox.IsChecked -and ($phone -notmatch "^[\d\s\-\+\(\)]{10,}$")) { [System.Windows.MessageBox]::Show("Valid phone number required for Autotask.", "Invalid Input") return } $StatusBlock.Text = "Provisioning in progress..." $window.Dispatcher.Invoke("Background", [action]{ $window.UpdateLayout() }) try { if ($AutotaskBox.IsChecked) { Write-Host "[INFO] Provisioning Autotask..." Invoke-AutotaskProvision -CompanyName $company -PhoneNumber $phone -Credentials $script:toolCredentials } if ($DattoBox.IsChecked) { Write-Host "[INFO] Provisioning Datto RMM..." Invoke-DattoProvision -CompanyName $company -Credentials $script:toolCredentials } $StatusBlock.Text = "Provisioning completed successfully." Write-Host "[SUCCESS] Provisioning complete." $CompanyNameBox.Text = "" $PhoneBox.Text = "" $AutotaskBox.IsChecked = $false $DattoBox.IsChecked = $false $SelectAllBox.IsChecked = $false } catch { Write-Host "[ERROR] Provisioning failed: $($_.Exception.Message)" $StatusBlock.Text = "Provisioning failed: $($_.Exception.Message)" } }) # Show the window $window.ShowDialog() | Out-Null