Tim D'Annecy

Windows

#Windows #Azure #AVD

Recently, a company I work with requested a way for users to access a web app that they were hosting on-premises, but didn't want to open up connections to the app to the internet. The app is out of support from the vendor and can't be opened outside of their secure network.

We planned to allow access to the app by instructing users to connect over a VPN, but we found this would be a hassle for users, as they would need to install the VPN client, connect to the VPN, and then launch a browser to access the app. In this case, the site also requires IE11 with a custom extension to be installed. These requirements just to access a single site really made it a headache for users and the Helpdesk received regular tickets from staff who had issues accessing the site.

Instead of the VPN approach, we deployed a Microsoft Edge shortcut in Azure Virtual Desktop. This approach has advantages because it allows users to launch a browser window and jump onto the network that the Host Pool is connected to. If you have your networking configured correctly in Azure, you can access internal sites and apps without going over the internet directly from the client PC. This also allows you to control the browser version and other settings that are configured in the Host Pool. If paired with Intune policies, you can define the browser settings and extensions that are available to users and require certain sites to open in Internet Explorer Compatibility Mode. This could be helpful if you have a legacy app that only works in IE11 and requires a specific extension to be installed.

In this post, I'll show you how to deploy a Microsoft Edge shortcut in Azure Virtual Desktop.

Read more...

#Windows #Azure #AVD #Intune

UPDATE: On April 18, 2023, Microsoft released a Public Preview of the new Azure Virtual Desktop app in the Microsoft Store. This should fix the issues noted in this post. Download the new app here: http://aka.ms/AVDStoreClient

I recently had an issue with an Azure AD Joined host pool in Azure Virtual Desktop where users could not successfully login using their Azure AD credentials.

In our environment, both the client computer and the Azure Virtual Desktop host computer are Azure AD Joined. The user was able to log in to their Azure AD Joined computer, but when they tried to log in to the Azure Virtual Desktop host computer, they would get an error message:

Couldn't connect. Something went wrong when we tried to connect. If this keeps happening, ask your admin or tech support for help. Error code: 0x9735 Extended error code: 0x0 Activity ID: {e194ae11-b2ed-4d33-9520-c1d5ed140000}

Screenshot of error message

Read more...

#Windows #Intune

A company I'm working with requested an Application Deployment of Word LTSC using a Multiple Activation Key (MAK) purchased through Volume Licensing in their Intune/Endpoint environment.

This post will discuss the steps I used to package this app.

Read more...

#Azure #PowerShell #Windows

If you’ve added a new session host to an existing Azure Virtual Desktop host pool, you might get a Windows Activation error watermark notifying you that the Windows license wasn’t found:

Screenshot of a Windows 10 desktop, showing a Windows activation error

Activate Windows. Go to Settings to activate Windows.

When I got the ticket from users complaining about the watermark, I started brainstorming. I thought I might be able to fix this issue several ways:

  1. License users with Microsoft 365 E5.

  2. Manually add an existing Windows license (only possible if you’re running a stock image of Windows, not the Azure-specific Windows Enterprise Multi-Session).

Since I don’t have those E5 licenses already and I’m running the multi-session OS, it would add cost to purchase and I would need to get approval.

Instead, I found that you can check the VM license by running this command in PowerShell (change the XXX values to match your Resource Group and VM name):

Import-Module AzCLI 
Connect-AzAccount
Get-AzVM -ResourceGroupName XXXresourcegroupXXX -Name XXXvirtualmachineXXX

After running that command, the string that you want to focus on is LicenseType. If it says Windows_Client, you are good to go and Azure will apply the license on the OS-level.

If it is null or displays as {}, that could be a cause for the Activation error. You can run this PowerShell command in the AzCLI (edit the XXX values to match your environment):

$rg = XXXresourcegroupXXX
$vm = XXXvirtualmachineXXX
$vm.LicenseType = ‘Windows_Client’

Get-AzVM -ResourceGroupName $rg -Name $vm | Update-AzVM 

I wish Azure had a built in Troubleshooting function or feature to “quick fix” this issue, but I couldn’t find one.

Putting this here for my notes when I have to fix this issue again.

Discuss...

#Intune #Windows

A company I'm working with requested an Intune deployment of Adobe Acrobat Pro DC to users on machines running Windows 10 or 11.

