diff --git a/main.ps1 b/main.ps1 index 9f98f28..960e93b 100644 --- a/main.ps1 +++ b/main.ps1 @@ -1,11 +1,12 @@ -# main.ps1 (clean, gated execution) +# main.ps1 # 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 { + } + catch { [System.Windows.MessageBox]::Show("MSAL.PS install failed: $($_.Exception.Message)", "Error") exit 1 } @@ -30,22 +31,22 @@ try { $window = [Windows.Markup.XamlReader]::Load($reader) if (-not $window) { Write-Host "[ERROR] Failed to load XAML window." - exit 1 + exit 1 + } + Write-Host "[INFO] WPF window loaded." } -Write-Host "[INFO] WPF window loaded." - Write-Host "[INFO] XAML loaded from remote URL." -} catch { +catch { Write-Host ("[ERROR] Failed to load XAML from {0}: {1}" -f $xamlUrl, $_.Exception.Message) exit 1 } - foreach ($name in $urls.Keys) { try { $scriptText = Invoke-WebRequest -Uri $urls[$name] -UseBasicParsing Invoke-Expression $scriptText.Content Write-Host "[INFO] Loaded $name.ps1 from URL." - } catch { + } + catch { Write-Host "[ERROR] Failed to load $name.ps1: $($_.Exception.Message)" exit 1 } @@ -60,44 +61,44 @@ Add-Type -AssemblyName WindowsBase Add-Type -AssemblyName System.Xaml # Map named UI controls -$CompanyNameBox = $window.FindName("CompanyNameBox") -$PhoneBox = $window.FindName("PhoneBox") -$SelectAllBox = $window.FindName("SelectAllBox") -$AutotaskBox = $window.FindName("AutotaskBox") -$DattoBox = $window.FindName("DattoBox") -$BackupBox = $window.FindName("BackupBox") -$ITGlueBox = $window.FindName("ITGlueBox") -$RocketcyberBox = $window.FindName("RocketcyberBox") -$CyberQPBox = $window.FindName("CyberQPBox") -$Pax8Box = $window.FindName("Pax8Box") -$LoginBtn = $window.FindName("LoginBtn") -$SubmitBtn = $window.FindName("SubmitBtn") -$StatusBlock = $window.FindName("StatusBlock") -$websiteBox = $window.FindName("WebsiteBox") -$streetBox = $window.FindName("StreetBox") -$cityBox = $window.FindName("CityBox") -$provinceBox = $window.FindName("ProvinceBox") -$postalCodeBox = $window.FindName("PostalCodeBox") -$countryBox = $window.FindName("CountryBox") - +$CompanyNameBox = $window.FindName("CompanyNameBox") +$PhoneBox = $window.FindName("PhoneBox") +$SelectAllBox = $window.FindName("SelectAllBox") +$AutotaskBox = $window.FindName("AutotaskBox") +$DattoBox = $window.FindName("DattoBox") +$BackupBox = $window.FindName("BackupBox") +$ITGlueBox = $window.FindName("ITGlueBox") +$RocketcyberBox = $window.FindName("RocketcyberBox") +$CyberQPBox = $window.FindName("CyberQPBox") +$Pax8Box = $window.FindName("Pax8Box") +$LoginBtn = $window.FindName("LoginBtn") +$SubmitBtn = $window.FindName("SubmitBtn") +$StatusBlock = $window.FindName("StatusBlock") +$WebsiteBox = $window.FindName("WebsiteBox") +$StreetBox = $window.FindName("StreetBox") +$CityBox = $window.FindName("CityBox") +$ProvinceBox = $window.FindName("ProvinceBox") +$PostalCodeBox = $window.FindName("PostalCodeBox") +$CountryBox = $window.FindName("CountryBox") +# Collapse until tools selected +$CompanyNameBox.Visibility = 'Collapsed' $PhoneBox.Visibility = 'Collapsed' +$WebsiteBox.Visibility = 'Collapsed' +$StreetBox.Visibility = 'Collapsed' +$CityBox.Visibility = 'Collapsed' +$ProvinceBox.Visibility = 'Collapsed' +$PostalCodeBox.Visibility = 'Collapsed' +$CountryBox.Visibility = 'Collapsed' +$SelectAllBox.Visibility = 'Collapsed' $SubmitBtn.Visibility = 'Collapsed' -$DattoBox.Visibility = 'Collapsed' $AutotaskBox.Visibility = 'Collapsed' +$DattoBox.Visibility = 'Collapsed' $BackupBox.Visibility = 'Collapsed' $ITGlueBox.Visibility = 'Collapsed' $RocketcyberBox.Visibility = 'Collapsed' $CyberQPBox.Visibility = 'Collapsed' $Pax8Box.Visibility = 'Collapsed' -$SelectAllBox.Visibility = 'Collapsed' -$CompanyNameBox.Visibility = 'Collapsed' -$websiteBox.Visibility = 'Collapsed' -$streetBox.Visibility = 'Collapsed' -$cityBox.Visibility = 'Collapsed' -$provinceBox.Visibility = 'Collapsed' -$postalCodeBox.Visibility = 'Collapsed' -$countryBox.Visibility = 'Collapsed' $BackupBox.IsEnabled = $false $ITGlueBox.IsEnabled = $false @@ -105,189 +106,184 @@ $RocketcyberBox.IsEnabled = $false $CyberQPBox.IsEnabled = $false $AutotaskBox.Add_Checked({ - $PhoneBox.Visibility = 'Visible' - $websiteBox.Visibility = 'Visible' - $streetBox.Visibility = 'Visible' - $cityBox.Visibility = 'Visible' - $provinceBox.Visibility = 'Visible' - $postalCodeBox.Visibility = 'Visible' - $countryBox.Visibility = 'Visible' -}) + $PhoneBox.Visibility = 'Visible' + $WebsiteBox.Visibility = 'Visible' + $StreetBox.Visibility = 'Visible' + $CityBox.Visibility = 'Visible' + $ProvinceBox.Visibility = 'Visible' + $PostalCodeBox.Visibility = 'Visible' + $CountryBox.Visibility = 'Visible' + }) $AutotaskBox.Add_Unchecked({ - $PhoneBox.Visibility = 'Collapsed' - $websiteBox.Visibility = 'Collapsed' - $streetBox.Visibility = 'Collapsed' - $cityBox.Visibility = 'Collapsed' - $provinceBox.Visibility = 'Collapsed' - $postalCodeBox.Visibility = 'Collapsed' - $countryBox.Visibility = 'Collapsed' -}) + $PhoneBox.Visibility = 'Collapsed' + $WebsiteBox.Visibility = 'Collapsed' + $StreetBox.Visibility = 'Collapsed' + $CityBox.Visibility = 'Collapsed' + $ProvinceBox.Visibility = 'Collapsed' + $PostalCodeBox.Visibility = 'Collapsed' + $CountryBox.Visibility = 'Collapsed' + }) $Pax8Box.Add_Checked({ - $PhoneBox.Visibility = 'Visible' - $websiteBox.Visibility = 'Visible' - $streetBox.Visibility = 'Visible' - $cityBox.Visibility = 'Visible' - $provinceBox.Visibility = 'Visible' - $postalCodeBox.Visibility = 'Visible' - $countryBox.Visibility = 'Visible' -}) + $PhoneBox.Visibility = 'Visible' + $WebsiteBox.Visibility = 'Visible' + $StreetBox.Visibility = 'Visible' + $CityBox.Visibility = 'Visible' + $ProvinceBox.Visibility = 'Visible' + $PostalCodeBox.Visibility = 'Visible' + $CountryBox.Visibility = 'Visible' + }) $Pax8Box.Add_Unchecked({ - $PhoneBox.Visibility = 'Collapsed' - $websiteBox.Visibility = 'Collapsed' - $streetBox.Visibility = 'Collapsed' - $cityBox.Visibility = 'Collapsed' - $provinceBox.Visibility = 'Collapsed' - $postalCodeBox.Visibility = 'Collapsed' - $countryBox.Visibility = 'Collapsed' -}) - -Write-Host "[INFO] UI control references assigned." + if (-not $AutotaskBox.IsChecked) { + $PhoneBox.Visibility = 'Collapsed' + $WebsiteBox.Visibility = 'Collapsed' + $StreetBox.Visibility = 'Collapsed' + $CityBox.Visibility = 'Collapsed' + $ProvinceBox.Visibility = 'Collapsed' + $PostalCodeBox.Visibility = 'Collapsed' + $CountryBox.Visibility = 'Collapsed' + } + }) # Global tool credentials $script:toolCredentials = $null # Select All logic $SelectAllBox.Add_Checked({ - $AutotaskBox.IsChecked = $true - $DattoBox.IsChecked = $true - Write-Host "[UI] Select All: Checked" -}) + $AutotaskBox.IsChecked = $true + $DattoBox.IsChecked = $true + }) $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 - } - - $DattoBox.Visibility = 'Visible' - $AutotaskBox.Visibility = 'Visible' - $SelectAllBox.Visibility = 'Visible' - $CompanyNameBox.Visibility = 'Visible' - $SubmitBtn.Visibility = 'Visible' - $BackupBox.Visibility = 'Visible' - $ITGlueBox.Visibility = 'Visible' - $RocketcyberBox.Visibility = 'Visible' - $CyberQPBox.Visibility = 'Visible' - $Pax8Box.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] 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 - } - - if ($Pax8Box.IsChecked) { - Write-Host "[INFO] Provisioning Pax8..." - Invoke-Pax8Provision -CompanyName $company -Phone $phone -Website $WebsiteBox.Text.Trim() -Street $StreetBox.Text.Trim() -City $CityBox.Text.Trim() -Province $ProvinceBox.Text.Trim() -PostalCode $PostalCodeBox.Text.Trim() -Country $CountryBox.Text.Trim() -Credentials $script:toolCredentials - } - - $StatusBlock.Text = "Provisioning completed successfully." - Write-Host "[SUCCESS] Provisioning complete." - $CompanyNameBox.Text = "" - $PhoneBox.Text = "" - $WebsiteBox.Text = "" - $StreetBox.Text = "" - $CountryBox.Text = "" - $CityBox.Text = "" - $ProvinceBox.Text = "" - $PostalCodeBox.Text = "" - - $SelectAllBox.IsChecked = $false $AutotaskBox.IsChecked = $false $DattoBox.IsChecked = $false - $ITGlueBox.IsChecked = $false - $BackupBox.IsChecked = $false - $RocketcyberBox.IsChecked = $false - $CyberQPBox.IsChecked = $false - $Pax8Box.IsChecked = $false - } - catch { - Write-Host "[ERROR] Provisioning failed: $($_.Exception.Message)" - $StatusBlock.Text = "Provisioning failed: $($_.Exception.Message)" - } -}) + }) -# Show the window +# Login handler +$LoginBtn.Add_Click({ + try { + $auth = Get-MSALToken -ClientId $CLIENT_ID -TenantId $TENANT_ID -Scopes $SCOPES -Interactive + + if (-not $auth.AccessToken) { + [System.Windows.MessageBox]::Show("Login failed.", "Auth") + return + } + + $headers = @{ "Content-Type" = "application/json" } + $body = @{ tenant_id = $auth.TenantId } | ConvertTo-Json -Depth 2 + + $response = Invoke-RestMethod -Uri $WEBHOOK_URL -Method POST -Headers $headers -Body $body + + if (-not $response -or $response -isnot [pscustomobject]) { + [System.Windows.MessageBox]::Show("Unauthorized or invalid response.", "Auth") + return + } + + $script:toolCredentials = @{} + foreach ($p in $response.PSObject.Properties) { + $script:toolCredentials[$p.Name] = $p.Value + } + + $DattoBox.Visibility = 'Visible' + $AutotaskBox.Visibility = 'Visible' + $SelectAllBox.Visibility = 'Visible' + $CompanyNameBox.Visibility = 'Visible' + $SubmitBtn.Visibility = 'Visible' + $BackupBox.Visibility = 'Visible' + $ITGlueBox.Visibility = 'Visible' + $RocketcyberBox.Visibility = 'Visible' + $CyberQPBox.Visibility = 'Visible' + $Pax8Box.Visibility = 'Visible' + $LoginBtn.Visibility = 'Collapsed' + + [System.Windows.MessageBox]::Show("Login successful.", "Auth") + } + catch { + [System.Windows.MessageBox]::Show("Login error: $($_.Exception.Message)", "Error") + } + }) + +# Submit handler +$SubmitBtn.Add_Click({ + $company = $CompanyNameBox.Text.Trim() + $phone = $PhoneBox.Text.Trim() + + $website = $WebsiteBox.Text.Trim() + $street = $StreetBox.Text.Trim() + $city = $CityBox.Text.Trim() + $province = $ProvinceBox.Text.Trim() + $postalCode = $PostalCodeBox.Text.Trim() + $country = $CountryBox.Text.Trim() + + $autotaskChecked = $AutotaskBox.IsChecked + $pax8Checked = $Pax8Box.IsChecked + + # Always required + if (-not $company) { + [System.Windows.MessageBox]::Show("Company Name is required.", "Missing Info") + return + } + + # Required if Autotask or Pax8 selected + if ($autotaskChecked -or $pax8Checked) { + if ($phone -notmatch "^[\d\s\-\+\(\)]{10,}$") { + [System.Windows.MessageBox]::Show("Valid phone number is required.", "Invalid Input") + return + } + + $missing = @() + if (-not $website) { $missing += "Website" } + if (-not $street) { $missing += "Street" } + if (-not $city) { $missing += "City" } + if (-not $province) { $missing += "Province" } + if (-not $postalCode) { $missing += "Postal Code" } + if (-not $country) { $missing += "Country" } + + if ($missing.Count -gt 0) { + $msg = "Please fill in the following required fields:`n" + ($missing -join "`n") + [System.Windows.MessageBox]::Show($msg, "Missing Info") + return + } + } + + + $StatusBlock.Text = "Provisioning in progress..." + $window.Dispatcher.Invoke("Background", [action] { $window.UpdateLayout() }) + + try { + if ($AutotaskBox.IsChecked) { + Invoke-AutotaskProvision -CompanyName $company ` + -PhoneNumber $phone ` + -Website $WebsiteBox.Text.Trim() ` + -Street $StreetBox.Text.Trim() ` + -City $CityBox.Text.Trim() ` + -Province $ProvinceBox.Text.Trim() ` + -PostalCode $PostalCodeBox.Text.Trim() ` + -Country "Canada" ` + -Credentials $script:toolCredentials + } + + $StatusBlock.Text = "Provisioning completed successfully." + Write-Host "[SUCCESS] Provisioning complete." + + # Reset fields + $CompanyNameBox.Text = "" + $PhoneBox.Text = "" + $WebsiteBox.Text = "" + $StreetBox.Text = "" + $CountryBox.Text = "" + $CityBox.Text = "" + $ProvinceBox.Text = "" + $PostalCodeBox.Text = "" + $SelectAllBox.IsChecked = $false + $AutotaskBox.IsChecked = $false + $DattoBox.IsChecked = $false + } + catch { + $StatusBlock.Text = "Provisioning failed: $($_.Exception.Message)" + } + }) + +# Show window $window.ShowDialog() | Out-Null diff --git a/tools/autotask.ps1 b/tools/autotask.ps1 index 819d64b..67996f6 100644 --- a/tools/autotask.ps1 +++ b/tools/autotask.ps1 @@ -1,21 +1,21 @@ function Invoke-AutotaskProvision { param ( - [Parameter(Mandatory)] - [string]$CompanyName, - - [Parameter(Mandatory)] - [string]$PhoneNumber, - - [Parameter(Mandatory)] - [hashtable]$Credentials + [Parameter(Mandatory)][string]$CompanyName, + [Parameter(Mandatory)][string]$PhoneNumber, + [string]$Website, + [string]$Street, + [string]$City, + [string]$Province, + [string]$PostalCode, + [string]$Country, + [Parameter(Mandatory)][hashtable]$Credentials ) if (-not $Credentials["AutotaskURL"] -or -not $Credentials["AutotaskIntCode"] -or -not $Credentials["AutotaskUsername"] -or -not $Credentials["AutotaskSecret"]) { throw "Missing Autotask credentials in hashtable." } - $BaseURL = $Credentials["AutotaskURL"] - $Url = "https://$BaseURL/atservicesrest/v1.0/companies" + $Url = "https://$($Credentials["AutotaskURL"])/atservicesrest/v1.0/companies" $Headers = @{ "ApiIntegrationcode" = $Credentials["AutotaskIntCode"] @@ -28,12 +28,18 @@ function Invoke-AutotaskProvision { companyName = $CompanyName companyType = 1 phone = $PhoneNumber + website = $Website + address1 = $Street + city = $City + state = $Province + postalCode = $PostalCode + countryID = 38 # Statically set to Canada ownerResourceID = 4 } | ConvertTo-Json -Depth 3 try { $Response = Invoke-RestMethod -Uri $Url -Method Post -Headers $Headers -Body $Body - $Response.itemId, $Response.id, $Response.companyID, $Response.item.id | Where-Object { $_ } | Select-Object -First 1 + return $Response.itemId, $Response.id, $Response.companyID, $Response.item.id | Where-Object { $_ } | Select-Object -First 1 } catch { throw "[Autotask] Provisioning failed: $($_.Exception.Message)" }