NSIS import PowerShell module with parameters - nsis

I have written a sample program that will write Hello World and created NSIS file and executed which works fine for me
My .nsi script goes as follows
!include "x64.nsh"
Name "nsExec Test"
OutFile "nsExecTest.exe"
#ShowInstDetails show
Section "Output to variable"
nsExec::ExecToStack 'powershell.exe "& "Import-Module C:\PowerShell\Hello.psm1"'
Pop $0
Pop $1
DetailPrint '"ImportModules" printed: $1'
SectionEnd
Which is printing the write host as per written when I executed it
My code in .psm1 file
# Filename: Hello.psm1
Write-Host
Write-Host 'Hello World!'
Write-Host "Good-bye World! `n"
# end of script
Now I am trying to achieve the same with parameters, can some one help me
# Filename: TestParameter.psm1
function TestParam([string] $TestParam)
{
Write-Host
Write-Host '$TestParam'
Write-Host "Good-bye $TestParam! `n"
}
# end of script

Instead of a module, how about using a plain script?
In C:\PowerShell\script1.ps1:
param([string]$TestParam)
Write-Host
Write-Host '$TestParam'
Write-Host "Good-bye $TestParam! `n"
And then in the nsi:
nsExec::ExecToStack 'powershell.exe "& "C:\PowerShell\script.ps1 -TestParam myString"'

Related

Azure DevOps : while using Release Pipeline in classic mode, I'm not able to pass the value of a variable to other tasks

I have to store the value of $pipeline.variables.res.value in new variable in release pipeline and then have to use that new variable in rest of the stages and tasks in release pipeline.
I'm working with classic pipelines.
I'm trying below code:
Set-Variable -Name abc -Value $pipeline.variables.res.value
Write-Host "New Value:" $abc
Write-Host "##vso[task.setvariable variable=xyz;isoutput=true]$abc"
Write-Host "New Value:" $abc
Output:
Not able to see any xyz variable in release pipeline. Also, on calling below commands in different tasks, I'm not getting any value as an output.
Input: Write-Host "New Value:" $xyz
Write-Host "New Value:" $abc
Output:2022-09-19T09:04:56.4561806Z New Value:
2022-09-19T09:04:56.4594657Z New Value:
Please help in this.
Your example is not structured correctly. This will not work because of all instructions in one task:
Set-Variable -Name abc -Value $pipeline.variables.res.value
Write-Host "New Value:" $abc
Write-Host "##vso[task.setvariable variable=xyz;isoutput=true]$abc"
Write-Host "New Value:" $xyz
You have to consume your variable in a next task like:
TASK 1
Set-Variable -Name abc -Value $pipeline.variables.res.value
Write-Host "New Value:" $abc
Write-Host "##vso[task.setvariable variable=xyz;isoutput=true]$abc"
TASK 2
Write-Host "New Value: $(xyz)"

Azure Powershell Output not displayed in the console