I wasn't able to find great documentation about how to deploy this using Intune, so I wanted to write all of this down. There are some guides going around about MSP transforms, but that's not necessary for a simple deployment.

  1. First, download the Adobe Acrobat Pro single app package and select the 64 bit Windows installer:

Screenshot of Adobe Acrobat Pro installer download page

  1. Then, download the Microsoft Win32 Content Prep Tool.

  2. Unzip all of the files to that they're in a format as follows:

    • C:\temp\Microsoft-Win32-Content-Prep-Tool-Master
    • C:\temp\Adobe Acrobat
  3. Now that you have the prereqs, we need to build the .intunewin package for Intune. Open Command Prompt and cd into the C:\temp\Microsoft-Win32-Content-Prep-Tool-Master folder and run .\IntuneWinAppUtil.exe

  4. Step through the prompts to build the package:

    • Please specify the source folder: C:\temp\Adobe Acrobat
    • Please specify the setup file: C:\temp\Adobe Acrobat\setup.exe
    • Please specify the output folder: C:\temp\Adobe Acrobat
    • Do you want to specify catalog folder (Y/N)? n

Screenshot of Windows Terminal running the Microsoft Win32 Content Prep tool for Adobe Acrobat Pro

Now that the intunewin package is built, it's time to upload it on the Microsoft Endpoint and create an app package. You can close the Win32 Content Prep tool.

  1. Log into the Intune/Endpoint dashboard and navigate to Apps > Windows > Add. Set the “App type” to Windows app (Win32):

Screenshot of Intune dashboard, Windows app creation wizard

  1. On the “App information” tab, upload the file you created at C:\temp\Adobe Acrobat\setup.intunewin and fill out the required fields to continue:

Screenshot of Intune dashboard, Windows app creation wizard, App information tab

  1. On the “Program” tab, enter the following values:
    • Install command: setup.exe /sAll
    • Uninstall command: msiexec /x "{AC76BA86-1033-FFFF-7760-BC15014EA700}" /q
    • Install behavior: System
    • Device restart behavior: App install may force a device restart
    • Specify return codes to indicate post-installation behavior: Leave as default.

Screenshot of the Intune dashboard, Add App wizard, Program tab

  1. On the “Requirements” tab, enter the following values:
    • Operating system architecture: 64-bit
    • Minimum operating system: Windows 10 1607

Screenshot of the Intune dashboard, Add App wizard, Requirements tab

  1. On the “Detection rules” tab, set “Rules format” to Manually configure detection rules, then click the “+ Add” button. Change the “Rule type” to MSI and set the “MSI product code” field to {AC76BA86-1033-FFFF-7760-BC15014EA700}. Set the “MSI product version check” to No:

Screenshot of the Intune dashboard, Add App wizard, Detection rules tab

  1. On the “Dependencies” and “Supersedence” tabs, just leave the defaults.

  2. On the “Assignments” tab, target your deployment group or users for installation. You may want to hide the “End user notifications” so that it's completely silent, but this is optional:

Screenshot of the Intune dashboard, Add App wizard, Assignments tab

After uploading the file, your application deployment will be complete:

Screenshot of the Intune dashboard, Add App wizard, file upload

Machines will begin to check in and pick up the app assignment. I find that installation can take up to 30 minutes, so be patient!

Screenshot of a Windows 11 desktop notification for Intune/Endpoint application deployment

Screenshot of a Windows 10 desktop notification for Intune/Endpoint application deployment

Discuss...

#Sharepoint #Windows

One of the companies that I'm working with wants to migrate all of their files on a local file share and move them into a Sharepoint site.

To do this, I ran the official Microsoft Sharepoint Migration Tool from the file server. I set my source as the shared folder containing 1.8 TB of files and the target destination as the Documents library in a newly created Sharepoint site.

After letting the tool run for a few hours, the migration tool completed and reported that it had successfully moved 209,726 files, but 500 were not moved because the scanner has issues with special characters:

Screenshot of the Microsoft Sharepoint Migration Tool, migration report page.

They're an international organization and has French, Spanish, and Portuguese workers that connect into the shared drive. They're also using a mix of MacOS and Windows PCs, so the folders can get pretty cluttered with UTF and non-Windows character sets.

From the logs, the Sharepoint Migration Tool seems to only allow ANSI characters, probably for URLs and other internal compatibility. I needed to rename all of the non-migrated files from the accented characters into the ANSI equivalents and then run an incremental sync.

