ConvertFrom-OpensearchItemId.ps1
<#PSScriptInfo
.VERSION 2023.12.5 .GUID dd297485-342b-4999-8c56-e9e969edb01a .AUTHOR Kent Sapp (@cksapp) .COMPANYNAME .COPYRIGHT © 2023 | Kent Sapp .TAGS Datto-Internal, OpenSearch .LICENSEURI https://gist.github.com/cksapp/2a46f9b4342877b62586ff2f99931fc1#file-license .PROJECTURI https://gist.github.com/cksapp/2a46f9b4342877b62586ff2f99931fc1 .ICONURI https://gist.github.com/assets/66083310/3e673212-80b1-4fbd-a0c7-2d489a378dbc .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES Lots of changes here, better handling of errors and URL validation. Looking good for Release Candidate! .PRIVATEDATA #> <# .SYNOPSIS 1. Modifies item IDs by replacing '-' with '+' and '_' with '/' 2. Converts the modified item ID to URL encoded format 3. Generates URL links for the modified item IDs .DESCRIPTION This is an interactive script that modifies ItemId(s) to a URL encoded format and generates a Microsoft link to the affected item. .NOTES This function is designed to modify item IDs from Datto OpenSearch and provide a link to the Outlook Web App calendar URL. This is a PowerShell adaptation for the original `EWSreplacer.bat` from @Liam Cowan for the PT [#4315344](https://kaseya.zendesk.com/agent/tickets/4315344) #> function Get-OutlookUrl { [CmdletBinding()] param ( [Parameter( Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true )] [string] $opensearchItemId ) process { if ($opensearchItemId -eq 'q') { return 'q' } Write-Verbose -Message "Processing OpensearchItemId: $opensearchItemId" # Step 1: Replaces '-' with '+' and '_' with '/' $originalMsItemId = $opensearchItemId -replace '-', '+' -replace '_', '/' Write-Verbose -Message "Step 1 - Microsoft ItemID: $originalMsItemId" # Add Type System.Web if not already added Try { [System.Web.HttpUtility]$HttpUtilityTest } Catch { Add-Type -AssemblyName System.Web } # Step 2: Convert to URL encoded format $urlEncodedItemId = [System.Web.HttpUtility]::UrlEncode($originalMsItemId) Write-Verbose -Message "Step 2 - URL Encoded: $urlEncodedItemId" # Step 3: Generate OWA calendar URL link [System.Uri]$owaUrl = "https://outlook.office.com/calendar/item/$urlEncodedItemId" Write-Verbose -Message "Step 3 - Converted URL: $owaUrl" ## Back to the validation step here not working, was not testing against powershell 5. ## Some logic added to handle the exceptions seen. # Validate URL link created if ([string]::IsNullOrWhiteSpace($doUrlValidation) -or $doUrlValidation -eq $true) { try { $response = Invoke-WebRequest -Uri $owaUrl -UseBasicParsing -ErrorAction SilentlyContinue if ($response.StatusCode -eq 200) { Write-Verbose -Message "Validation for $($owaUrl.AbsoluteUri) successful." #Write-Host "Validation for $($owaUrl.AbsoluteUri) successful." } } catch { # Handling for error (417) when validation run on PowerShell 5.1 if ($_.Exception.Message -notlike '*The remote server returned an error: (417).*') { $script:invalidUrl = $true # Set the flag to true for invalid URLs Write-Host "$($_.Exception.Message)" -ForegroundColor Red Write-Host "Output URL appears to have produced an error. Check the original input `"" -ForegroundColor DarkRed -NoNewLine Write-Host "$opensearchItemId" -ForegroundColor Yellow -NoNewLine Write-Host "`" and try again." -ForegroundColor DarkRed } } } # Cast the URI back to a string type and return the variable [String]$owaLink = $owaUrl.AbsoluteUri $owaLink } } function Start-Counter { param ( $countdownSeconds = 5 ) # Countdown for ($i = $countdownSeconds; $i -ge 1; $i--) { Write-Host "Exit in $i seconds..." Start-Sleep -Seconds 1 } } # URL Validation choice user input $urlValidationChoice = $(Write-Host "Warning: This can add additional processing and time on larger datasets`n" -ForegroundColor yellow -NoNewLine) + $(Write-Host "Attempt URL validation? Type: Y or Yes, N or No."; Read-Host) # Enable URL validation check, takes in user input and converts to boolean value if ($urlValidationChoice.ToLower() -eq 'y' -or $urlValidationChoice -eq 'yes') { $doUrlValidation = $true } # Script portion to allow for user selection $sourceMethod = Read-Host "Select your input method (Type: 1 for Console, 2 for CSV file) then `"Enter`"" switch ($sourceMethod) { 1 { do { $opensearchItemId = Read-Host "Enter itemID(s) from OpenSearch (or type 'q' to quit)" $convertedUrl = Get-OutlookUrl -opensearchItemId $opensearchItemId if ($convertedUrl -ne 'q') { Write-Host "URL Link: " -ForegroundColor Green -NoNewLine Write-Host "$($convertedUrl)" -ForegroundColor Cyan } } while ($convertedUrl -ne 'q') break } 2 { # Get user input for CSV file path $csvFilePath = Read-Host "Enter the path to the CSV file" $csvFilePath = $csvFilePath -replace '"', '' # Remove any quotes from the file path if found # Get the data from the CSV file try { $sourceCsvData = Import-Csv -Path $csvFilePath -Delimiter ',' Write-Verbose -Message "File successfully imported." # Check if the file extension is .csv $fileType = (Get-Item $csvFilePath).Extension if ($fileType -ne '.csv') { throw "Error: The file `"$((Get-Item $csvFilePath).Name)`" does not appear to be a CSV. Check the input and try again." } } catch { $csvImportError = $_.Exception.Message Write-Host "$csvImportError" -ForegroundColor Red Read-Host -Prompt "Press any key to continue" Start-Counter; break } # Filter CSV data to include only columns with labels containing "ItemId" $csvItemIds = $sourceCsvData | ForEach-Object { $_.PSObject.Properties | Where-Object { $_.Name -like 'ItemId*' } | ForEach-Object { $_.Value } } # Array to store results $convertedCsvArray = [System.Collections.ArrayList]::new() # Convert each $csvItemIds and put them back into the array foreach ($opensearchItemId in $csvItemIds) { Write-Host "Processing CSV ItemId: $opensearchItemId" # Convert the Item Ids and get the results [string]$convertedUrl = Get-OutlookUrl -opensearchItemId $opensearchItemId # Create a custom object with the "Output_Url" property $outputResult = [PSCustomObject]@{ "Output_Url" = $convertedUrl } # Check for valid URL, outputs error if invalid URL seen if ($invalidUrl) { # If $invalidUrl is true, add custom error message to $outputResult $invalidUrlDetected = $true Add-Member -InputObject $outputResult -MemberType NoteProperty -Name "ErrorMessage" -Value "Invalid output URL detected. Check the input ItemId: $opensearchItemId" } # Add the output result to the ArrayList $convertedCsvArray.Add($outputResult) Write-Host "Converted URL Link: " -ForegroundColor Green -NoNewLine Write-Host "$convertedUrl" -ForegroundColor Cyan # Clean up the $invalidUrl variable if seen $invalidUrl = $null } # Get parent directory, and filename from the input CSV file path $csvDirectory = (Get-Item $csvFilePath).Directory.FullName $csvFileName = (Get-Item $csvFilePath).BaseName $outputCsvPath = Join-Path -Path $csvDirectory -ChildPath "$csvFileName-Converted_Urls.csv" # Check the flag after processing the URLs. Exports results to a new CSV file in the source directory if ($invalidUrlDetected) { Write-Host "One or more invalid URLs were found. Be sure to validate the output." -ForegroundColor Red $convertedCsvArray | Select-Object -Property Output_Url,ErrorMessage | Export-Csv -Path $outputCsvPath -NoTypeInformation -Delimiter ',' -Encoding UTF8 } else { Write-Host "Validation Passed: URL output appears to be valid" -ForegroundColor Green $convertedCsvArray | Export-Csv -Path $outputCsvPath -NoTypeInformation -Delimiter ',' -Encoding UTF8 } Write-Host "Results exported to: $outputCsvPath" $openFileExport = Read-Host -Prompt "Do you want to open the file? (Select, Y/yes or N/no)" if ($openFileExport.ToLower() -eq 'y' -or $openFileExport -eq 'yes') { Invoke-Item $outputCsvPath } Exit #Start-Counter; break } default { # Default switch, catch any invalid user input and exit Write-Host "Invalid choice. Exiting the script." Start-Counter break } } |