PowerShell, Remote Administration, Microsoft, Office, 365, Azure, Azure Active Directory, Bulk Scripting, Automation
# Copyright © Jack T. Neely, 06/29/2023 # This script downloads the Azure CLI, installs all az cli extensions, and updates all az cli extensions. # Note: You may see many warnings during the installation process. Most of these warnings are harmless but be sure you understand the consequences when running this script. # Begin script # # Install az cli via MSI over PowerShell ( https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-windows?tabs=powershell#install-or-update ) $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; Remove-Item .\AzureCLI.msi # Set az config to automatically install az cli extensions if an extension command is run az config set extension.use_dynamic_install=yes_without_prompt # Set default network credentials to allow proxy services. (This may be necessary for some machines.) (New-Object System.Net.WebClient).Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials # Obtain the list of all available extensions $extensions = az extension list-available --output tsv --query '[].name' # Install each extension based on name foreach ($extension in $extensions) { try { Write-Output "Installing $extension" az extension add --name $extension } catch { Write-Error "An error occurred. Unable to install $extension." } } # Update each extension based on name foreach ($extension in $extensions) { try { Write-Output "Installing $extension" az extension update --name $extension } catch { Write-Error "An error occurred. Unable to update $extension." } } Write-Host "Script completed." -ForegroundColor Green # End script #
# Define Azure AD and App Details $ClientID = "" $ClientSecret = "" $TenantID = "" $YourId = "" $RequestTimeout = 120 # Request timeout in seconds # Start local log transcript $LogFilePath = Join-Path $PSScriptRoot ".log" Start-Transcript -Path $LogFilePath # Get Access Token $TokenUrl = "https://login.microsoftonline.com/$TenantID/oauth2/v2.0/token" $TokenBody = @{ Client_Id = $ClientID Scope = "https://graph.microsoft.com/.default" Client_Secret = $ClientSecret Grant_Type = "client_credentials" } $TokenResponse = Invoke-RestMethod -Uri $TokenUrl -Method Post -Body $TokenBody -TimeoutSec $RequestTimeout $AccessToken = $TokenResponse.Access_Token # Define Headers for Graph API $Headers = @{ Authorization = "Bearer $AccessToken" } # Query Microsoft Graph API for Managed Devices $BaseUrl = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices" $Filter = "`$filter=operatingSystem+eq+'Windows'" $ApiUrl = $BaseUrl + '?' + $Filter Function Invoke-WithRetry { Param ( [ScriptBlock]$ScriptBlock, [Int]$MaxRetries = 5, [Int]$TimeoutSec = 60 ) $RetryCount = 0 Do { Try { Return & $ScriptBlock } Catch { $IsThrottled = $_.Exception.Response.StatusCode -eq 'TooManyRequests' -or $_.Exception.Response.StatusCode -eq '429' $IsConnectionClosed = $_.Exception.Message -like "*unexpected EOF or 0 bytes from the transport stream*" If ($IsThrottled -or $IsConnectionClosed) { $RetryCount++ $BaseWaitTime = If ($_.Exception.Response.Headers['Retry-After']) { $_.Exception.Response.Headers['Retry-After'] } Else { [Math]::Pow(2, $RetryCount) } # Adding randomness to the wait time $WaitTime = $BaseWaitTime + (Get-Random -Minimum 1 -Maximum 5) Write-Host "Request throttled or connection closed. Retrying in $WaitTime seconds..." Start-Sleep -Seconds $WaitTime } Else { Throw $_ } } } While ($RetryCount -lt $MaxRetries) Throw "Maximum number of retries reached." } # Fetch the devices (Filter for Windows Devices) $ManagedDevicesResponse = @() $Top = "`$top=100" $NextPageUrl = $ApiUrl + '&' + $Top Do { Write-Host "Querying URL: $NextPageUrl" $Response = Invoke-WithRetry -ScriptBlock { Invoke-RestMethod -Uri $NextPageUrl -Headers $Headers -TimeoutSec $RequestTimeout } -TimeoutSec $RequestTimeout $ManagedDevicesResponse += $Response.Value $NextPageUrl = $Response."@odata.nextLink" } While ($NextPageUrl) # Loop Through Devices and Retrieve Information $ReportData = @() $TotalDevices = $ManagedDevicesResponse.Count $Counter = 0 ForEach ($Device in $ManagedDevicesResponse) { try { $Counter++ Write-Progress -Activity "Processing Devices" -Status "$Counter of $TotalDevices" -PercentComplete (($Counter / $TotalDevices) * 100) $DeviceId = $Device.Id # Define the URL for fetching detected apps for this device $DetectedAppsUrl = "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceId/detectedApps" Write-Host "Fetching apps for DeviceId $DeviceId." # Fetch all detected apps for this device $InstalledApps = @() Do { $Response = Invoke-WithRetry -ScriptBlock { Invoke-RestMethod -Uri $DetectedAppsUrl -Headers $Headers -TimeoutSec 60 } If ($Response.Value) { $InstalledApps += $Response.Value } $DetectedAppsUrl = $Response.'@odata.nextLink' } While ($DetectedAppsUrl) Write-Host "Apps detected on device $DeviceId : $($InstalledApps.Count)" # Filter Apps Containing "powerbi" and Add to Report Data $MatchingApps = $InstalledApps | Where-Object { $_.DisplayName -like "*powerbi*" } Write-Host "Apps containing 'powerbi' on device $DeviceId : $($MatchingApps.Count)" ForEach ($App in $MatchingApps) { $ReportData += [PSCustomObject]@{ UserPrincipalName = $Device.UserPrincipalName DeviceName = $Device.DeviceName Manufacturer = $Device.Manufacturer Model = $Device.Model OperatingSystem = $Device.OperatingSystem AppName = $App.DisplayName AppVersion = $App.Version } } } catch { Write-Host "An error occurred processing device $DeviceId. Error: $($_.Exception.Message)" continue } } # Sort the Report Data $SortedReportData = $ReportData | Sort-Object DeviceName, UserPrincipalName, AppName, AppVersion, OperatingSystem # Export Sorted Report Data to CSV $CurrentDate = Get-Date -Format "MM-dd-yyyy" $CsvPath = "PowerBIReportedInstallations-$CurrentDate.csv" $SortedReportData | Export-Csv -Path $CsvPath -NoTypeInformation # Setup for sending the email $SendMailUrl = "https://graph.microsoft.com/v1.0/users/$YourId/sendMail" $EmailHeaders = @{ Authorization = "Bearer $AccessToken" "Content-Type" = "application/json" } $EmailBody = @{ Message = @{ Subject = "Power BI Reported Installations - $CurrentDate" ToRecipients = @( @{ EmailAddress = @{ Address = "" } } @{ EmailAddress = @{ Address = "" } } ) CcRecipients = @( @{ EmailAddress = @{ Address = "" } } ) From = @{ EmailAddress = @{ Address = "" } } Attachments = @( @{ '@odata.type' = "#microsoft.graph.fileAttachment" ContentBytes = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($CsvPath)) ContentType = "text/csv" Name = "PowerBIReportedInstallations-$CurrentDate.csv" } ) } SaveToSentItems = $true } | ConvertTo-Json -Depth 10 # Send the email Invoke-WithRetry -ScriptBlock { Invoke-RestMethod -Uri $SendMailUrl -Headers $EmailHeaders -Method Post -Body $EmailBody -TimeoutSec $RequestTimeout } -TimeoutSec $RequestTimeout Write-Host "Email sent." Write-Host "Script completed." # Stop local log transcript Stop-Transcript
Write-Host "`r`n`r`n`r`n`r`nWhat is the full path for your file? `r`nE.g. C:\leads.csv" -ForegroundColor Magenta $path = Read-Host -Prompt "Path" $leads = Import-Csv -Path $path -Header "Record ID","First Name","Last Name","Email","Office Number","Primary Associated Company ID","Associated Company","Record ID - Company","Company name","Cell Number","City","Industry" $count = $leads.count $results = @() Write-Host "`r`n`r`nProcessing . . . Do not exit." -ForegroundColor Magenta Write-Host "This could take some time. Have a coffee. Maybe a few." -ForegroundColor Cyan Start-Sleep -Seconds 3 $i = 0 foreach ($lead in $leads) { $domain = ($lead.Email).Split("@")[1] $autodiscover = Resolve-DnsName -Name autodiscover.$domain -Type CNAME -DnsOnly -QuickTimeout -ErrorAction SilentlyContinue $i++ if ($autodiscover.NameHost -eq "autodiscover.outlook.com") { $results += $lead #Write-Host $domain -ForegroundColor Cyan #Write-Host $lead.Email -ForegroundColor Cyan #Write-Host $autodiscover.NameHost "`r`n" #Write-Host $results | ft } else {} Write-Host "$i out of $count" } $results | Export-Csv -Path C:\M365_Leads.csv -NoTypeInformation Write-Host "`r`n`r`n`r`n`r`nScript complete!" -ForegroundColor Magenta Write-Host "Results output to C:\M365_Leads.csv" -ForegroundColor Cyan Write-Host "`r`nPress enter to exit." -ForegroundColor Magenta
- Validate CNAME autodisocver record, modify client registry, and repair Outlook autodiscover based on type of Exchange services (using email address as input).
Note: This PowerShell script gets passed into cmd, allowing a seamless click-to-run user experience.
@echo off PowerShell -Command "Get-Content '%~dpnx0' | Select-Object -Skip 3 | Out-String | Invoke-Expression" goto :eof Write-Host "`r`n`r`nWhat is your email address?" -ForegroundColor Cyan $domain = (Read-Host).Split("@")[1] Write-Host "`r`n`r`nFetching CNAME record for autodiscover.$domain . . ." -ForegroundColor Cyan $d = Resolve-DnsName -Name autodiscover.$domain -Type CNAME -DnsOnly -QuickTimeout -ErrorAction SilentlyContinue $d | ft Name,NameHost $dS = ($d.NameHost -eq "autodiscover.outlook.com") -or ($d.NameHost -eq "adr.exghost.com") if ($d.NameHost -eq $null ) { Write-Host "`r`n`r`nAutodiscover record not published. Please contact your administrator or try again." -ForegroundColor Cyan } elseif (!$dS) { Write-Host "`r`n`r`nAutodiscover record not valid. Please contact your administrator or try again." -ForegroundColor Cyan } else { Write-Host "`r`n`r`nAutodiscover record valid" Write-Host "`r`n`r`nTesting connection for autodiscover.$domain . . ." -ForegroundColor Cyan $p = Test-Connection autodiscover.$domain -Count 1 -Quiet If (!$p) { Do { Write-Host "`r`nping autodiscover.$domain was NOT successful.`r`nTesting connection again . . ." $p = Test-Connection autodiscover.$domain -Count 1 -Quiet Write-Host "`r`n`r`n( Press Ctrl C to cancel )" } until ($p) } Write-Host "`r`nping autodiscover.$domain successful`r`n`r`n" if ($dS) { If ($dS -and $p -and $d.NameHost -eq "autodiscover.outlook.com") { Write-Host "No autodiscover issues detected.`r`nMicrosoft 365 Autodiscover Registry Edit is recommended." } elseif ($dS -and $p -and $d.NameHost -eq "adr.exghost.com") { Write-Host "No autodiscover issues detected.`r`nHosted Exchange Autodiscover Registry Edit is recommended." } else { Write-Host "Something went wrong." } } Write-Host "`r`n`r`nBacking up registry . . ." -ForegroundColor Cyan Set-Location -Path HKCU:\SOFTWARE\Microsoft\Office\16.0\Outlook\AutoDiscover $reg = 'HKCU:\SOFTWARE\Microsoft\Office\16.0\Outlook\AutoDiscover' $out = "$temp\backup.reg" reg export HKCU\SOFTWARE\Microsoft\Office\16.0\Outlook\AutoDiscover C:\Temp\backup.reg /y Write-Host "`r`n`r`n$reg backed up to $out" -ForegroundColor Cyan Write-Host "`r`nPrevious registry can be restored by double clicking this file to run.`r`nThis will reverse the following registry changes:`r`n`r`n" Set-ItemProperty -Path $reg -Name 'PreferLocalXML' -Value 0 Set-ItemProperty -Path $reg -Name 'ExcludeHTTPRedirect' -Value 0 Set-ItemProperty -Path $reg -Name 'PreferLocalXML' -Value 1 Set-ItemProperty -Path $reg -Name 'ExcludeHttpsAutoDiscoverDomain' -Value 1 Set-ItemProperty -Path $reg -Name 'ExcludeScpLookup' -Value 1 Set-ItemProperty -Path $reg -Name 'ExcludeSrvLookup' -Value 1 Set-ItemProperty -Path $reg -Name 'ExcludeSrvRecord' -Value 1 if ($d.NameHost -eq "autodiscover.outlook.com") { Set-ItemProperty -Path $reg -Name 'ExcludeExplicitO365Endpoint' -Value 0 Write-Host "`r`n`r`nM365 Autodiscover Registry Edit successfully executed.`r`n`r`nProgram complete." -ForegroundColor Cyan } elseif ($d.NameHost -eq "adr.exghost.com") { Set-ItemProperty -Path $reg -Name 'ExcludeExplicitO365Endpoint' -Value 1 Write-Host "`r`n`r`nHosted Exchange Autodiscover Registry Edit successfully executed.`r`n`r`nProgram complete." -ForegroundColor Cyan } else { Write-Host "`r`n`r`nSomething went wrong. Please terminate this program." -ForegroundColor Cyan } } Write-Host "`r`n`r`n`r`n`r`nPress enter to exit.`r`n`r`n`r`n" -ForegroundColor Magenta Read-Host
$objUsers = Get-Mailbox -Resultsize Unlimited Foreach ($objUser in $objUsers){ $RemoveAutoMapping = Get-MailboxPermission -Identity $($objUser.Alias) | Where {$.AccessRights -eq "FullAccess"} $RemoveAutoMapping | Remove-MailboxPermission $RemoveAutoMapping | Foreach {Add-MailboxPermission -Identity $.Identity -User $.User -AccessRights:FullAccess -AutoMapping $False} }