To fix the accent characters, I could go in manually an rename all of the files, but I needed an easier way to automate this.

Instead of figuring out the right regex command and running a rename command in Powershell, I decided to keep it simple and use the Bulk Rename Utility app that I've used for other projects, including the project I wrote about to redact Microsoft Word files quickly.

After downloading and installing the app on the file server, open it and target the share location on the local drive.

Inside the app, click on the option at Special > Character Translations.

I copied and pasted all of the accented characters and their ANSI equivalents into Excel and formatted it for Bulk Rename Utility to be able to use the list. Copy and paste this list into this Character Translation popup window:

Á=A
á=a
À=A
à=a
Â=A
â=a
Ã=A
ã=a
Ä=A
ä=a
Å=A
å=a
ç=c
Ç=C
É=E
é=e
È=E
è=e
Ê=E
ê=e
Ë=E
ë=e
Í=I
í=i
Ì=I
ì=i
Î=I
î=i
Ï=I
ï=i
Ó=O
ó=o
Ò=O
ò=o
Ô=O
ô=o
Ö=O
ö=o
Õ=O
õ=o
Ú=U
ú=u
Ù=U
ù=u
Û=U
û=u
Ü=U
ü=u

After pasting in the info, press OK and return to the main window:

Screenshot of Bulk Rename Utility, character translation popup with characters

Make sure that you have the following options checked:

✓ Files > Files

✓ Files > Folders

✓ Files > Subfolders

✓ Special > Character Translations

Screenshot of Bulk Rename Utility, subfolders and character translations selected.

After checking those boxes, click into the file list at the top right of the window and press the Ctrl + A keys on the keyboard to select all items. This could take a few seconds, depending on the number of files that you have.

To verify, click the “Preview” button, then press “Rename” if the renaming task looks correct:

Screenshot of Bulk Rename Utility, file rename preview popup window

After renaming these files, I returned to the Sharepoint Migration Tool and started the Incremental sync. The sync completed successfully without any file scan issues or unrecognized characters.

Footer

Discuss...

#Exchange #Powershell #Windows

I recently had a request to spin up an on-prem Exchange 2016 server and connect it with a new Azure AD tenant and Exchange Online. I created a new Azure VM with Server 2016 and ran the latest patches and Windows updates before installing anything.

After going through the regular Exchange server setup process and installing Azure AD connect on the Domain Controller, I needed to install and run the Exchange Hybrid Configuration Wizard on the Exchange server.

After running the install, I ran into an error that would not let me continue:

Failed Setup terminiated with an Exit Code 1603

To work through this, I found a post [A] that details some changes that need to be made in the registry relating to the TLS

I wrote up this quick script so that it can be run quickly without mucking through regedit:

Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v2.0.50727' -Name 'SystemDefaultTlsVersions' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v2.0.50727' -Name 'SchUseStrongCrypto' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v2.0.50727' -Name 'SystemDefaultTlsVersions' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v2.0.50727' -Name 'SchUseStrongCrypto' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -Name 'SystemDefaultTlsVersions' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -Name 'SystemDefaultTlsVersions' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319' -Name 'SchUseStrongCrypto' -PropertyType dword -Value 1
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -Name 'DisabledByDefault' -PropertyType dword -value 0
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Name 'DisabledByDefault' -PropertyType dword -value 0
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client' -Name 'Enabled' -PropertyType dword -value 1
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server' -Name 'Enabled' -PropertyType dword -value 1

Discuss...

#Powershell #Windows

Recently, I was trying to run a Powershell command to retrieve the list of Local Admin accounts on a domain-joined machine.

I ran this command in Powershell 5.1 and 7.2.5:

Get-LocalGroupMember -Group Administrators

I received an error that something was wrong with the command.

Get-LocalGroupMember : Failed to compare two elements in the array. At line:1 char:1

A SuperUser post [A] suggested that the error is caused by invalid admin accounts that are not cleaned up during domain join or AAD join. The post suggests running the following Powershell command to remove the invalid admin accounts:

# Clean-AdministratorGroup.ps1
# https://gist.github.com/tdannecy/daf057ab9b9280290efb34677d9c0ea8
# https://superuser.com/a/1481036

