Automation Workflows
The below PowerShell script automates the process of importing layers into the Civillo. It handles authentication, interacts with the Civillo API to select organizations and projects, reads layer data from a CSV file, creates layers in the specified project, and assigns them to directories. The script provides user-friendly menus for organization and project selection and handles various aspects of the import process.
The below script requires PowerShell version 7.0 or above. To install PowerShell 7 please watch this installation video.
Users can copy, edit and save this script. To review how to use the script, you can run:
PS > get-help .\{name-of-script}.ps1or
PS > get-help .\{name-of-script}.ps1 -FullAnd can be run using the following parameters (at a minimum)
PS > .\{name-of-script}.ps1 \ -civilloKey "{YOUR-CIVILLO-KEY}" \ -civilloSecret "{YOUR-CIVILLO-SECRET}" \ -inputcsv "PATH\TO\file.csv"or
PS > .\{name-of-script}.ps1 \ -civilloKey "{YOUR-CIVILLO-KEY}" \ -civilloSecret "{YOUR-CIVILLO-SECRET}" \ -inputCsv "PATH\TO\file.csv" \ -organization "{organization nickname}" -projectId "{Project ID}"- The organization nickname can be retrieved using the Organizations’ endpoint.
- The Project ID can be retrieved using the Projects’ endpoint.
An example CSV file can be downloaded from here.
PowerShell script
Section titled “PowerShell script”<# .SYNOPSIS Imports local CAD, IFC and 12d files to a Civillo project, and assigns the layer to a directory.
.DESCRIPTION Imports local CAD, IFC and 12d files to a Civillo project, and assigns the layer to a directory.
.PARAMETER civilloKey Civillo Key. Log into Civillo and navigate to your profile (https://app.civillo.com/i/user/profile). At the bottom of the profile page, you can generate a new key or copy an existing key.
.PARAMETER civilloSecret Civillo Secret. Log into Civillo and navigate to your profile (https://app.civillo.com/i/user/profile). At the bottom of the profile page, you can generate a new key with secret, or use your previously saved secret.
.PARAMETER inputCsv Path to input CSV file which contains the following columns: File,LayerName,Description,Directory,srid,units,makeGlobal,convert3dFaces,convert3dFacesToTerrain
.PARAMETER organization Civillo organization nickname. This can be found in the URL of your civillo project. E.g: https://app.civillo.com/{ORGANIZATION-NAME}/project/{PROJECT-ID}/?L9;V2#17/-37.75085/144.997355
.PARAMETER projectId Civillo project ID of the organization. This can be found in the URL of your civillo project. E.g: https://app.civillo.com/{ORGANIZATION-NAME}/project/{PROJECT-ID}/?L9;V2#17/-37.75085/144.997355
.EXAMPLE PS> .\civillo-import-csv.ps1 -civilloKey "{YOUR-CIVILLO-KEY}" -civilloSecret "{YOUR-CIVILLO-SECRET}" -inputCsv "{PATH/TO/CSV/FILE}"
.EXAMPLE PS> .\civillo-import-csv.ps1 -civilloKey "{YOUR-CIVILLO-KEY}" -civilloSecret "{YOUR-CIVILLO-SECRET}" -inputCsv "{PATH/TO/CSV/FILE}" -organization "{ORGANIZATION ABBREVIATION}"
.EXAMPLE PS> .\civillo-import-csv.ps1 -civilloKey "{YOUR-CIVILLO-KEY}" -civilloSecret "{YOUR-CIVILLO-SECRET}" -inputCsv "{PATH/TO/CSV/FILE}" -organization "{ORGANIZATION ABBREVIATION}" -projectId {PROJECT-ID}#>
#Requires -Version 7.0param ( [Parameter(Position=1, Mandatory=$true)] [string]$civilloKey, [Parameter(Position=2, Mandatory=$true)] [string]$civilloSecret, [Parameter(Position=3, Mandatory=$true)] [string]$inputCsv, [Parameter(Position=4, Mandatory=$false)] [string]$organization, [Parameter(Position=5, Mandatory=$false)] [Int16]$projectId)
Add-Type -AssemblyName System.Windows.Forms# FunctionsFunction DrawMenu { ## Support function to the Menu function below param ($menuItems, $menuPosition, $menuTitle) $fcolor = $host.UI.RawUI.ForegroundColor $bcolor = $host.UI.RawUI.BackgroundColor $l = $menuItems.length + 1 cls Write-Host "$menuTitle" -fore $fcolor -back $bcolor Write-Host "" Write-debug "L: $l MenuItems: $menuItems MenuPosition: $menuposition" for ($i = 0; $i -le $l;$i++) { Write-Host " " -NoNewLine if ($i -eq $menuPosition) { Write-Host "$($menuItems[$i])" -fore $bcolor -back $fcolor } else { Write-Host "$($menuItems[$i])" -fore $fcolor -back $bcolor } }}
Function Menu { ## Generate a small "DOS-like" menu. ## Choose a menuitem using up and down arrows, select by pressing ENTER param ([array]$menuItems, $menuTitle = "MENU") $vkeycode = 0 $pos = 0 DrawMenu $menuItems $pos $menuTitle While ($vkeycode -ne 13) { $press = $host.ui.rawui.readkey("NoEcho,IncludeKeyDown") $vkeycode = $press.virtualkeycode Write-host "$($press.character)" -NoNewLine If ($vkeycode -eq 38) {$pos--} If ($vkeycode -eq 40) {$pos++} if ($pos -lt 0) {$pos = $menuItems.length -1} if ($pos -ge $menuItems.length) {$pos = 0} DrawMenu $menuItems $pos $menuTitle } Write-Output $($menuItems[$pos])}
Function Invoke-OpenFileDialog { ## Open a file dialog window to select CSV. Param ( [Parameter(Mandatory=$true, Position=0)] [string] $DialogTitle, [Parameter(Mandatory=$true, Position=1)] [string] $InitialDirectory, [Parameter(Mandatory=$true, Position=2)] [string] $Filter, [Parameter(Mandatory=$true, Position=3)] [boolean] $TopMost )
if ($InitialDirectory -ne "") { if (!(Test-Path -LiteralPath $InitialDirectory -PathType Container)) { $InitialDirectory = ([System.IO.FileInfo]$InitialDirectory).DirectoryName } }
# Create a streaming image by streaming the base64 string to a bitmap stream source. $IconBase64 = "iVBORw0KGgoAAAANSUhEUgAAABsAAAAgCAYAAADjaQM7AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsEAAA7BAbiRa+0AAATQSURBVEhL7ZZ/TBxFFMd3Znfv9g6O3xypCVRSaFoCtS2QFkmhVFEoqRCLFStJTUHapPWPGk3xL1NtY2JMNRoTU4SmSI8fVUtTQgsmQikmgFZNCRxi0wrFMx7g3XFHub3d2fXNsSAgAv2hiUk/yWR33pu335k3b2eXecj/DqRd75uG+nqeZdlVDodDCA0LW8ViPB4TE3Nzc3LyHW3IvYmdrakJlmU5HloiQigJTFEqw8gcyw7JhFwLCQ4e4jhuOL+gwDEdMc2yYjabjW1va1sjimKqJElbMcaZhJBIcE3CSqwsx12FVbSDv/flsrKp6ajFWVSspaXF9JvNtsnn8+XAzAvg4eupHR5+Ay5f6vT6Vr1e31NUVOSm9pUyK/a73Y4vNTdvUQjZB+nJAYHVqqoyIObgeb6O1+lO5+Tmfh9lNhMt5K7xi1VVVpaBwCEQ2EAF/A6EZI7nTxoMhveKi4tH/cb7BFVUVJTLkvTOjMgMsJpXYQ/e17oPBAxpy1MVRev+haIoa4eGh7HWfSAgS031Lo9n8oIkE8jc/HqByvsRCuG8TqfrhHb9uT17xjTXPYFeueqOXne7I1s/8ctRH2HWavZ50ElAs0MbRBj3YYR69YIwBns8ABXqDQ8P/+Pbnh7H5pQUvDM3VwGbrIXOAx1rsgo/M0GHzWTS9pitSZSwoVwiasrCPVwInQCMIbB6Fe6psBNEMGRA9Hq9fdRHh4GN7j+PWfZd1H3ZYhqSzQe+kOIzbD5mKDmAfJw+2hQxIeJ9korzQTNyOeG50LF0InOhfUEQtvmtljNVu0WVTaoSMsy8quwnCFdGmgIsu8XuYTwxkuER1V0SYbaB5CN3IzwDHF234uLjN8xOoerTigpVkZ2XQp8eGZPUkxyjYgWhaxLmPl8dJFzJFPt/DXUPJnimyHbY20yiMokgHKiFLwkU2ZGS0tIPZsVq6xt0HpezGxFx8IeIHR/1ysa3WIVkUR9H04KYnzDHfS2y/MUEvdyX4WrDZNKRICp8FmHgvFSYdSBuWrhy2MOOLWlpTyQlJsrzkvtZdXW0x+Pp5JDi0gebSyzC4+mKKB7zKWoQ9dPBtEE1TrAY9TIs167yfHuS3jeY5v5GUe844r0EbxUJTvNJcrjRaLwRaDK9XlhYaJ+Jn8e5hoZkp9PZI8uSGGJkDzdGFba43O7j8B6+BKnzQ4Po7bSw3+KEKuiHT8x3E4i9EhMc0FX/VKiNeubyNzFKfV1dtsvlaoLzUqdjkSU6gi8vwQVBm2T3h15Z2SGB0qKBABU3C+ze2mdjazXTLIseR88XFX0VEhLyDLweXh9R994ala+fGD+X/UbMzTxDYMBOA4u76UMX1iXtwwP7eQ6dn7bM558m6AdWmDo+Pk5nuIb2MUbWQF45HrMxvfHE7agszud9UyRKKk0vFTKyiIkUcN6ZgthmOn4hS4pRKk6dioNDuRVOiFjapy8onAp9gZz8duTGjIufjIQ9OTE59SKImaIDuJrT+Y9a/IGLsKwY5UJjY5zdbrfAb0GqZvKLQlVag/TKkRf2H2rRzEuyok8I/LjcgH+N7fDOtGom/7FEFGW9lzEImmlZVrSyGawDA1xnR8drcFsKbRLSe/TAwYOX/c5/i66uLsOA1arTug/5L2GYPwFXOg3LzbJqMQAAAABJRU5ErkJggg==" $iconBytes = [Convert]::FromBase64String($IconBase64) $stream = New-Object IO.MemoryStream($iconBytes, 0, $iconBytes.Length) $stream.Write($iconBytes, 0, $iconBytes.Length)
# Create a new form to hold the object and set it properties for the main # OpenFileDialog form. $OpenFileDialogForm = New-Object System.Windows.Forms.Form $OpenFileDialogForm.Icon = [System.Drawing.Icon]::FromHandle((New-Object System.Drawing.Bitmap -Argument $stream).GetHIcon()) $OpenFileDialogForm.TopMost = $TopMost
# Set the properties of the main OpenFileDialog along with using the # OpenFileDialogForm properties from above. $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog $OpenFileDialog.Filter = $Filter $OpenFileDialog.InitialDirectory = $InitialDirectory $OpenFileDialog.Title = $DialogTitle $userClicked = $OpenFileDialog.ShowDialog($OpenFileDialogForm) $OpenFileDialog.Dispose()
if ($userClicked -eq 'OK') { return $OpenFileDialog.FileName } else { return "" }}
$civilloUriBase = "https://app.civillo.com/api/v1"
## Check for the Civillo Key parameter for this script;if ($null -eq $civilloKey) { $civilloKey = read-host -Prompt "Enter Civillo Key: "}## Check for the Civillo Secret parameter for this script;if ($null -eq $civilloSecret) { $civilloSecret = read-host -Prompt "Enter Civillo Secret: "}## Encode the Civillo Key and Secret into a Base64 string$encoded = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("$($civilloKey):$($civilloSecret)"))
## Set the header authorization for the API requests$headers = @{ 'Authorization' = "Bearer $($encoded)"}
## Validate the CSV inputif ($null -eq $inputCsv) { $dialogTitle = 'Select a CSV file' $initialDirectory = (New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path $dialogFilter = 'CSV (*.csv)|*.csv' $inputCsv = Invoke-OpenFileDialog -DialogTitle $dialogTitle -InitialDirectory $initialDirectory -Filter $dialogFilter -TopMost $true}if ($inputCsv -eq ""){ Write-Host "No CSV file supplied. Exiting." -ForegroundColor Yellow exit}$fileExists = Test-Path -Path $inputCsv -PathType Leafif ($fileExists -eq $false){ Write-Host "No CSV file located at $($inputCsv). Exiting." -ForegroundColor Yellow exit}
## Validate the input CSV has the correct headers$csvHeaders = Get-Content -Path $inputCsv | Select-Object -First 1$expectedHeaders = "file,layername,description,directory,srid,units,makeglobal,convert3dfaces,convert3dfacestoterrain"
if ($csvHeaders.ToLower() -ne $expectedHeaders) { Write-Host "Inconsistent CSV file headers. This script is expecting these column names in the input CSV: " -ForegroundColor Yellow Write-Host "---------------------------------------------------------------------------------------------------------" Write-Host "File, LayerName, Description, Directory, SRID, Units, MakeGlobal, Convert3dFaces, Convert3dFacesToTerrain" Write-Host "---------------------------------------------------------------------------------------------------------" Write-Host "Exiting." -ForegroundColor Yellow exit}
$csv = Import-csv -path $inputCsv
## Processing Step 1. Select an Organizationif ([string]::IsNullOrEmpty($organization)) {
$uri = "$($civilloUriBase)/applications" Try { $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -contenttype 'application/json' } Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red exit }
If ($response.Count -eq 1) { $organization = $response[0].nickname } ElseIf ($response.Count -lt 6) { $organizationArray = @() foreach($org in $response) { $organizationArray += $org.name } $selection = Menu $organizationArray "Please choose an organization:" foreach($org in $response) { if($org.name -eq $selection){ $organization = $org.nickname } } } Else { $organizationFilter = Read-Host -Prompt "Set an organization filter" $organizationArray = @() foreach($org in $response) { if ($org.name -Match $organizationFilter) { $organizationArray += $org.name } } $selection = Menu $organizationArray "Please choose an organization:" foreach($org in $response) { if($org.name -eq $selection){ $organization = $org.nickname } } }}
Start-Sleep -Seconds 1## Processing Step 2. Select an organization's project$uri = "$($civilloUriBase)/$($organization)/projects"Try { $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -contenttype 'application/json'}Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red exit}
$projectsArray = @()foreach($app in $response) { $projectsArray += $app.name}
if (-not($response.id -contains $projectId)) { $projectId = $null}
if ([string]::IsNullOrEmpty($projectId) -Or $projectId -eq 0) { $selection = Menu $projectsArray "Please choose a project:" $projectId = 1; Write-Host "Using $selection project" foreach($app in $response) { if($app.name -eq $selection){ $projectId = $app.id } }}
## Processing Step 3. Select a list of valid SRS inputs for CivilloStart-Sleep -Seconds 1$uri = "$($civilloUriBase)/global/reference_systems"Try { $projections = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -contenttype 'application/json'}Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red}
## Processing Step 4. Fetch the current list of layers in the projectStart-Sleep -Seconds 1$uri = "$($civilloUriBase)/$($organization)/projects/$($projectId)/layers"Try { $projectLayers = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -contenttype 'application/json'}Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red}
## Processing Step 5. Loop over the CSV rows.foreach($line in $csv){ ## Processing Step 5.1. Validate the input file $outputFile = Split-Path $line.file -leaf $fileExists = Test-Path -Path $line.file -PathType Leaf if ($fileExists -eq $false) { Write-Host "$($outputFile) not located at $($line.file). Skipping." -ForegroundColor Yellow continue }
## Processing Step 5.2. This script will only process CAD. IFC and 12d files $knownExtensionsForScript = @(".dgn", ".dxf", ".dwg", ".ifc", ".12da", ".12daz") $fileExtension = [System.IO.Path]::GetExtension($line.file.ToLower()) if ($knownExtensionsForScript.IndexOf($fileExtension) -eq -1) { Write-Host "$($outputFile) will not be processes by this script. Only dgn, dxf, dwg, ifc, 12da and 12daz are accepted. Skipping." -ForegroundColor Yellow continue }
## Processing Step 5.3. Validate the CSV SRID value [int]$srid = -1 foreach($projection in $projections) { if ($projection.name -Contains $line.srid ) { $srid = $projection.value } } if ($srid -eq -1) { $defaultFilter = "GDA2020" # Using the Civillo projections, select the actual SRS. Write-Host "$($line.srid) not found as a Civillo projection. Please select a projection" -ForegroundColor Yellow [string]$projectionFilter = Read-Host -Prompt "Set a SRS filter [$($defaultFilter)]" $projectionFilter = ($defaultFilter,$projectionFilter)[[bool]$projectionFilter]
$projectionSeparatedList = @() foreach($projection in $projections) { if ($projection.name -Match $projectionFilter) { $projectionSeparatedList += $projection.name } } $sridName = Menu $projectionSeparatedList "Select SRS for file $($outputFile):" foreach($projection in $projections) { if ($projection.name -Contains $sridName ) { $srid = $projection.value } } }
## Processing Step 5.4. Validate the Units $unitsEnum = @("m", "mm") $units = $line.units.ToLower() if ($unitsEnum.IndexOf($units) -eq -1) { $units = Menu $unitsEnum "Please choose a valid unit for the file $($outputFile):" }
$trueFalseEnum = @("true", "false") ## Processing Step 5.5. Validate makeGlobal $makeGlobal = $line.makeglobal.ToLower() if ($trueFalseEnum.IndexOf($makeGlobal) -eq -1) { $makeGlobal = Menu $trueFalseEnum "Please choose a valid `"Make Global`" for the file $($outputFile):" }
## Processing Step 5.5. Validate convert3dFaces $convert3dFaces = $line.convert3dFaces.ToLower() if ($trueFalseEnum.IndexOf($convert3dFaces) -eq -1) { $convert3dFaces = Menu $trueFalseEnum "Please choose a valid `"Convert 3D Faces`" for the file $($outputFile):" }
## Processing Step 5.5. Validate convert3dFacesToTerrain $convert3dFacesToTerrain = $line.convert3dFacesToTerrain.ToLower() if ($trueFalseEnum.IndexOf($convert3dFaces) -eq -1) { $convert3dFacesToTerrain = Menu $trueFalseEnum "Please choose a valid `"Convert 3D Faces to Terrain`" for the file $($outputFile):" }
Write-Host "Processing $($outputFile)" -ForegroundColor green [int64]$lastModifiedDate = Get-Date -Date ((Get-Item $line.file).LastWriteTime) -UFormat %s $lastModifiedDate = $lastModifiedDate * 1000
[int]$mode = 0 [int]$replaceLayerId = 0 #Does the layer already exist by name? $fileExistsInProject = $false foreach($layer in $projectLayers) { if ($layer.name.Trim() -eq $line.layername.Trim()) { $mode = 1 $replaceLayerId = $layer.layerId Start-Sleep -Seconds 1 # Check to see if last revision fileLastModified matches our $lastModifiedDate $uri = "$($civilloUriBase)/$($organization)/projects/$($projectId)/layers/$($layer.layerId)" Try{ $layerMetadata = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -contenttype 'application/json' } Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red } if ($layerMetadata) { $lastRevisionModifiedDate = $layerMetadata.revisions[0].source.fileLastModified if ($lastRevisionModifiedDate -eq $lastModifiedDate) { Write-Host "$($outputFile) version currently loaded to the `"$selection`" project. Skipping." -ForegroundColor Yellow $fileExistsInProject = $true } } } } if ($fileExistsInProject) { continue }
## Processing Step 5.6. Create the body JSON as part of the layer create POST $body = @{"mode"="$($mode)" "fileNames"=@("$($outputFile)") "fileLastModifieds"=@($($lastModifiedDate)) "title"="$($line.layername)" "permitSelf"="true" "makeGlobal"="$($makeGlobal)" "makeOnByDefault"="false" "srid"=$($srid) "description"="$($line.description)" "units"="$($units)" "convert3dFaces"="$($convert3dFaces)" "convert3dFacesToTerrain"="$($convert3dFacesToTerrain)" } | ConvertTo-Json
if ($mode -eq 1) { $body = @{"mode"="$($mode)" "fileNames"=@("$($outputFile)") "replaceLayerId"=$replaceLayerId "fileLastModifieds"=@($($lastModifiedDate)) "title"="$($line.layername)" "permitSelf"="true" "makeGlobal"="$($makeGlobal)" "makeOnByDefault"="false" "srid"=$($srid) "description"="$($line.description)" "units"="$($units)" "convert3dFaces"="$($convert3dFaces)" "convert3dFacesToTerrain"="$($convert3dFacesToTerrain)" } | ConvertTo-Json }
## Processing Step 5.7. Make POST preflight request to generate Civillo Job Start-Sleep -Seconds 1 $uri = "$($civilloUriBase)/$($organization)/projects/$($projectId)/layers" Try { $preflight = Invoke-RestMethod -Uri $uri -Headers $headers -Method Post -Body $body -ContentType "application/json" } Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red } Start-Sleep -Seconds 1
## Processing Step 5.8. Upload the file to Civillo processPath # Construct the URL with query parameters $queryParams = @{ 'token' = $preflight.token 'application' = $preflight.application 'jobID' = $preflight.job } $queryString = ($queryParams.GetEnumerator() | ForEach-Object { $_.Key + '=' + [System.Uri]::EscapeDataString($_.Value) }) -join '&' $requestUri = $preflight.processPath + '?' + $queryString
Try { $Form = @{file=Get-Item -Path $line.File} $response = Invoke-RestMethod $requestUri -Method 'POST' -Form $Form } Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message }
## Processing Step 5.9. Request the progress of the Job every 30 seconds Start-Sleep -Seconds 1 $completeResponse = $false $errorResponse = "" $counter = 1 do { $counter++ $uri = "$($civilloUriBase)/$($organization)/projects/$($projectId)/layers/processing?jobId=$($preflight.job)" $processing = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -ContentType "application/json" $completeResponse = $processing.complete $errorResponse = $processing.error if ($errorResponse) { Write-Host "Error: " $description -ForegroundColor Red break } Write-Progress -Activity ">" -Status "$($processing.status):" -PercentComplete $processing.progress Start-Sleep -Seconds 30 if ($counter -gt 50) { Write-Host "Processing $($outputFile) longer than 25 minutes. Aborting watch" -ForegroundColor Red } } while ($completeResponse -eq $false) Write-Progress -Completed -Activity "Completed"}# Refresh the list of layersStart-Sleep -Seconds 1$uri = "$($civilloUriBase)/$($organization)/projects/$($projectId)/layers"Try { $projectLayers = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get -contenttype 'application/json'}Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red exit}
## Processing Step 6. Assign Layers to their directoriesforeach($line in $csv){ $outputFile = Split-Path $line.file -leaf foreach($layer in $projectLayers) { if ($layer.name -eq $line.layername) { Start-Sleep -Seconds 1 $uri = "$($civilloUriBase)/$($organization)/projects/$($projectId)/layer-directory" Try { $body = @{"layerId"=$layer.layerId "path"="$($line.directory)" } | ConvertTo-Json Invoke-RestMethod -Uri $uri -Headers $headers -Method Put -Body $body -contenttype 'application/json' Write-Host "$($outputFile) added to directory `"$($line.directory)`"." -ForegroundColor Green } Catch { Write-Host $_.ErrorDetails -ForegroundColor Red Write-Host $_.Exception.Message -ForegroundColor Red exit } } }}
Write-Host "Finished layer import." -ForegroundColor Greenexit