Skip to content

Instantly share code, notes, and snippets.

@jpda
Last active September 25, 2015 19:19
Show Gist options
  • Select an option

  • Save jpda/5c114b83e229075ed062 to your computer and use it in GitHub Desktop.

Select an option

Save jpda/5c114b83e229075ed062 to your computer and use it in GitHub Desktop.
Windows 10 IoT Multi-device Deployment
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