function Clean-AdministratorGroup {
    $administrators = @(
        ([ADSI]"WinNT://./Administrators").psbase.Invoke('Members') |
        ForEach-Object { 
            $_.GetType().InvokeMember('AdsPath', 'GetProperty', $null, $($_), $null) 
        }
    ) -match '^WinNT';
        
    $administrators = $administrators -replace 'WinNT://', ''
        
    $administrators | ForEach-Object {   
        if ($_ -like "$env:COMPUTERNAME/*" -or $_ -like "AzureAd/*") {
            continue;
        }
        Remove-LocalGroupMember -group 'Administrators' -member $_
    }
}
Clean-AdministratorGroup

Footer image

Discuss...

#Windows #Powershell #Networking

A company I'm working with is looking to move from an OpenVPN connection to a Meraki VPN on newly installed MX hardware.

To accomplish this, I wrote a short script that can be deployed in GPO that adds the new VPN connection and uninstalls the existing OpenVPN application.

Here's the script:

# Migrate-VPN.ps1
# Adds a new Meraki VPN config and removes the existing OpenVPN GUI application.
# Tim D'Annecy 2022-08-04

Start-Transcript -Path 'C:\temp\Migrate-VPN.log'
function Add-VPN {
  $ConnectionName = 'New VPN'
  $ServerAddress = 'XXXyourhostnameXXX'
  $PresharedKey = 'XXXyourpskXXX'

  $check = Get-VpnConnection -Name $ConnectionName -AllUserConnection -ErrorAction SilentlyContinue

  if ($check) {
    Write-Host 'VPN connection named' $ConnectionName 'already exists. Exiting.'
  }
  else {
    Write-Host 'Adding VPN connection' $ConnectionName
    Add-VpnConnection `
      -Name $ConnectionName `
      -ServerAddress $ServerAddress `
      -TunnelType L2tp `
      -EncryptionLevel Optional `
      -L2tpPsk $PresharedKey `
      -AuthenticationMethod Pap `
      -RememberCredential $true `
      -AllUserConnection  $true `
      -Force `
      -WarningAction SilentlyContinue
  }
}
Add-VPN

function Remove-OpenVPN {
  if (Test-Path -Path 'C:\Program Files\OpenVPN') {
    Write-Host 'OpenVPN installed. Removing...'
    (Get-WmiObject -Class Win32_Product -filter "Name LIKE 'OpenVPN%'").Uninstall() | Out-Null
  }
  else {
    Write-Host 'OpenVPN not installed. Exiting.'
  }
}
Remove-OpenVPN

Stop-Transcript

Copy and paste this script into your \\domain.com\SYSVOL\scripts folder and save it as Migrate-VPN.ps1.

Once you've done this, go into Group Policy Management and create a new GPO Object that does 3 things:

  • Create a folder at C:\temp

  • Copy the file from \\domain.com\SYSVOL\scripts\Migrate-VPN.ps1 to C:\temp\Migrate-VPN.ps1

  • Run a Scheduled Task that calls Powershell to run the script every hour on the hour

With these things in place, you should see the changes trickle out to your environment as the machines check in.

Discuss...

#Windows #Powershell #Azure

I have a few companies that I work with that are using a traditional Active Directory domain environment (GPO, WSUS, etc.) but are not using an inventory tool like Intune or PDQ.

One of the biggest issues that they report is that they aren't able to get any information about live machines in their environment.

Gathering this information is a critical step in moving to cloud-based endpoint management. You won't be able to decommission a domain if there are objects that still check back in to on-prem infrastructure for management.

To work around this, I wrote a Powershell script that runs on a local computer, gathers some information about its config, then pushes it to an Azure Table. This collected data can then be exported to a .csv file and can be ingested into other tools for analytics.

Azure Storage Account and Table

Open the Azure portal and create a new Storage Account. Keep all of the defaults and step through the wizard.

Once the deployment is complete, navigate to the Storage Account and select Tables. In this view, create a table named “domaineddevices”:

Screenshot of Azure portal, viewing a Table inside a Storage Account

After creating the Table, navigate to the Access keys blade. Copy this key and paste it into the $accesskey line in the script below:

Screenshot of Azure portal, viewing the Access Keys inside a Storage Account

For better compatibility in your environment, change the Minimum TLS version to 1.0 under the Configuration blade. This will allow older versions of Windows to check in with the Table:

Screenshot of Azure portal, viewing the Configuration blade inside a Storage Account

Once this Storage Account is setup, move to the Powershell section and paste in your Key that you copied earlier.

Powershell script

