Outputting a file with an Azure Function - powershell

I'm trying to experiment with Azure Functions. Basically my use case is calling the function with a GUID as GET Parameter, having the function download the WIX toolkit DLL and an MSI file, updating a parameter in the MSI file, and the returning that file to the caller of the function (as download prompt for example).
I'm mostly there, just need some help getting the download prompt/send to happen, my code so far:
$urlWix = "http://domain/wix.dll"
$outputWix = "$Env:TEMP\wix.dll"
Invoke-WebRequest -Uri $urlWix -OutFile $outputWix
try{Add-Type -Path $outputWix}catch{$Null}
$urlMSI = "http://domain/file.msi"
$outputFile = "$Env:TEMP\file.msi"
Invoke-WebRequest -Uri $urlMSI -OutFile $outputFile
$oDatabase = New-Object Microsoft.Deployment.WindowsInstaller.Database($outputFile,[Microsoft.Deployment.WindowsInstaller.DatabaseOpenMode]::Direct);
$sSQLQuery = "SELECT * FROM Property WHERE Property= 'MYPROPERTY'"
[Microsoft.Deployment.WindowsInstaller.View]$oView = $oDatabase.OpenView($sSQLQuery)
$oView.Execute()
$oRecord = $oView.Fetch()
$oRecord.SetString("Value","MyCustomValue")
$oView.Modify([Microsoft.Deployment.WindowsInstaller.ViewModifyMode]::Update,$oRecord)
$oView.Close();
$oDatabase.Dispose();
$file = get-item $outputFile
write-output $file

Unfortunately due to content type issues this is not possible in powershell. You can do this via a C#, F#, or Node (isRaw) function. The problem is that you need to specify headers via the JSON response format, which would convert any non-text data into a base64 string.
If you want to sent a text file via powershell it is possible:
$response = ConvertTo-JSON #{
Body="your file data";
Headers=#{
# unfortunately it seems functions does not support 'filename=...'
'Content-Disposition'='attachment';
# you would use application/octet-stream, but because it's converted to JSON you lose binary content
'Content-Type'='text/plain';
};
}
Out-File -Encoding Ascii -FilePath $res -inputObject $response

Related

Read Invoke-WebRequest line by line

I'm trying to keep a central list of log file locations where my log file cleanup script can grab the most up to date list.
$logpaths = (Invoke-WebRequest -UseBasicParsing -Uri 'http://10.7.58.99/logpaths.txt').Content
foreach($logpath in $logpaths)
{
"line"
$logpath
}
My script was sort of working but I was seeing some strange behavior so when I broke it down I found that within the foreach loop it just loops once and dumps the entire contents.
If I download the file the a text file on the local machine I can then use [System.IO.File]::ReadLines and it steps through perfectly. However, I don't want to download the file each time I run it or store it on the local server at all for that matter. How can I step through the content of Invoke-WebRequest line by line?
Based on this example from the .NET docs, you could read a response stream line-by-line like this, which should have better performance.
$url = 'http://10.7.58.99/logpaths.txt'
& {
$myHttpWebRequest = [System.Net.WebRequest]::Create($url)
$myHttpWebResponse = $myHttpWebRequest.GetResponse()
$receiveStream = $myHttpWebResponse.GetResponseStream()
$encode = [System.Text.Encoding]::GetEncoding("utf-8")
$readStream = [System.IO.StreamReader]::new($receiveStream, $encode)
while (-not $readStream.EndOfStream) {
$readStream.ReadLine()
}
$myHttpWebResponse.Close()
$readStream.Close()
} | foreach {
$logPath = $_
}
You might want to turn this into a nice little function. Let me know if you need help.

I am trying to download files using PowerShell where target file name is not known?