I have created a script in the Azure PowerShell.
If I use the "echo" command, it displays output to the console.
However, if I use Write-Output and Write-Error, I don't see the output.
I have uploaded the script "change-to-static.ps1" to a storage account. Then I open the "Cloud Shell" with a button at the top bar. Then I type "./change-ip-to-static.ps1" in the PowerShell console.
Therefore, the script does not produce any output unless I replace "Write-Output" and "Write-Error" with "echo" or "print".
Please help me. What should I do to see the output?
The script is below.
There is a similar question at How to output something in PowerShell. I have read it, but there are no concrete examples on how to achieve my goal, i.e. how to modify my script to see the output. And in my case, it does not output even if I redirect to a text file. However, commands like "echo" and "print" in my case work but they are not covered in the above example. See the script below.
$IPs = Get-AzPublicIpAddress;
$Static = "Static";
foreach ($PublicIP in $IPs) {
$Method = $PublicIP.PublicIpAllocationMethod;
$Name = $PublicIP.Name;
if ($Method -eq $Static) {
$message = "The method of " + $Name + " is already " + $Static;
Write-Progress -Activity $message;
}
else {
Write-Progress -Activity "Changing the method of "+$Name+" from "+$Method+" to "+$Static+"...";
$PublicIP.PublicIpAllocationMethod = $Static;
Set-AzPublicIpAddress -PublicIpAddress $PublicIP;
Write-Progress -Activity "Querying the method of "+$Name+"...";
$ModifiedAddress = Get-AzPublicIpAddress -Name $Name -ResourceGroupName $PublicIP.ResourceGroupName -Location $PublicIP.Location
$NewMethod = $ModifiedAddress.PublicIpAllocationMethod;
if ($NewMethod -eq $Static) {
Write-Output "The method for "+$Name+" has successfully changed to "+$Static;
}
else {
Write-Error -Message "Cannot change the method for "+$Name+" to "+$Static+", it is still "+$NewMethod+"!!!";
}
}
}
P.S. I have updated the script (use this URL) according to the suggestions, but there is still no output. Only "echo" or "print" gives the output.
P.P.S. The Write-Progress does not even show a temporary message in the status line during Set-AzPublicIpAddress which takes a couple of seconds to complete, or if I add the Start-Sleep cmdlet. It does only set during Get-AzPublicIpAddress.
After reading your last edit to my answer, I believe you made a bit of confusion in using Write-* commandlets, and in your script logic, so I provided a more detailed answer with context.
echo in the Powershell Azure Cloud Shell is an alias of Write-Output, as executing echo without parameters clearly shows (docs here ).
PS /home/mikelangelo> echo
cmdlet Write-Output at command pipeline position 1
Supply values for the following parameters:
InputObject:
Moreover: the unix echo can also be run in the Powershell Azure Cloud Shell.
PS /home/mikelangelo> which echo
/usr/bin/echo
PS /home/mikelangelo> /usr/bin/echo ciao mondo
ciao mondo
print, on the other hand, is not a Powershell alias, so the unix counterpart is the one which always get executed when using the print keyword (presently a symlink to run-mailcap - but it's not clear to me how it comes into play into your use case.)
PS /home/mikelangelo> which print
/usr/bin/print
So, basically, echo and Write-Output will both work, because they call the same commandlet, unless you execute /usr/bin/echo directly, mixing up technologies and effectively impairing portability.
Back to the question:
Write-Output works as expected. The logic is faulty: You use = as a comparison operator, but you need to use -eq instead.
Write-Progress needs to be used differently, replace it with Write-Host or Write-Output. Refer to the docs for an explanation.
Note that Write-Output sends an object down the pipeline, which can eventually be represented as a console output.
Write-Progress and Write-Host, on the other hand, do not generate output - the latter sends an object to the host for displaying, though, so Write-Host is the recommended way to display something in the console. Refer to this question for more details on Write-Host, Write-Output and the Powershell pipeline.
Like other commenters before me I can also confirm that the code from your gist works just fine in Azure Cloud Shell.
I noticed that there is only an output if you have at least one dynamic public ip that the script can change to static. The reason is, that only in this case you use Write-Output to return a string to the console.
If there is no dynamic public ip left, your script only writes a progress message, but you never get to see it, as the script execution ends too quickly after you write the message and progress messages don't linger.
Put the command Start-Sleep -Seconds 2 under the line with Write-Progress and you will see what I mean:
$IPs = Get-AzPublicIpAddress;
$Static = "Static";
foreach ($PublicIP in $IPs) {
$Method = $PublicIP.PublicIpAllocationMethod;
$Name = $PublicIP.Name;
if ($Method -eq $Static) {
$message = "The method of $Name is already $Static";
Write-Progress -Activity $message;
Start-Sleep -Seconds 2 # This will keep the script running 2 seconds longer and the message visible.
}
else {
Write-Progress -Activity "Changing the method of $Name from $Method to $Static ...";
$PublicIP.PublicIpAllocationMethod = $Static;
Set-AzPublicIpAddress -PublicIpAddress $PublicIP;
Write-Progress -Activity "Querying the method of $Name ...";
$ModifiedAddress = Get-AzPublicIpAddress -Name $Name -ResourceGroupName $PublicIP.ResourceGroupName
$NewMethod = $ModifiedAddress.PublicIpAllocationMethod;
if ($NewMethod -eq $Static) {
Write-Output "The method for $Name has successfully changed to $Static";
}
else {
Write-Error -Message "Cannot change the method for $Name to $Static, it is still $NewMethod!!!";
}
}
}
Write-Progress is probably not the cmdlet that you want to use to write out the progress of your script (despite it's name). As I do not see how you would need to further process the output of your script, you might as well replace it with Write-Host.

Azure adds leading space to variable

If I run this powershell code locally:
$url = "refs/pull/5625/merge"
$SourceBranchFromBuild = $url.split('/')[-1]
$featureReleaseUrl = "http://$sourceBranchFromBuild.azurewebsites.net"
Write-Output $featureReleaseUrl
The output is:
http://merge.azurewebsites.net
When I run this code on a Azure Powershell:
$url = "refs/pull/5625/merge"
$SourceBranchFromBuild = $url.split('/')[-1]
Write-Host "##vso[task.setvariable variable=prSourceBranchName;]"$SourceBranchFromBuild
And then create the URL in another Azure Powershell script:
$featureReleaseUrl = "http://$env:prSourceBranchName.azurewebsites.net"
Write-Output $featureReleaseUrl
The ouput is
http:// merge.azurewebsites.net
What's causing this leading space character in the $env:prSourceBranchName?
Azure isn't adding anything - your write-host is!
Your code is doing this:
PS> $x = "xxx"
PS> write-host "aaa"$x
aaa xxx
but presumably you want
PS> $x = "xxx"
PS> write-host "aaa$x"
aaaxxx
Note where the second quote is the write-host in both examples. In the first it's before the $x variable name. In the second it's after.
In your question it's calling this (with the quote before the variable name):
Write-Host "##vso[task.setvariable variable=prSourceBranchName;]"$SourceBranchFromBuild
which will write a logging command to the log file, and Azure DevOps will process that and update the environment variable.
You're probably expecting it to write this to the log file:
##vso[task.setvariable variable=prSourceBranchName;]merge
but it's actually writing this:
##vso[task.setvariable variable=prSourceBranchName;] merge
Try switching your code to this (i.e. second quote after the variable name):
Write-Host "##vso[task.setvariable variable=prSourceBranchName;]$SourceBranchFromBuild"
and it should omit the space in front of the branch name in your url.
#PeterBoomsma Try putting $SourceBranchFromBuild inside the double quotes like this:
Write-Host "##vso[task.setvariable variable=prSourceBranchName;]$SourceBranchFromBuild"

Powershell function to use excel to convert between file formats

I have a need to use scheduled tasks or chron jobs to convert files between xlsx and csv and xml and xlsx.
I didn't find anything I loved so I wrote a module to make my life easier.
Initial requirements had me knowing that I had access to static full file names when the job was scheduled. After writing a simple script I expanded it for features I thought might come in handy.
I saved this into a psm1 file and load it with my profile.
Features:
Pipeline safe(ish)
-flexible outputs
-extension option if you like the name but not the file type
-delete option in case you want to clean up after yourself
Comments up front create workable help in PowerShell, but are fairly readable too.
<#
.SYNOPSIS
This function will take files and use the excel application to convert them.
.DESCRIPTION
This function allows you to use the full power of Excel to open and save files. The infile can be any file that Excel can open. The outfile can be xlsx, xsl, xml, or csv. Also, there is an option to delete the destination file before runing the save operation to avoid prompts when overwriting, and to erase the origin file after the process has completed.
.EXAMPLE
Convert-Excel -infile 'Source.xml' -outfile 'destination.xlsx' -delete $true
Converts source.xml to xlsx file type.
Deletes source.xml when done.
Deletes destination.xlsx before it converts.
.EXAMPLE
Convert-Excel -infile 'Source.xlsx' -outfile 'destination.csv'
Converts xlsx to csv.
Leaves both files behind when done.
.EXAMPLE
Convert-Excel -infile 'Source.csv'
Converts infile Source.csv (or whatever format) to xlsx of the same name.
Leaves both behind when done.
.EXAMPLE
Convert-Excel -infile 'Source.xlsx' -Extension '.csv'
Converts xlsx to csv. By passing just the extension it will use the same base file name.
Leaves both files behind when done.
.EXAMPLE
Convert-Excel -infile 'C:\Users\notI\PRD-06661-12082017 - Copy.xml' -outfile 'C:\Users\notI\PRD-06661-12082017-Copy.csv'
Loads full path xml
Saves full path csv
.EXAMPLE
dir *.xml | Sort-Object -Property LastWriteTime | Convert-Excel -Extension ".csv"
Similar to above but uses the pipeline to do multiple conversions.
If full outfile name is given, it will create just one file over and over again. In this example it would go in chronological order creating csv files.
.EXAMPLE
Dir *.xml | Convert-Excel -extension ".csv" -delete $True | Convert-Excel -extension ".xml"
Thats just weird, but it might solve your problem, and it works.
.PARAMETER infile
Name of the origin file to use. If the full path is not given it will be opened from the context the script is running in.
.PARAMETER outfile (extension)
Name of the destination file to create. If the full path is not given it will save in the default destination of Excel.
.PARAMETER delete
If $true it will delete the target location file if it exists before conversion and the origin file after conversion. Functions like a move with clobber.
If anything else or blank it will leave origin in place and if destination exists it will prompt for overwriting.
#>
function Convert-Excel{
param(
[parameter(ValueFromPipeLineByPropertyName=$True,ValueFromPipeline=$True)]
[Alias('FullName')]
[string] $infile,
[Alias('Extension')]
[string] $outfile,
[bool] $delete
)
Begin {
$begin_outfile = $outfile
Try {$ExcelWB = new-object -comobject excel.application}
Catch {Write-Host "This host does not seem to have Excel installed"}
}
Process{
#Check infile
if (-not($infile)) {
Write-Output "You must supply a value for -infile"
break
}
else {
Try {$file = Get-Item $infile}
Catch {Write-Output "$infile does not seem to exist, or I can't get to it"; break}
}
#Check outfile
#Reset value for pipeline loop
$outfile = $begin_outfile
#If blank just presume xlsx
if (-not($outfile)) {
$outfile = $file.FullName -replace '\.[^.]+$',".xlsx"
Write-Verbose "No outfile supplied, setting outfile to $outfile"
}
#If startswith a dot, use as an extension.
If ($outfile.StartsWith(".")) {
$outfile = $file.FullName -replace '\.[^.]+$',$outfile
Write-Verbose "Extension supplied, setting outfile to $outfile"
}
#derive XlFileFormat from extension
if($outfile -cmatch '\.[^.]+$') {
$extens="" #Reset for pipeline loop
switch ($Matches[0])
{
".xlsx" {$extens = 51}
".csv" {$extens = 6}
".xml" {$extens = 46}
".xls" {$extens = -4143}
".xlsm" {$extens = 52}
default {$extens = 51}
}
}
else {
break #if it can't find an extension in regex
}
if ($file.FullName -eq $outfile) {
#Nobody needs us to create a copy of an existing file.
write-verbose "Goal already achieved, moving on"
}
Else {
if(Test-Path ($outfile)){
#Avoid prompting to overwrite by removing an existing file of the same name
Remove-Item -path ($outfile)
}
Try {
Write-Verbose "Loop Check $infile"
if ($file.Extension -eq ".xml") {
#Make assumptions for XML. If you need more control don't automate
$Workbook = $ExcelWB.Workbooks.OpenXML($file.FullName,1)
}
else {
#Act Normal
$Workbook = $ExcelWB.Workbooks.Open($file.FullName)
}
$Workbook.SaveAs($outfile,$extens)
$Workbook.Close($false)
}
Catch {
Write-Host "Unable to convert file $file because Excel cannot open or save it without help"
break
}
if ($delete) {#If asked to delete
if(Test-Path ($outfile)){ #And a file now exists where outfile said it should be
if(Test-Path ($infile)){ #And there is a file at infile
Remove-Item -path ($infile) #Delete it
}
}
}
#Mostly to keep from breaking the pipeline, but not bad as an output of a file creator
Return $outfile
}
}
End{
#Cleanup
$ExcelWB.quit()
}
}
export-modulemember -function Convert-Excel

Strange error with NewWebPage - SP 2010 Foundation

I'm having a strange issue here using the NewWebPage xml command in SP 2010 Foundation. The below code works does not work
cls
Write-Host "Production Manager v8.0 Deployment Utility" -ForegroundColor red
Write-Host ""
#Starting the script. Lets see if we can find the configuration and map files first.
Write-Host -NoNewline "Checking for the Configuration.xml, PageMap.xml and PageTemplate.xml files: " -ForegroundColor white
if((Test-Path "Configuration.xml") -and (Test-Path "PageMap.xml") -and (Test-Path "PageTemplate.xml"))
{
Write-Host "FOUND" -ForegroundColor green
}
else
{
Write-Host "NOT FOUND" -ForegroundColor red
Write-Host "Check for the necessary files and try again."
Write-Host ""
Write-Host ""
exit
}
Write-Host "Reading Configuration.xml"
[xml]$Configuration = Get-Content Configuration.xml
Write-Host "Reading PageMap.xml"
[xml]$PageMap = Get-Content PageMap.xml
Write-Host "Reading from Production Manager Site: "$Configuration.Configuration.SiteConfiguration.SiteURL
#Some variables necessary for the loop
$pageCreationLoopIterations = 0
$pageLayout = ""
$pageTitle = ""
$createPageCommand = '<?xml version="1.0" encoding="UTF-8"?><Method ID="0,NewWebPage"><SetList Scope="Request">' + $productionManagerLibrary.ID + '</SetList><SetVar Name="Cmd">NewWebPage</SetVar><SetVar Name="ID">New</SetVar><SetVar Name="Type">WebPartPage</SetVar><SetVar Name="WebPartPageTemplate">' + $pageLayout + '</SetVar><SetVar Name="Overwrite">true</SetVar><SetVar Name="Title">MyPage</SetVar></Method>';
#Beginning the loop
Write-Host "Running through the PageMap file"
foreach($Page in $PageMap.Pages.Page)
{
$web = Get-SPWeb $Configuration.Configuration.SiteConfiguration.SiteURL
$productionManagerLibrary = $web.Lists | Where { $_.Title -eq "Production Manager" }
$pageName = if($Page.SelectSingleNode("PageName")) { $Page.PageName } else { $Configuration.Configuration.PageConfiguration.DefaultPageName }
$pageLayout = if($Page.SelectSingleNode("PageLayout")) { $Page.PageLayout } else { $Configuration.Configuration.PageConfiguration.DefaultPageLayout }
Write-Host 'Creating Page ' $pageName
$web.ProcessBatchData($createPageCommand)
}
But this one works just fine every time I run it:
$url = "http://mpm8/mpm";
$listname = "Production Manager"
$web = Get-SPWeb $url
$pagesLibrary = $web.Lists | Where { $_.Title -eq "Production Manager" }
$pageLayout = 8
$cmd = '<?xml version="1.0" encoding="UTF-8"?><Method ID="0,NewWebPage"><SetList Scope="Request">' + $pagesLibrary.ID + '</SetList><SetVar Name="Cmd">NewWebPage</SetVar><SetVar Name="ID">New</SetVar><SetVar Name="Type">WebPartPage</SetVar><SetVar Name="WebPartPageTemplate">' + $pageLayout + '</SetVar><SetVar Name="Overwrite">true</SetVar><SetVar Name="Title">MyPage</SetVar></Method>';
$web.ProcessBatchData($cmd)
I really cannot see anything different between the two scripts. The error I get running the first one is:
<Result ID="0,NewWebPage" Code="-2130575350">
<ErrorText>Invalid URL Parameter.
The URL provided contains an invalid Command or Value. Please check the URL again. </ErrorText></Result>
Can you help me on this one? Maybe I cannot run this thing out a foreach loop? :(
Thanks!
You have set the $createPageCommand first (before the loop), then set the variables that it needs second (in the loop).
If you run this in the ISE and step through (ie debug it), you can see the variables and their values, otherwise simply emit the values of the variables to screen as you run through the loop to ensure it is setting them correctly.
So, in your example, place the $createPageCommand within the loop and after $productionManagerLibrary is set. Then, immediately before $web.ProcessBatchData($createPageCommand) you should emit the value of $createPageCommand to ensure it is OK.
I've only eye balled this without running it, but please us know if this is the reason!

Resources