I was struggling with writing to an Azure Table, specifically creating the needed encryption pieces. I found a few posts [A] [A] that had the main crypto pieces that I needed. I wrote the rest of the information gathering lines and tweaked it to successfully upload the data that the script gathered to Azure Tables.

Here's my modified Powershell script:

# Check-DomainStatus.ps1

$ScriptVersion = 20220802

Start-Transcript -Path 'C:\temp\Check-DomainStatus.log' -Append -NoClobber
$storageAccount = 'STORAGEACCOUNT' # Update these values for your environment
$accesskey = 'XXX' # Update these values for your environment
$TableName = 'domaineddevices'
$DomainName = 'XXX' # Update these values for your environment

function InsertReplaceTableEntity($TableName, $PartitionKey, $RowKey, $entity) {
    $version = "2017-04-17"
    $resource = "$tableName(PartitionKey='$PartitionKey',RowKey='$Rowkey')"
    $table_url = "https://$storageAccount.table.core.windows.net/$resource"
    $GMTTime = (Get-Date).ToUniversalTime().toString('R')
    $stringToSign = "$GMTTime`n/$storageAccount/$resource"
    $hmacsha = New-Object System.Security.Cryptography.HMACSHA256
    $hmacsha.key = [Convert]::FromBase64String($accesskey)
    $signature = $hmacsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))
    $signature = [Convert]::ToBase64String($signature)
    $headers = @{
        'x-ms-date'    = $GMTTime
        Authorization  = "SharedKeyLite " + $storageAccount + ":" + $signature
        "x-ms-version" = $version
        Accept         = "application/json;odata=fullmetadata"
    }
    $body = $entity | ConvertTo-Json
    Invoke-RestMethod -Method PUT -Uri $table_url -Headers $headers -Body $body -ContentType application/json
}

# GPO calculation
$RegPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Extension-List\{00000000-0000-0000-0000-000000000000}'
$LowTime = Get-ItemProperty -path $RegPath -name "EndTimeLo"
$HighTime = Get-ItemProperty -path $RegPath -name "EndTimeHi"
$CompTime = ([long]$HighTime.EndTimeHi -shl 32) + [long] $LowTime.EndTimeLo
$GPOProcessDate = [DateTime]::FromFileTime($CompTime)

# Reduce some calls
$dsregcmd = (C:\Windows\System32\dsregcmd.exe /status)
$computerinfo = Get-ComputerInfo 
$wmiobjectw32 = Get-WmiObject -class win32_bios

$body = @{
    # Required values 
    RowKey                     = $($env:COMPUTERNAME)
    PartitionKey               = 'domaineddevices'

    # Optional values
    AzureADJoinedStatus        = ($dsregcmd | Select-String 'AzureADJoined' | Out-String).replace(' ', '').replace("`n", '').replace("`r", '')
    AdminAccountPresent     = if ((Get-LocalUser).Name -Contains 'LocalAdmin' ) { $true } else { $false }
    Domain                     = $env:USERDOMAIN
    DomainJoinStatus           = ($dsregcmd | Select-String 'DomainJoined' | Out-String).replace(' ', '').replace("`n", '').replace("`r", '')
    EnterpriseJoinedStatus     = ($dsregcmd | Select-String 'EnterpriseJoined' | Out-String).replace(' ', '').replace("`n", '').replace("`r", '')
    FortiClientVPNFilesPresent = if (Test-Path -Path 'C:\Program Files\Fortinet\FortiClient' -ErrorAction SilentlyContinue) { $true } else { $false }
    FortiClientVPNRunning      = if (Get-Process -ProcessName 'FortiTray' -ErrorAction SilentlyContinue) { $true } else { $false }
    # # GPOProcessDate             = [datetime]::FromFileTime(([Int64] ((Get-ItemProperty -Path "Registry::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Extension-List\{00000000-0000-0000-0000-000000000000}").startTimeHi) -shl 32) -bor ((Get-ItemProperty -Path "Registry::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\State\Machine\Extension-List\{00000000-0000-0000-0000-000000000000}").startTimeLo)).toString()
    GPOProcessDate             = [datetime]$GPOProcessDate
    LogonServer                = $env:LOGONSERVER | Out-String
    Manufacturer               = ($wmiobjectw32).Manufacturer
    NetworkMAC                 = (Get-WmiObject win32_networkadapterconfiguration | Select-Object Description, MACaddress, IPAddress, DefaultIPGateway, DNSDomain) | Out-String
    OSBuild                    = (($computerinfo).OsHardwareAbstractionLayer | Out-String).replace(' ', '').replace("`n", '').replace("`r", '')
    OSEdition                  = (($computerinfo).WindowsProductName | Out-String).replace(' ', '').replace("`n", '').replace("`r", '')
    OSVersion                  = [int32]((($computerinfo).WindowsVersion | Out-String).replace(' ', '').replace("`n", '').replace("`r", ''))
    QuestODMAgentRunning       = if (Get-Process -ProcessName 'BinaryTree.ADM.Agent' -ErrorAction SilentlyContinue) { $true } else { $false }
    QuestODMFilesPresent       = if (Test-Path -Path 'C:\Program Files (x86)\Quest\On Demand Migration Active Directory Agent' -ErrorAction SilentlyContinue) { $true } else { $false }
    ScriptVersion              = [int32]$ScriptVersion
    SerialNumber               = ($wmiobjectw32).SerialNumber
    StorageType                = (Get-PhysicalDisk).MediaType | Out-String
    Traceroute                 = (Test-NetConnection -TraceRoute $DomainName -Hops 5 -ErrorAction SilentlyContinue) | Out-String
    Uptime                     = (New-TimeSpan -Start (Get-CimInstance -Class Win32_OperatingSystem -Property LastBootUpTime).LastBootUpTime -End (Get-Date)).ToString()
    Users                      = (Get-ChildItem -Path 'C:\Users\' | ForEach-Object {
            $size = Get-ChildItem -Path $_.FullName -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Average -Sum -ErrorAction SilentlyContinue
            Write-Output $_.Name, $_.LastWriteTime.ToString("yyyy-MM-dd"), "$([math]::round($size.sum/1GB)) GB", '---' }) | Out-String
    WindowsVPNManualStatus     = (Get-VpnConnection -ErrorAction SilentlyContinue).Name | Out-String
    WindowsVPNStatus           = (Get-VpnConnection -AllUserConnection -ErrorAction SilentlyContinue).Name | Out-String
}