I'm using following script to download files using powershell.
$folder = "c:\temp\"
$userAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101
Firefox/7.0.1"
$web = New-Object System.Net.WebClient
$web.Headers.Add("user-agent", $userAgent)
Get-Content "c:\temp\URL_List.txt" |
Foreach-Object {
"Downloading " + $_
try {
$target = join-path $folder ([io.path]::getfilename($_))
$web.DownloadFile($_, $target)
} catch {
$_.Exception.Message
}
}
The URL_List.txt file has list of URL's I'm trying to download files from. Here's a sample URL from the list: https://drive.google.com/uc?export=download&id=0B84LPHCa2YmdZmFMV0dsYl9FeTg
If you look at the URL there's no absolute file name in the URL so I'm not sure how to set the target parameter for WebClient.DownloadFile() method.
Read the Download Files. Also might be worth checking since you're using Powershell, Google Drive REST Api module for Powershell.
So the question as I understand it is how to extract the filename for a Google Drive file without downloading the file first
This Chilkat page gave me the idea that it should be possible to access properties with a GET request. Chilkat is a paid API, so I thought I'd try to cobble together a method using direct PowerShell commands.
Invoke-WebRequest works but downloads the whole file. We only need the headers.
This site has the core code for that. From there, it's just parsing the Content-Disposition header to extract the "filename" (and not "filename*"):
$WebPath = "https://drive.google.com/uc?export=download&id=0B84LPHCa2YmdZmFMV0dsYl9FeTg"
$request = [System.Net.WebRequest]::Create( $WebPath )
$headers = $request.GetResponse().Headers
# Content-disposition includes a name-value pair for filename:
$cd = $headers.GetValues("Content-Disposition")
$cd_array = $cd.split(";")
foreach ($item in $cd_array) {
if ($item.StartsWith("filename=")) {
# Get string after equal sign
$filename = $item.Substring($item.IndexOf("=")+1)
# Remove quotation marks, if any
$filename = $filename.Replace('"','')
}
}

Update SSRS Datasource Reference in Powershell before Upload

I hope somebody can help me with this issue, i am trying to upload an SSRS report using powershell, however it totally loses the datasource reference after its uploaded.
I found some script online which changes the reference, but this would not work for what i need because its using the datasource name to become the reference, but in my scenario the datasource name could be something like Datasource1 but the reference could be /Data Sources/Translations
What i need to do is alter the reference in the bytearray before its uploaded.
This is the script im using so far, which is working for the upload.
#ReportName
$reportName = [System.IO.Path]::GetFileNameWithoutExtension($rdlFile);
write-host $reportName -ForegroundColor Green
#Upload File
try
{
#Get Report content in bytes
Write-Host "Getting file content of : $rdlFile"
$byteArray = gc $rdlFile.FullName -encoding Byte
$msg = "Total length: {0}" -f $byteArray.Length
Write-Host $msg
Write-Host "Uploading to: $reportFolder_Final"
$type = $ssrsProxy.GetType().Namespace
$datatype = ($type + '.Property')
$DescProp = New-Object($datatype)
$DescProp.Name = 'Description'
$DescProp.Value = ''
$HiddenProp = New-Object($datatype)
$HiddenProp.Name = 'Hidden'
$HiddenProp.Value = 'false'
$Properties = #($DescProp, $HiddenProp)
#Call Proxy to upload report
$warnings = $null
$Results = $ssrsProxy.CreateCatalogItem("Report",$reportName,$reportFolder_Final, $IsOverwriteReport,$byteArray,$Properties,[ref]$warnings)
If i try reading the XML in as a string and then converting it back into a bytearray before uploading it to the ssrs server the upload fails as it complains about the formatting. I had plans of reading it in as a string, altering the datasource reference, encoding it and then uploading but thats the part i need your help with doing.
Cheers
i have managed to suss this out, for anyone having similar issues i used the following code to read the data in from the XML file as a system.byte, converted it into a UTF8 string, made my changes to the references and then converted it back to a UTF8 Bytestream before uploading to the report server WDSL
$byteArray = gc $rdlFile.FullName -encoding Byte
$byteArray = [System.Text.Encoding]::UTF8.GetString($byteArray)
$byteArray = $byteArray -replace "<DataSourceReference>", "<DataSourceReference>/Data Sources/"
$byteArray = $byteArray -replace "<SharedDataSetReference>", "<SharedDataSetReference>/Datasets/"
$byteArray = [System.Text.Encoding]::UTF8.GetBytes($byteArray)

Best practice to use string data from a file

What is the best practice to create a text-based database for a PowerShell script?
What I really mean exactly?
I have a PowerShell script which use an url. This address may be used in other PS scripts, but I would be pretty happy if a quite simple practice is exists to solve to store this URL (or more) in a file, and the scripts use form this content, what I only have to define, which variable, line should be used, like this:
SharePointUrl = "..."
CcUrl = "..."
And in the script:
$SPUrl = DataFile.SharePointUrl ...
Something like this.
Not sure if I understand your question correctly. Are you looking for something like this?
$SharePointUrl = 'http://www.example.org/...'
New-Object -Type PSObject -Property #{'URL'=$SharePointUrl} |
Export-Csv 'C:\path\to\some.csv' -NoType
$DataFile = Import-Csv 'C:\path\to\some.csv'
$SPUrl = $DataFile.URL
Edit: After re-reading your question it seems you have an input file with key=value pairs. That can be processed like this:
$DataFile = Get-Content 'C:\path\to\data.txt' -Raw | ConvertFrom-StringData
$SPUrl = $DataFile.SharePointUrl
Another option is to write the configuration to a second PowerShell script:
# as variables
$SharePointUrl = "..."
$CcUrl = "..."
# or as a hashtable
$DataFile = #{
SharePointUrl = "..."
CcUrl = "..."
}
and dot-source that script in your original script.
. 'C:\path\to\config.ps1'

