I have Azure Data Factory CI/CD pipeline. My ADF have few global params, so I am following Microsoft documentation for their CI/CD. On same documentation page, there is below 'Update global param' powershell script. Issue is whenever this script runs, it resets my ADF network access to 'Public endpoint' from 'private endpoint'.
param
(
[parameter(Mandatory = $true)] [String] $globalParametersFilePath,
[parameter(Mandatory = $true)] [String] $resourceGroupName,
[parameter(Mandatory = $true)] [String] $dataFactoryName
)
Import-Module Az.DataFactory
$newGlobalParameters = New-Object 'system.collections.generic.dictionary[string,Microsoft.Azure.Management.DataFactory.Models.GlobalParameterSpecification]'
Write-Host "Getting global parameters JSON from: " $globalParametersFilePath
$globalParametersJson = Get-Content $globalParametersFilePath
Write-Host "Parsing JSON..."
$globalParametersObject = [Newtonsoft.Json.Linq.JObject]::Parse($globalParametersJson)
# $gp in $factoryFileObject.properties.globalParameters.GetEnumerator())
# may be used in case you use non-standard location for global parameters. It is not recommended.
foreach ($gp in $globalParametersObject.GetEnumerator()) {
Write-Host "Adding global parameter:" $gp.Key
$globalParameterValue = $gp.Value.ToObject([Microsoft.Azure.Management.DataFactory.Models.GlobalParameterSpecification])
$newGlobalParameters.Add($gp.Key, $globalParameterValue)
}
$dataFactory = Get-AzDataFactoryV2 -ResourceGroupName $resourceGroupName -Name $dataFactoryName
$dataFactory.GlobalParameters = $newGlobalParameters
Write-Host "Updating" $newGlobalParameters.Count "global parameters."
Set-AzDataFactoryV2 -InputObject $dataFactory -Force
I want Network access to be via 'Private endpoint' ALWAYS. Does anyone faced this issue?
Just change last line of your Global param script as follows:
Set-AzDataFactoryV2 -InputObject $dataFactory -PublicNetworkAccess "Disabled" -Force
Now your ADF network access won't reset to Public one.
We have a number of Azure function apps to deploy, using the same code. In our Azure DevOps pipeline we have an Azure PowerShell (4.*) script to deploy the code and start the function app:
Param (
[string] $resourceGroupName,
[string[]] $funcapps,
[string] $filePath
)
Write-Output "Deploying the following function apps: $funcapps";
foreach ($app in $funcapps)
{
Write-Output "Deploying function app: $app";
$webapp = Publish-AzWebapp -ResourceGroupName $resourceGroupname -Name $app -ArchivePath $filePath -Force;
Write-Output "Starting function app: $app";
$webapp = Start-AzWebApp -ResourceGroupName $resourceGroupName -Name $app;
Write-Output "Started function app: $app = $($webapp.State)";
}
This works fine (both from local PowerShell and from Azure DevOps), but with the number of apps we're deploying can take a while. To try to make it perform better, we tried to run the publish/start statements in parallel:
Param (
[string] $resourceGroupName,
[string[]] $funcapps,
[string] $filePath
)
Workflow Parallel-Deploy {
Param (
[string] $resourceGroupName,
[string[]] $funcapps,
[string] $filePath
)
Write-Output "Deploying the following function apps: $funcapps";
foreach -parallel($app in $funcapps)
{
Write-Output "Deploying function app: $app";
$webapp = Publish-AzWebapp -ResourceGroupName $resourceGroupname -Name $app -ArchivePath $filePath -Force;
Write-Output "Starting function app: $app";
$webapp = Start-AzWebApp -ResourceGroupName $resourceGroupName -Name $app;
Write-Output "Started function app: $app = $($webapp.State)";
}
}
Parallel-Deploy -resourceGroupName $resourceGroupName -funcapps $funcapps -filePath $filePath
The code is the same, just moved into a Workflow to use "foreach -parallel".
If I run the script from a local PowerShell, everything works fine - but from the Azure DevOps pipeline, I get an error, No account found in the context. Please login using Connect-AzAccount.
I've found reference to changes made in the Azure PowerShell DevOps task, that the context needs to be passed explicitly to background tasks. I tried to follow the example listed (Save-AzContext and Import-AzContext - updating from the Save-AzureRmContext/Import-AzureRmContext in the example), but it's still giving me the same error.
Any suggestions on what I'm doing wrong, and how to get the context correctly set inside the "foreach -parallel" block?
Edit 1
I probably should have shown exactly what I did for SaveContext/ImportContext...
Param (
[string] $resourceGroupName,
[string[]] $funcapps,
[string] $filePath,
[string] $tmpDir
)
$contextPath = "$tmpDir/context.json"
Save-AzContext -Path $contextPath" -Force
Workflow Parallel-Deploy {
Param (
[string] $resourceGroupName,
[string[]] $funcapps,
[string] $filePath,
[string] $contextPath
)
foreach -parallel($app in $funcapps)
{
# Output context - initially not set
Get-AzContext;
# Fetch and display context - now set
Import-AzContext -Path $contextPath;
Get-AzContext;
Write-Output "Deploying function app: $app";
$webapp = Publish-AzWebapp -ResourceGroupName $resourceGroupname -Name $app -ArchivePath $filePath -Force;
...
This still gave me the error that the account wasn't found in the context.
As per the suggestions, I changed to using a job instead:
foreach ($app in $funcapps) {
$jobname = "$app-Job";
Start-Job -Name $jobname -ScriptBlock {
Param (
[string] $resourceGroupName,
[string[]] $funcapps,
[string] $filePath,
[string] $contextPath
)
# Output context - initially not set
Get-AzContext;
# Fetch and display context - now set
Import-AzContext -Path $contextPath;
Get-AzContext;
Write-Output "Deploying function app: $app";
$webapp = Publish-AzWebapp -ResourceGroupName $resourceGroupname -Name $app -ArchivePath $filePath -Force;
...
And this too said the context wasn't correct.
save\import should work just fine, as well as just allowing the context autosave Enable-AzContextAutosave.
Alternatively you can just a native capability to launch cmdlets as jobs:
Publish-AzWebapp -ResourceGroupName $resourceGroupname -Name $app `
-ArchivePath $filePath -Force -AsJob
and then just wait for the jobs to finish and start the webapps.
I have created a pipeline which has two parameters at pipeline level.
I want to send the values to these parameters using powershell and trigger the pipeline.
Any idea how to do it using Powershell.
I'll leave a script that you can then modify to your needs:
Login-AzureRmAccount
Select-AzureRmSubscription -Subscription "yourSubId"
$dfname = "youDataFActoryName"
$rgName = "yourResourceGroupName"
$pipe = "pipeName"
$parameters = #{
"param1" = "asdasd"
"param2" = "123456"
}
Invoke-AzureRmDataFactoryV2Pipeline -DataFactoryName $dfname -ResourceGroupName $rgName -PipelineName $pipe -Parameter $parameters
Hope this helped!
Parameter is to be passed as a hashtable. The hashtable is created using #
e.g :
$param = #{"year" = "2022"}
Invoke-AzDataFactoryV2Pipeline -ResourceGroupName "UAT" -DataFactoryName "ADF" -PipelineName "Snapshot" -Parameter $param
I created VSTS Task Group with Azure Powershell Task Inline Script with Four Parameters. I have added this Task Group to Release Definition and configured parameters. When i try to release it failed with following error
2018-03-23T10:28:42.2811600Z ##[error]At
C:\Users\buildguest\AppData\Local\Temp\6e927620-8956-47d6-b926-00d9177a4c26.ps1:2
char:9
+ [String] Container-Service,
+ ~ Parameter declarations are a comma-separated list of variable names with optional initializer expressions.
At
C:\Users\buildguest\AppData\Local\Temp\6e927620-8956-47d6-b926-00d9177a4c26.ps1:2
char:9
+ [String] Container-Service,
+ ~ Missing ')' in function parameter list.
Here is Azure Powershell Script
Param(
[String] $(apiManagementRg),
[String] $(apiManagementName),
[String] $(swaggerUrl),
[String] $(basePath),
[String] $(apiId)
)
$ApiMgmtContext = New-AzureRmApiManagementContext -ResourceGroupName $(apiManagementRg) -ServiceName $(apiManagementName)
Import-AzureRmApiManagementApi -Context $ApiMgmtContext -SpecificationFormat "Swagger" -SpecificationUrl $(swaggerUrl) -Path $(basePath) -ApiId $(apiId)
Release Definition Screenshot
Release Definition
It's mainly caused by the PowerShell script syntax errors.
Based on your script, it seems $(apiManagementRg), $(apiManagementName), $(swaggerUrl), $(basePath) and $(apiId) are variables defined in your release definition.
To use the user defined variables from the release definition into Powershell script parameters, you should specify the user defined variables in Script Arguments to pass the values into your PowerShell parameters.
Detail steps as below:
Remove the task group you created.
Re-create task group with Azure PowerShell Task as below:
Inline Script:
Param(
[String] $Rg,
[String] $Name,
[String] $Url,
[String] $path,
[String] $apiId
)
$ApiMgmtContext = New-AzureRmApiManagementContext -ResourceGroupName $Rg -ServiceName $Name
Import-AzureRmApiManagementApi -Context $ApiMgmtContext -SpecificationFormat "Swagger" -SpecificationUrl $url -Path $path -ApiId $apiId
Script Arguments:
-Rg "$(apiManagementRg)" -Name "$(apiManagementName)" -Url "$(swaggerUrl)" -path "$(basePath)" -apiId "$(apiId)"
I am working with PowerShell 4.0 and I am trying to pass a string array as one of the parameters for an Invoke-Command -ScriptBlock in which I am calling another PowerShell script on a remote server. When I do this, the string array seems to get flattened so that it appears as a single string value, rather than a string array.
Listed below is the 1st script, which is being called by a Bamboo deployment server that provides the initial parameters.
In the Debug section, the $SupportFolders string array is iterated by the FlowerBoxArrayText function and it properly writes the two folder paths to the console, as expected.
24-Oct-2017 14:59:33 *****************************************************************************
24-Oct-2017 14:59:33 **** E:\SRSFiles\SRSOutput
24-Oct-2017 14:59:33 **** E:\SRSFiles\SRSBad
24-Oct-2017 14:59:33 *****************************************************************************
Here is the initial part of the 1st script file, showing the input parameters, the string array creation and where I am calling the remote script via Invoke-Command;
[CmdletBinding(DefaultParametersetName='None')]
param (
# Allows you to specify Install, Delete or Check.
[ValidateSet("Install", "Delete", "Check")][string] $Action = "Check",
# Allows you to specify the remote server name.
[string] $ComputerName = "None",
# Allows you to specify the username to use for installing the service.
[string] $Username = "None",
# Allows you to specify the password to use for installing the service.
[string] $Password = "None",
# Allows you to specify the location of the support folders for the service, if used.
[string] $SupportFoldersRoot = "None"
)
Function CreateCredential()
{
$Pass = $Password | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($Username, $Pass)
Return $Cred
}
Function FlowerBoxArrayText($TextArray, $TextColor="Yellow")
{
Write-Host "*****************************************************************************" -ForegroundColor $TextColor
foreach($TextLine in $TextArray)
{
IndentedText $TextLine $TextColor
}
Write-Host "*****************************************************************************" -ForegroundColor $TextColor
}
Function IndentedText($TextToInsert, $TextColor="Yellow")
{
Write-Host "**** $TextToInsert" -ForegroundColor $TextColor
}
$Credential = CreateCredential
[string[]] $ResultMessage = #()
[string] $Root = $SupportFoldersRoot.TrimEnd("/", "\")
[string[]] $SupportFolders = #("$Root\SRSOutput", "$Root\SRSBad")
#Debug
Write-Host "**** Starting debug in ManageAutoSignatureProcessorService ****"
FlowerBoxArrayText $SupportFolders -TextColor "Green"
Write-Host "**** Ending debug in ManageAutoSignatureProcessorService ****"
#End Debug
$ResultMessage = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock {
param($_action,$_username,$_password,$_supportFolders) &"C:\Services\ManageService.ps1" `
-Action $_action `
-ComputerName DEV `
-Name DevProcessor `
-DisplayName 'DevProcessor' `
-Description 'DevProcessor' `
-BinaryPathName C:\Services\DevProcessor.exe `
-StartupType Manual `
-Username $_username `
-Password $_password `
-ServicePathName C:\Services `
-SupportFolders $_supportFolders `
-NonInteractive } -ArgumentList $Action,$Username,$Password,(,$SupportFolders)
if ($ResultMessage -like '*[ERROR]*')
{
FlowerBoxArrayText $ResultMessage -textColor "Red"
}
else
{
FlowerBoxArrayText $ResultMessage -textColor "Green"
}
Then, in the ManageService.ps1 script file on the remote server, I have the following;
[CmdletBinding(DefaultParametersetName='None')]
param (
# Allows you to specify Install, Delete or Check.
[ValidateSet("Install", "Delete", "Check")][string] $Action = "Check",
# Allows you to specify the name of the remote computer.
[string] $ComputerName = "None",
# Allows you to specify the service name.
[string] $Name = "None",
# Allows you to specify the service display name.
[string] $DisplayName = "None",
# Allows you to specify the service description.
[string] $Description = "None",
# Allows you to specify the path to the binary service executable file.
[string] $BinaryPathName = "None",
# Allows you to specify how the service will start, either manual or automatic.
[ValidateSet("Manual", "Automatic")][string] $StartupType = "Manual",
# Allows you to specify the domain username that the service will run under.
[string] $Username = "None",
# Allows you to specify the password for the domain username that the service will run under.
[string] $Password = "None",
# Allows you to specify the path to the service install scripts and service files on the remote server.
[string] $ServicePathName = "None",
# Allows you to specify the location of the support folders for the service, if used. The default value is an empty array
[string[]] $SupportFolders = #(),
# Disables human interaction, and allows all tests to be run even if they 'fail'.
[switch] $NonInteractive
)
Function CreateCredential()
{
$Pass = $Password | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential($Username, $Pass)
Return $Cred
}
[bool] $OkToInstall = $False
[string[]] $ResultMessage = #()
#Debug
$ResultMessage = $ResultMessage += "[DEBUG] ***************************************"
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders: [$SupportFolders] ."
foreach ($Folder in $SupportFolders)
{
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders Item: $Folder."
}
$Count = #($SupportFolders).Count
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders Count: $Count ."
$ResultMessage = $ResultMessage += "[DEBUG] ***************************************"
#End Debug
The line,
$ResultMessage = $ResultMessage += "[DEBUG] SupportFolders: [$SupportFolders] ."
shows the following result from the $ResultMessage value that is returned to the calling script;
**** [DEBUG] SupportFolders: [E:\SRSFiles\SRSOutput E:\SRSFiles\SRSBad] .
Notice that the array is flattened out.
The foreach loop that follows also only prints out one value instead of two;
"E:\SRSFiles\SRSOutput E:\SRSFiles\SRSBad"
I have spent considerable time researching a solution but have yet to find an answer.
Any ideas?
EDIT 1 using #Bacon Bits suggestion;
$Options = #{'Action' = $Action
'ComputerName' = 'DEV'
'Name' = 'DevProcessor'
'DisplayName' = 'DevProcessor'
'Description' = 'Generate daily processes'
'BinaryPathName' = 'C:\Services\DevProcessor\DevProcessor.exe'
'StartupType' = 'Manual'
'Username' = $Username
'Password' = $Password
'ServicePathName' = 'C:\Services\DevProcessor'
'SupportFolders' = $SupportFolders
}
$ScriptBlock = {
param($Options)
& {
param(
$Action,
$ComputerName,
$Name,
$DisplayName,
$Description,
$BinaryPathName,
$StartupType,
$Username,
$Password,
$ServicePathName,
$SupportFolders,
$NonInteractive
)
&powershell "C:\Services\DevProcessor\ManageService.ps1 $Action $ComputerName $Name $DisplayName $Description $BinaryPathName $StartupType $Username $Password $ServicePathName $SupportFolders"
} #Options;
}
$ResultMessage = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock $ScriptBlock -ArgumentList $Options
If I run the code modified as it is listed above, I still get the flattened array for $SuppportFolders and the ManageService.ps1 script trips up over parameters that have spaces, even though they are quoted when I assign them.
The option to completely wrap the code in ManageService.ps1, as opposed to simply calling the remote script is not really viable because the ManagedService.ps1 script is fairly extensive and generic so I can call it from over 30 automation scripts in my deployment server.
I believe what #Bacon Bits is suggesting would work if it was feasible to wrap the ManageService script.
To pass a single array, you can do this:
Invoke-Command -Session $Session -ScriptBlock $ScriptBlock -ArgumentList (,$Array);
However, that only works if you only need to pass a single array. It can all fall apart as soon as you start to pass multiple arrays or multiple complex objects.
Sometimes, this will work:
Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList (, $Array1), (, $Array2), (, $Array3);
However, it can be inconsistent in my experience. Sometimes it flattens the arrays out again.
What you can do is something similar to this answer.
{param($Options)& <# Original script block (including {} braces)#> #options }
Basically what we do is:
Wrap the script in a scriptblock that accepts a single hashtable as an argument.
Put all our arguments into the hashtable.
Use the passed hashtable as a splat variable.
So it would be something like:
$Options = #{
Action = 'Check';
ComputerName = 'XYZ123456';
Name = 'MyName';
.
.
.
}
$ScriptBlock = {
param($Options)
& {
[CmdletBinding(DefaultParametersetName='None')]
param (
# Allows you to specify Install, Delete or Check.
[ValidateSet("Install", "Delete", "Check")][string] $Action = "Check",
# Allows you to specify the name of the remote computer.
[string] $ComputerName = "None",
# Allows you to specify the service name.
[string] $Name = "None",
.
.
.
.
#End Debug
} #Options;
}
Invoke-Command -ComputerName RemoteServer -ScriptBlock $ScriptBlock -ArgumentList $Options;
Here's a trivial working example:
$Options = #{
List1 = 'Ed', 'Frank';
List2 = 5;
List3 = 'Alice', 'Bob', 'Cathy', 'David'
}
$ScriptBlock = {
param($Options)
& {
param(
$List1,
$List2,
$List3
)
"List1"
$List1
''
"List2"
$List2
''
"List3"
$List3
} #Options;
}
Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $Options;
Output:
List1
Ed
Frank
List2
5
List3
Alice
Bob
Cathy
David
Note that I tested this on PowerShell v5. I no longer have a system with PowerShell v4 to test on.