I needed to copy some content to my azure application that the Build and deploy that I constructed for it wouldn’t need to do every deploy every time. So my quest began on how do I upload files to an Azure application. The most common and recognized way of uploading files to azure applications is through webdeploy. I didn’t think I needed to package up and use webdeploy so I sought out a way to do this with PowerShell. This post is about that pursuit.
So I started with get-childitem -recurse “$downloadFolder\content”. Now that I had my content in a variable called $files I can put this in a foreach loop and use Octavie van Haaften‘s Upload-FileToWebapp.
During the upload of the files I need to determine if the file from my local disk is a File or Directory. I used the following classes to determine this:
If the item was a directory then I had to make the upload location match the location on disk. I did this through a little bit of replacement logic and used the $kudufolder as my variable to use for the upload function from Octavie.
The same holds true for the upload of a file. The only difference between the file and the directory is the /. When you are uploading/creating a directory / to kudu means a directory.
Recently I have been working with @developermj on a class that he wrote for deploying code to a server from a zip file. This blog article is about how that code works.
To Start this off we need to gain access to the dot net classes that have the features for zipping and unzipping files in them:
These will get added with two statements using and Add-type
#requires -version 5.0
using namespace System.IO
using namespace System.IO.Compression
param(
[Parameter(Mandatory=$true)][string]$sourceZip,
[Parameter(Mandatory=$true)][string]$destPath
)
add-type -assemblyname 'System.IO.Compression'
add-type -assemblyname 'System.IO.Compression.FileSystem'
Then we’ll build the first part of our utility which is our function to deploy the files. This function is where all the magic is:
function Deploy-Files {
param(
[ValidateNotNullOrEmpty()][FileInfo]$sourceZip,
[ValidateNotNullOrEmpty()][DirectoryInfo]$destFolder
)
if (-not $sourceZip.Exists) {
throw "Zip $($sourceZip.Name) does not exist"
}
[ZipArchive]$archive = [ZipFile]::Open($sourceZip, "Read")
[DeployFile[]]$files = $archive.Entries | where-object {$_.Length -gt 0} `
| %{[ArchiveFile]::new($_)}
if ($files.Length -eq 0) {
Write-Information "No files to copy"
}
$hasWritten = $false
foreach ($file in $files) {
[FileInfo]$destFile = "$destFolder$($file.GetName())"
$copied = $file.TryCopy($destFile)
if ($copied) { $hasWritten = $true }
}
Write-Information "Done"
if (-not $hasWritten) {
Write-Information "...Nothing copied"
}
}
Since the incoming object is of type Fileinfo we can find out if the file exists with this statement: if (-not $sourceZip.Exists) . If the sourcezip exists then we progress on through our function. Else we throw an exception.
Since we’ve imported the dot net classes for filecompression we now have an available type we can cast our $archive variable to [ZipArchive]. Since ZipArchive requires a stream we can open the zip file with the ZipFile class and stream it to the ZipArchive object.
Now that we have the entire contents for the archive in a variable $archive we can use apply our class to the variable. Below is what the value of my $archive looks like.
Since we are creating a new object of type [deployFile[]] Powershell will see this and instantiate a new object from our Class. In the example above we are taking each archive entry and creating a new [ArchiveFile]. If we follow the code through this loop we’ll find the first data element that’s length is greater than 0 will be defined as a [Archivefile].
As you can see from the declaration for this class [ArchiveFile] inherits the [DeployFile] class. PowerShell will hit the constructor that matches what was passed to the class. We passed a [ZipArchiveEntry]
Since this is now defined a new object it inherits all the methods that are declared in the class for this object type. This object type has The following methods defined:
GetModifiedDate, Copy, GetName
It then inherits from the [DeployFile] from this inheritance it gets the following methods:
If we continue to loop through each item in our intial $archive variable we’ll notice that we end up with a new Variable of type [DeployFile]. This $files variable is now of that type if we pipe the variable to get member we’ll see that we have a class name of [ArchiveFile]. if we look at the members of the $files of the array we’ll see the [Archivefile] class and the methods that were inherited from the other class [DeployFiles].
This function will check to see if the file doesn’t exist with -not $file.exists. Then it checks to see what the modified date is. if the Modified date is greater than the files last writetime in UTC. Then we are going to return true. Which means that this file is newer and should be copied. Hence the function name should copy. If both those tests fail then we’ll return false because the file exists and its timestamp is less than the lastwritetimeutc.
Now we return back to the TryCopy. Provided the return results of the try copy is true we’ll next check to see if we need to create a directory through a call to the class [DeployFile]::CreateFolderifNeeded([fileinfo]). This function is part of the deployfile class and will create a folder if it isn’t present for the file in question.
Now that the folder is created. We can now call the copy function from the $file object.
This will copy the file to the destination filename based on the $file object.
Note:
I haven’t been able to get this script to run on it’s own without writing a wrapper script to then call this one. I’ve posted an article about this on Powershell.org.
This file contains 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