How to capture xsl:message output using Saxon XSLT transformer in PowerShell

I cannot find a way to capture the <xsl:message/> output of the .NET Saxon XSLT transformer when running via PowerShell.
I've tried various PowerShell methods of capturing output and various ways to tell Saxon to output the data without success. I need to use the Saxon XSLT library because our company is committed to it.
Tried
Transformer.MessageListener. Problem: PowerShell doesn't support creating an interface to capture MessageListener.Message events
Transformer.TraceFunctionDestination. Problem: Can't instantiate StandardLogger with a constructor.
$outLogger = New-Object Saxon.Api.StandardLogger -ArgumentList $outLogger
$outLogger.UnderylingTextWriter = $outLogFile
$saxTransform.TraceFunctionDestination = $outLogger
Various PowerShell output redirection methods. I don't think Saxon plays nice with that.
$out = $saxTransform.Run($outSerializer) *> "c:\eric\log.txt"
Write-Output "nada-> $out"
XSLT:
<xsl:template match="/">
<xsl:message>I want to see this in a file</xsl:message>
</xsl:template>
PowerShell:
# Instantiate an Xslt transformer
$saxonPath = GetSaxonDllPath
Add-Type -Path $saxonPath
$saxProcessor = New-Object Saxon.Api.Processor
$saxCompiler = $saxProcessor.NewXsltCompiler()
$baseXsltPath = [System.IO.Path]::GetDirectoryName($inXsltPath)
$uri = [System.Uri]$baseXsltPath
$saxCompiler.BaseUri = $uri
Write-Output "Compiling..."
$uri = [System.Uri]$inXsltPath
$saxExecutable = $saxCompiler.Compile($uri)
$saxTransform = $saxExecutable.Load()
#Prepare the input XML file
$basePath = [System.IO.Path]::GetDirectoryName($inXmlFilePath)
$uri = [System.Uri]$basePath
$inFileStream = [System.IO.File]::OpenRead($inXmlFilePath)
$saxTransform.SetInputStream($inFileStream, $uri);
##Set XSLT processing parameters
if ($arguments) {
foreach($argument in $arguments.GetEnumerator()) {
$paramName = New-Object Saxon.Api.QName($argument.Name)
$paramValue = New-Object Saxon.Api.XdmAtomicValue($argument.Value)
$saxTransform.SetParameter($paramName, $paramValue)
}
}
#Prepare the output file
$outFileStream = [System.IO.File]::OpenWrite($outFileName)
$outSerializer = New-Object Saxon.Api.Serializer
$outSerializer.SetOutputStream($outFileStream);
$outLogFile = [System.IO.File]::OpenWrite("c:\eric\log.txt")
$outLogger = New-Object Saxon.Api.StandardLogger($outLogFile)
#Transform
Write-Output "Transforming..."
$saxTransform.Run($outSerializer)
Write-Output "Transformation done."
$outLogFile.Close()
$outLogFile.Dispose()
#Cleanup
$inFileStream.Close()
$inFileStream.Dispose()
$outFileStream.Close()
$outFileStream.Dispose()
We have some sympathy with your requirement. But the use of the XslTransformer.MessageListener is the only why of intercepting where the xsl:message output is sent. Setting the TraceFunctionDestination will not capture the xsl:message output.
I am not an expert in Powershell, but as a workaround I would think you can write an implementation of IMessageListener in a C# class and build this as an assembly for use within Powershell.