Scripts/Update-OneDriveSetup.ps1

<#
    .SYNOPSIS
    Helper script to update OneDrive during Windows image creation
 
    .DESCRIPTION
    This script is designed to be used during creation of Windows images for deployment to client systems.
 
    The version of OneDrive bundled with a Windows release will often be out-of-date by the time of deployment to a client system. While OneDrive will update itself, some updates require Administrative privileges, which users may not have. By pre-installing the latest update we can hopefully avoid unwanted UAC prompts, while also streamlining the initial logon process.
 
    .PARAMETER SetupDestDir
    Path to the directory where we'll store a copy of the OneDrive setup file.
 
    The default is $env:ProgramData\Microsoft\OneDriveSetup.
 
    .PARAMETER SetupFilePath
    Path to the OneDrive setup file.
 
    The default is OneDriveSetup.exe in the current working directory.
 
    .PARAMETER SkipRunningSetup
    Skips executing OneDrive setup.
 
    .PARAMETER SkipUpdatingDefaultProfile
    Skips updating the default user profile to automatically run OneDrive setup on login.
 
    .EXAMPLE
    Update-OneDriveSetup
 
    Copies OneDrive setup to the default destination path, runs setup and updates the default user profile.
 
    .NOTES
    OneDrive release notes
    https://support.office.com/en-us/article/onedrive-release-notes-845dcf18-f921-435e-bf28-4e24b95e5fc0
 
    .LINK
    https://github.com/ralish/PSWinGlue
#>


#Requires -Version 3.0
#Requires -RunAsAdministrator

[CmdletBinding()]
Param(
    [ValidateNotNullOrEmpty()]
    [String]$SetupFilePath = 'OneDriveSetup.exe',

    [ValidateNotNullOrEmpty()]
    [String]$SetupDestDir = "$env:ProgramData\Microsoft\OneDriveSetup",

    [Switch]$SkipRunningSetup,
    [Switch]$SkipUpdatingDefaultProfile
)

$GetDefaultUserProfileDirectory = @'
[DllImport("userenv.dll", EntryPoint = "GetDefaultUserProfileDirectoryW", SetLastError = true)]
public static extern bool GetDefaultUserProfileDirectory(IntPtr lpProfileDir, out uint lpcchSize);
 
[DllImport("userenv.dll", CharSet = CharSet.Unicode, EntryPoint = "GetDefaultUserProfileDirectoryW", ExactSpelling = true, SetLastError = true)]
public static extern bool GetDefaultUserProfileDirectory(System.Text.StringBuilder lpProfileDir, out uint lpcchSize);
'@


if ($SkipRunningSetup -and $SkipUpdatingDefaultProfile) {
    throw 'Nothing to do as all operations skipped!'
}

$SetupFile = Get-Item -Path $SetupFilePath -ErrorAction Stop
if ($SetupFile -isnot [IO.FileInfo]) {
    throw 'OneDrive setup path is not a file: {0}' -f $SetupFilePath
} elseif ($SetupFile.Extension -ne '.exe') {
    throw 'OneDrive setup file must be an executable: {0}' -f $SetupFilePath
}

if (!(Test-Path -Path $SetupDestDir -PathType Container)) {
    Write-Verbose -Message 'Creating OneDrive setup directory ...'
    $null = New-Item -Path $SetupDestDir -ItemType Directory -Force -ErrorAction Ignore
}

$SetupDest = Get-Item -Path $SetupDestDir -ErrorAction Stop
if ($SetupDest -isnot [IO.DirectoryInfo]) {
    throw 'OneDrive setup destination is not a directory: {0}' -f $SetupDestDir
}

Write-Verbose -Message 'Copying OneDrive setup file ...'
$SetupDestFilePath = Join-Path -Path $SetupDestDir -ChildPath 'OneDriveSetup.exe'
Copy-Item -Path $SetupFilePath -Destination $SetupDestFilePath -Force
[IO.FileInfo]$SetupFilePath = Get-Item -Path $SetupDestFilePath -ErrorAction Stop

if (!$SkipRunningSetup) {
    Write-Verbose -Message 'Terminating any existing OneDrive setup ...'
    Stop-Process -Name 'OneDriveSetup' -ErrorAction Ignore

    Write-Verbose -Message 'Running OneDrive setup ...'
    $OneDriveSetup = Start-Process -FilePath $SetupFilePath.FullName -ArgumentList @('/silent') -Wait -PassThru
    if ($OneDriveSetup.ExitCode -notin ('0', '3010')) {
        Write-Warning -Message ('OneDrive setup returned exit code: {0}' -f $OneDriveSetup.ExitCode)
    }
}

if (!$SkipUpdatingDefaultProfile) {
    Write-Verbose -Message 'Updating OneDrive setup path for default user profile ...'

    if (!('PSWinGlue.UpdateOneDriveSetup' -as [Type])) {
        Add-Type -Namespace PSWinGlue -Name UpdateOneDriveSetup -MemberDefinition $GetDefaultUserProfileDirectory
    }

    $DefaultUserProfileBufSize = 0
    $null = [PSWinGlue.UpdateOneDriveSetup]::GetDefaultUserProfileDirectory([IntPtr]::Zero, [ref]$DefaultUserProfileBufSize)
    if (!$DefaultUserProfileBufSize) {
        throw 'Failed to determine buffer size for GetDefaultUserProfileDirectory().'
    }

    $DefaultUserProfilePath = New-Object -TypeName Text.StringBuilder -ArgumentList ($DefaultUserProfileBufSize - 1)
    if ([PSWinGlue.UpdateOneDriveSetup]::GetDefaultUserProfileDirectory($DefaultUserProfilePath, [ref]$DefaultUserProfileBufSize) -eq $false) {
        throw (New-Object -TypeName ComponentModel.Win32Exception)
    }

    $DefaultUserProfileHive = Join-Path -Path $DefaultUserProfilePath.ToString() -ChildPath 'NTUSER.DAT'
    Write-Debug -Message ('Default user profile registry hive: {0}' -f $DefaultUserProfileHive)

    $Registry = Start-Process -FilePath reg -ArgumentList @('LOAD', 'HKLM\OneDriveSetup', "`"$DefaultUserProfileHive`"") -Wait -PassThru
    if ($Registry.ExitCode -ne 0) {
        throw 'Failed to load the default user profile registry hive with error code: {0}' -f $Registry.ExitCode
    }

    Set-ItemProperty -Path 'HKLM:\OneDriveSetup\Software\Microsoft\Windows\CurrentVersion\Run' -Name 'OneDriveSetup' -Value $SetupFilePath.FullName

    $Registry = Start-Process -FilePath reg -ArgumentList @('UNLOAD', 'HKLM\OneDriveSetup') -Wait -PassThru
    if ($Registry.ExitCode -ne 0) {
        throw 'Failed to unload the default user profile registry hive with error code: {0}' -f $Registry.ExitCode
    }
}