Write-Host 'Creating new or updating table entity'
InsertReplaceTableEntity -TableName $TableName -entity $body -RowKey $body.RowKey -PartitionKey $body.PartitionKey

Write-Host 'Outputting all values for log:'
Write-Host $body 
Stop-Transcript

Save that script to somewhere like SYSVOL.

Group Policy Object

After saving the file to the domain controller, create a GPO with the following items:

Computer Configuration > Preferences > Windows Settings > File

General tab:

Screenshot of Group Policy Management Editor File wizard

  • Source file(s): \\domain.local\SYSVOL\domain.local\scripts\Check-DomainStatus.ps1

  • Destination FIle: C:\temp\Check-DomainStatus.ps1

Computer Configuration > Control Panel Settings > Scheduled Tasks

General tab:

Screenshot of Group Policy Management Editor Scheduled Tasks wizard

  • Action: Replace

  • Name: Check-DomainStatus

  • When running the task, use the following user account: NT AUTHORITY\System

  • Run whether user is logged on or not

  • Run with highest privileges

  • Configure for: Windows Vista or Windows Server 2008

Triggers tab:

Screenshot of Group Policy Management Editor Scheduled Tasks wizard

  • New > Begin the task: On a schedule

  • Daily, Recur every: 1 days

  • Repeat task every: 1 hour for a duration of: 1 day

  • Enabled

Actions tab:

  • New > Action > “Start a program”

  • Program/script: powershell.exe

  • Add arguments(optional): -NoProfile -ExecutionPolicy Bypass -File "c:\temp\Check-DomainStatus.ps1"

Conditions tab:

Screenshot of Group Policy Management Editor Scheduled Tasks wizard Conditions tab

  • All options unchecked.

Settings tab:

Screenshot of Group Policy Management Editor Scheduled Tasks wizard Settings tab

  • Allow task to be run on demand

  • Run task as soon as possible after a scheduled start is missed

  • Stop the task if it runs longer than 1 hour

  • If the running task does not end when requested, force it to stop

  • If the task is already running, then the following rule applies: Do not start a new instance

Once deployed, the task will be available on the local machine in Task Scheduler and can be started immediately:

Screenshot of Task Scheduler MMC

Azure Storage Explorer

After deploying the script, you can use the Azure Storage Explorer app to view and export the data as it arrives:

Screenshot of Azure Storage Explorer opening a Table

Discuss...