Last active
September 25, 2015 19:19
-
-
Save jpda/5c114b83e229075ed062 to your computer and use it in GitHub Desktop.
Windows 10 IoT Multi-device Deployment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| param ( | |
| [string][Parameter(Mandatory = $true)]$DeviceName, | |
| [string][Parameter(Mandatory = $false)]$DefaultAppName = "IotCoreDefaultApp", | |
| [string][Parameter(Mandatory = $true)]$TargetAppName, | |
| [string][Parameter(Mandatory = $true)]$TargetAppPath, | |
| [string][Parameter(Mandatory = $true)]$DeviceUsername, | |
| [string][Parameter(Mandatory = $true)]$DevicePassword | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| Add-Type -Path "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Net.Http.dll" | |
| function Upload-File { | |
| param( | |
| [System.IO.FileSystemInfo]$Path, | |
| [string]$Uri | |
| ) | |
| Write-Host -NoNewline [Upload-File]: Sending $Path.Name to $Uri... | |
| $boundaryId = [System.Guid]::NewGuid().ToString() | |
| $wc = New-Object System.Net.Http.HttpClient($null) | |
| $mfdc = New-Object System.Net.Http.MultipartFormDataContent($boundaryId) | |
| $fileBytes = [byte[]][System.IO.File]::ReadAllBytes($Path.FullName) | |
| $wc.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Basic", [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("$DeviceUsername`:$DevicePassword"))) | |
| $bytes = New-Object System.Net.Http.ByteArrayContent -ArgumentList @(,$fileBytes) | |
| $mfdc.Add($bytes, "`"$Path`"", "`"$Path`"") | |
| $mfdc.Headers.Remove("Content-Type") | |
| $mfdc.Headers.TryAddWithoutValidation("Content-Type", "multipart/form-data; boundary=$boundaryId") | |
| $resp = $wc.PostAsync($Uri, $mfdc).Result | |
| Write-Host $resp.StatusCode $resp.StatusDescription | |
| } | |
| function Get-PiCredentials { | |
| $securePass = ConvertTo-SecureString $DevicePassword -AsPlainText -Force | |
| return New-Object System.Management.Automation.PSCredential($DeviceUsername, $securePass) | |
| } | |
| function Invoke-AuthenticatedWebRequest { | |
| param ( | |
| [string][Parameter(Mandatory=$true)]$Uri, | |
| [string] $Method = "Get", | |
| [switch] $ClearContentType, | |
| [object] $Body = "" | |
| ) | |
| $creds = Get-PiCredentials | |
| Write-Host -NoNewline [Invoke-AuthenticatedWebRequest]: Making $Method request to $Uri... | |
| if($Method -eq "Post" -and $ClearContentType){ #post with cleared content type | |
| if($Body -eq ""){ | |
| $req = Invoke-WebRequest -Uri $Uri -Credential $creds -Method $Method -ContentType "" | |
| } else { | |
| $req = Invoke-WebRequest -Uri $Uri -Credential $creds -Method $Method -Body $Body -ContentType "" | |
| } | |
| } elseif ($Method -eq "Post") { #post with default content type | |
| if($Body -eq ""){ | |
| $req = Invoke-WebRequest -Uri $Uri -Credential $creds -Method $Method | |
| } else { | |
| $req = Invoke-WebRequest -Uri $Uri -Credential $creds -Method $Method -Body $Body | |
| } | |
| } else { #get | |
| $req = Invoke-WebRequest -Uri $Uri -Credential $creds -Method $Method | |
| } | |
| Write-Host $req.StatusCode $req.StatusDescription | |
| return $req | |
| } | |
| function Remove-Dependency { | |
| [string]$PackageFullName = "" | |
| if($PackageFullName = "") { Write-Host Package not found, moving on... } | |
| Write-Host Removing $PackageFullName... | |
| Write-Host POSTing to "$uriRoot/uninstall?package=$PackageFullName" | |
| Invoke-AuthenticatedWebRequest -Uri "$uriRoot/uninstall?package=$PackageFullName" -Method Post -ClearContentType | |
| } | |
| function FindAndRemove-Package { | |
| param( | |
| [string]$Package | |
| ) | |
| if($Package -eq $null) { return } | |
| Write-Host -NoNewline Looking for package named $Package... | |
| $target = $apps.InstalledPackages | where { $_.Name -contains $Package } | |
| if($target -ne $null) { | |
| Write-Host found. Removing... | |
| Remove-Dependency -PackageFullName $target.PackageFullName | |
| } else { | |
| Write-Host not found. | |
| } | |
| } | |
| function Get-InstalledApps { | |
| Write-Host Getting installed packages... | |
| $request = Invoke-AuthenticatedWebRequest -Uri "$uriRoot/installed" | |
| return ConvertFrom-Json $request -Verbose | |
| } | |
| function Get-DeploymentStatus { | |
| $status = Invoke-AuthenticatedWebRequest -Uri "$uriDeploymentRoot/status" | |
| Write-Host $status | |
| } | |
| function Print-StartupInfo { | |
| Write-Host "Connecting to $DeviceName to deploy $TargetAppName" | |
| Write-Host "Endpoint: $deviceEndpoint" | |
| Write-Host "Public API: $uriRoot" | |
| Write-Host "Private API: $privateUriRoot" | |
| } | |
| function Encode-AppId { | |
| param ([object]$App) | |
| $encodedDefaultAppName = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($App.PackageRelativeId)) | |
| Write-Host Transformed $App.PackageRelativeId to $encodedDefaultAppName. Setting default... | |
| return [System.Uri]::EscapeDataString($encodedDefaultAppName) | |
| } | |
| function Get-AppByName { | |
| param ([string]$Name) | |
| return $apps.InstalledPackages | where { $_.Name -contains $Name } | |
| } | |
| function Set-DefaultAppWinRm { | |
| param ( | |
| [string]$PcName, | |
| [string]$AppName, | |
| [switch]$Reboot | |
| ) | |
| $winrmCred = Get-PiCredentials | |
| Get-Service -Name WinRM | Start-Service -Verbose | |
| Set-Item WSMan:\localhost\Client\TrustedHosts -Value "$PcName" -Force -Verbose | |
| $script = { | |
| & iotstartup add headed $using:AppName | |
| } | |
| if($Reboot) { | |
| $script = { | |
| & iotstartup add headed $using:AppName | |
| & shutdown /r /t 0 | |
| } | |
| } | |
| Invoke-Command -ComputerName $Name -Credential $winrmCred -ScriptBlock { $script } -Verbose | |
| } | |
| function Set-DefaultApp { | |
| param ( | |
| [string]$PcName, | |
| [string]$EncodedAppName, | |
| [switch]$Reboot | |
| ) | |
| $cred = Get-PiCredentials | |
| Invoke-AuthenticatedWebRequest -Uri "$privateUriRoot/setdefault?appid=$EncodedAppName" -Method Post -ClearContentType | |
| if($Reboot){ | |
| Invoke-AuthenticatedWebRequest -Uri "$apiRoot/control/reboot" -Method Post -ClearContentType | |
| } | |
| } | |
| $creds = Get-PiCredentials | |
| $deviceEndpoint = "$DeviceName`:8080" | |
| $apiRoot = "http://$deviceEndpoint/api" | |
| $uriRoot = "http://$deviceEndpoint/api/appx" | |
| $privateUriRoot = "http://$deviceEndpoint/api/iot/appx" | |
| $uriDeploymentRoot = "$uriRoot/deployment" | |
| #get package list | |
| $apps = Get-InstalledApps | |
| $defaultApp = Get-AppByName -Name $DefaultAppName | |
| $targetApp = Get-AppByName -Name $TargetAppName | |
| #set default app | |
| if($defaultApp -ne $null) { #found the default app, good to go | |
| $urlEncodedBase64App = Encode-AppId -App $defaultApp | |
| Write-Host POSTing to "$privateUriRoot/setdefault?appid=$urlEncodedBase64App" | |
| Invoke-AuthenticatedWebRequest -Uri "$privateUriRoot/setdefault?appid=$urlEncodedBase64App" -Method Post -ClearContentType | |
| } | |
| #uninstall target app, if exists | |
| FindAndRemove-Package -Package $targetApp.PackageFullName | |
| #resetting pending deployments | |
| Invoke-AuthenticatedWebRequest -Uri "$uriDeploymentRoot/reset" -Method Post -ClearContentType | |
| #install target + dependencies | |
| $appxFile = Get-ChildItem -Path $TargetAppPath -Filter *.appx | |
| $appxCert = Get-ChildItem -Path $TargetAppPath -Filter *.cer | |
| Write-Host Found $appxFile.Name | |
| Upload-File -Uri "$uriDeploymentRoot/package" -Path $appxFile | |
| Upload-File -Uri "$uriDeploymentRoot/certificate" -Path $appxCert | |
| #dependencies | |
| $dependencyFolder = Join-Path -Path $TargetAppPath -ChildPath "Dependencies/ARM" | |
| Write-Host Checking $dependencyFolder for dependencies... | |
| $dependencies = Get-ChildItem -Path $dependencyFolder | |
| Write-Host Found $dependencies.Length dependencies. Removing... | |
| foreach($dependency in $dependencies) { | |
| $name = $dependency.BaseName | |
| # Not removing ATM - doesn't appear to be necessary. | |
| #FindAndRemove-Package -Package $name | |
| #Upload-File -Uri "$uriDeploymentRoot/dependency" -Path $dependency | |
| } | |
| #commit changes | |
| Invoke-AuthenticatedWebRequest -Uri "$uriDeploymentRoot/commit" -Method Post -ClearContentType | |
| $apps = Get-InstalledApps | |
| $newApp = Encode-AppId -App (Get-AppByName -Name $TargetAppName) | |
| Invoke-AuthenticatedWebRequest -Uri "$apiRoot/taskmanager/start?appid=$newApp" -Method Post -ClearContentType | |
| Set-DefaultApp -PcName $DeviceName -EncodedAppName $newApp -Reboot |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment