Powershell script is working locally but throws 'Exception calling "ReadAllText" ' on on Azure release pipeline

360 Views Asked by At

As my title says I am trying to get following actions taken on Azure release pipeline:

  1. Open .zip file
  2. Find the JSON file with this name
  3. Edit it and save it
  4. Close the .zip file

Also, as my title says I managed to make it work locally, and it works. However, when I add PowerShell task

Run a PowerShell script on Linux, macOS, or Windows

to my Azure release pipeline with code below, it will throw exception:

Exception calling "ReadAllText" with "2" argument(s): "Could not find file 'C:\azagent\A1\_work\r17\a\appsettings.Production.json'."
At C:\azagent\A1\_work\_temp\75ed88f3-4260-42d8-afba-43d0aa0f6a93.ps1:40 char:1+ $JsonData = [IO.File]::ReadAllText($configFile, [Text.Encoding]::GetEncoding(
125 ...

I have tried everything as seen in my code snippet, but could not make it work:

# cd to the agent artifacts directory (where the zip file exist)
cd $env:Agent_ReleaseDirectory
cd "_MyProject.ProjectApi-CI\drop"

$fileToEdit = "appsettings.Production.json"

[Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem");
# Open zip and find the particular file (assumes only one inside the Zip file)
$zipfileName = dir -filter '*.zip'
$zip =  [System.IO.Compression.ZipFile]::Open($zipfileName.FullName, "Update")

# $configFile = $zip.Entries.Where({$_.name -like $fileToEdit}) | Select-Object -first 1
$configFile = $zip.Entries.Where({$_.name -like $fileToEdit}) 

# ==================================================================================

# Read the contents of the file
# and IMMEDIATELY Convert file to JSON
$JsonData = [IO.File]::ReadAllText($configFile, [Text.Encoding]::GetEncoding(1252)) | ConvertFrom-Json

# Read the contents of the file
# $content = Get-Content -Path $configFile

# Convert file to JSON
# $JsonData = $text | ConvertFrom-Json

# List all of the Properties of this JSON file
#($JsonData | Get-Member -Type NoteProperty).Name

# Access property by name
# $JsonData.AppSettings.IsDebug

# Change selected variables with new values
$JsonData.AppSettings.setting1 = $true
$JsonData.AppSettings.setting2 = "Some text here"
$JsonData.AppSettings.setting3 = "Some more text here"


# update (json) file with new (json) content (this works only when there is not zip file stuff)
# $JsonData | ConvertTo-Json | Set-Content $configFile

# This will return "PSCustomObject"
# $JsonData.GetType();

# In order to write content of "$JsonData" variable, I need to convert it to string 
# It would also work with "$desiredFile.Write($jsonString | ConvertTo-Json)" but depth is only 2 by default
$jsonString = ConvertTo-Json -InputObject $JsonData -Depth 5
# $jsonString

# ==================================================================================

# Update file with new content
$desiredFile = [System.IO.StreamWriter]($configFile).Open()
$desiredFile.BaseStream.SetLength(0)

# Insert the $text to the file and close
$desiredFile.Write($jsonString)
$desiredFile.Flush()
$desiredFile.Close()

# Write the changes and close the zip file
$zip.Dispose()

So the issue lies in line:

$JsonData = [IO.File]::ReadAllText($configFile, [Text.Encoding]::GetEncoding(1252)) | ConvertFrom-Json

But I could not find any other way to read text from .zip. For example Get-Content requires path. From what I am understanding it is throwing exception because [IO.File]::ReadAllText expects path as second variable, but then again, why is it working locally ?

EDIT: I have the answer why is it working locally. When you open up the file locally with WinRar it will add this file to the temp folder. That's why it is working locally. However, at the time of this writing, I have not found a way to do it as there is something wrong with Powershell versions on Azure Release pipelines.

1

There are 1 best solutions below

4
Santiago Squarzon On

Not sure if this will solve your issue but for sure is more compatible with every system which may be why your current code is failing. This uses the ZipArchive Class instead of ZipFile to open the Zip entry stream then that wrapped stream is read with a StreamReader. This method should work in all PowerShell versions starting from Windows PowerShell 5.1 up to PowerShell Preview 7.3.0-rc.1.

Add-Type -AssemblyName System.IO.Compression

$encoding   = [Text.Encoding]::GetEncoding(1252)
$file       = Get-Item .\myZip.zip
$fileStream = $file.Open([IO.FileMode]::Open)
$zipArchive = [IO.Compression.ZipArchive]::new($fileStream, [IO.Compression.ZipArchiveMode]::Update)
# If you know the exact relative path to the file, use this method
# otherwise you can still use your `.Where{ $_.Name -like ... }` method
$zipEntry   = $zipArchive.GetEntry('dir1/dir2/myJson.json')
$zipStream  = $zipEntry.Open()
$reader     = [IO.StreamReader]::new($zipStream, $encoding)
$JsonData = $reader.ReadToEnd() | ConvertFrom-Json
$JsonData.AppSettings.setting1 = $true
$JsonData.AppSettings.setting2 = "Some text here"
$JsonData.AppSettings.setting3 = "Some more text here"
$json   = ConvertTo-Json -InputObject $JsonData -Depth 5
$zipStream.SetLength(0) # => Starts writing from the beginning of the stream
$writer = [IO.StreamWriter]::new($zipStream, $encoding)
$writer.BaseStream.SetLength(0)
$writer.Write($json)
$writer, $reader, $zipEntry, $zipArchive, $fileStream | ForEach-Object Dispose