Do Azure workflows need additional authentication considerations? - csv

I'm unable to deploy availability sets in parallel from a simple table from Powershell ISE onto my MSDN subscription.
Table
Type RG Name Loc
AvSet NLG NLGUTCDCPWFEAVL01 eastus2
AvSet NLG NLGUTCDCPAPPAVL01 eastus2
AvSet NLG NLGUTCDCPCCDBAVL01 eastus2
This works when executed without a workflow.
$c=Import-Csv C:\Users\ayanm\Downloads\NLG.csv|? type -eq 'AVSet'
foreach ($b in $c)
{New-AzureRmAvailabilitySet -ResourceGroupName $b.RG -Name $b.name -Location $b.loc}
But when I try to put it in a workflow, it doesn't
Workflow Deploy-AVSet
{$c=Import-Csv C:\Users\ayanm\Downloads\NLG.csv|? type -eq 'AVSet'
foreach -Parallel ($b in $c)
{New-AzureRmAvailabilitySet -ResourceGroupName $b.RG -Name $b.name -Location $b.loc}
}
Error:
Microsoft.PowerShell.Utility\Write-Error : Run Login-AzureRmAccount to login.
At Deploy-AVSet:4 char:4
+ CategoryInfo : NotSpecified: (:) [Write-Error], RemoteException
+ FullyQualifiedErrorId : System.Management.Automation.RemoteException,Microsoft.PowerShell.Commands.WriteErrorCommand
Checked Powershell version; 5.1. Updated all modules. Rebooted computer. Is this an unsupported workflow activity?
https://blogs.technet.microsoft.com/heyscriptingguy/2013/01/02/powershell-workflows-restrictions/

The `Login-AzureRmAccount' cmdlet doesn't MSDN crediantial object. So I added an O365 account as an owner of the subscription and am able to deploy in parallel.
Workflow Deploy-AVSet
{$c=Import-Csv C:\Users\ayanm\Downloads\NLG.csv|? type -eq 'AVSet'
$cred= New-Object System.Management.Automation.PSCredential "name#domain.onmicrosoft.com",$(ConvertTo-SecureString "Password" -asplaintext -force)
foreach ($b in $c)
{AzureRM.Resources\Login-AzureRmAccount -Credential $cred
New-AzureRmAvailabilitySet -ResourceGroupName $b.RG -Name $b.name -Location $b.loc -PlatformFaultDomainCount $b.faultdomain -PlatformUpdateDomainCount $b.UpdateDomain
}
}

Related

Setup Azure Function from Powershell

In Azure CLI, there is az functionapp, but no such equivalent can be found in Powershell AzureRM-library nor Az-library.
Using raw Azure resources, I've attempted something like this to create a function app on my Application Service Plan:
New-AzResource -ResourceType 'Microsoft.Web/Sites' `
-ResourceGroupName "MyRgName" `
-Location "westeurope" `
-ResourceName "MyFunctionName" `
-kind 'functionapp' `
-Properties #{ServerFarmId="abc-123"; alwaysOn=$True;} `
-ApiVersion '2018-11-01' `
-Force;
It almost works, but doesn't create a 100% working Function App. Azure Portal will spit lots of errors and warnings, for example from missing Host Keys.
Alternatives:
ARM-templates. What to put into a template to successfully create Azure Function? I have no idea. The one generated by Azure Portal is useless.
Azure Portal: Not really handy approach for environment setup from Azure DevOps release pipeline, but it will create a fully working Function App.
The question is: How to create a Function App from a Powershell script?
I am doing the exact same thing to create a dev sandbox environment.
Provisioning function apps is a gap in the Az Powershell module but it does appear to be possible.
I provisioned my function app by following the steps here https://clouddeveloper.space/2017/10/26/deploy-azure-function-using-powershell/ but changed it to use an existing app service plan instead of consumption plan.
$AppServicePlan = "abc-123"
$AppInsightsKey = "your key here"
$ResourceGroup = "MyRgName"
$Location = "westeurope"
$FunctionAppName = "MyFunctionName"
$AzFunctionAppStorageAccountName = "MyFunctionAppStorageAccountName"
$FunctionAppSettings = #{
ServerFarmId="/subscriptions/<GUID>/resourceGroups/$ResourceGroup/providers/Microsoft.Web/serverfarms/$AppServicePlan";
alwaysOn=$True;
}
# Provision the function app service
New-AzResource -ResourceGroupName $ResourceGroup -Location $Location -ResourceName $FunctionAppName -ResourceType "microsoft.web/sites" -Kind "functionapp" -Properties $FunctionAppSettings -Force | Out-Null
$AzFunctionAppStorageAccountKey = Get-AzStorageAccountKey -ResourceGroupName $ResourceGroup -AccountName $AzFunctionAppStorageAccountName | Where-Object { $_.KeyName -eq "Key1" } | Select-Object Value
$AzFunctionAppStorageAccountConnectionString = "DefaultEndpointsProtocol=https;AccountName=$AzFunctionAppStorageAccountName;AccountKey=$($AzFunctionAppStorageAccountKey.Value)"
$AzFunctionAppSettings = #{
APPINSIGHTS_INSTRUMENTATIONKEY = $AppInsightsKey;
AzureWebJobsDashboard = $AzFunctionAppStorageAccountConnectionString;
AzureWebJobsStorage = $AzFunctionAppStorageAccountConnectionString;
FUNCTIONS_EXTENSION_VERSION = "~2";
FUNCTIONS_WORKER_RUNTIME = "dotnet";
}
# Set the correct application settings on the function app
Set-AzWebApp -Name $FunctionAppName -ResourceGroupName $ResourceGroup -AppSettings $AzFunctionAppSettings | Out-Null
This might help:
To create an Azure Function we have dependency over "Storage Account", "Service Plan", "a resource group" and "Application Insight"(optional). Below i am initially defining variables. Post that i am checking if Resource group exist. If not it will create a new one. Post which i created Azure Storage Account, Service Plan and Application Insight. In Azure function we need to select Runtime Stack which can be "Java"/"DotNet"/"Python" etc. Here I am using "Dotnet". Azure Function requires Storage Account keys to link the same, which is extracted below under variables "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING" etc. To link AppInsight with Function we need to map Application_Insight_InstrumentationKey. Please follow inline comments :
#=============Defining All Variables=========
$location = 'Southeast Asia'
$resourceGroupName = 'functionrgnew1'
$storageAccount = 'functionsasdnewqq1'
$subscriptionId = '<id>'
$functionAppName = 'functionapppsdfsdnew1'
$appInsightsName = 'appinsightnameprdad'
$appServicePlanName = 'functionappplan'
$tier = 'Premium'
#========Creating Azure Resource Group========
$resourceGroup = Get-AzResourceGroup | Where-Object { $_.ResourceGroupName -eq $resourceGroupName }
if ($resourceGroup -eq $null)
{
New-AzResourceGroup -Name $resourceGroupName -Location $location -force
}
#selecting default azure subscription by name
Select-AzSubscription -SubscriptionID $subscriptionId
Set-AzContext $subscriptionId
#========Creating Azure Storage Account========
if(!(Test-AzureName -Storage $storageAccount))
{
New-AzStorageAccount -ResourceGroupName $resourceGroupName -AccountName $storageAccount -Location $location -SkuName "Standard_LRS"
}
#========Creating App Service Plan============
New-AzAppServicePlan -ResourceGroupName $resourceGroupName -Name $appServicePlanName -Location $location -Tier $tier
$functionAppSettings = #{
ServerFarmId="/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Web/serverfarms/$appServicePlanName";
alwaysOn=$True;
}
#========Creating Azure Function========
$functionAppResource = Get-AzResource | Where-Object { $_.ResourceName -eq $functionAppName -And $_.ResourceType -eq "Microsoft.Web/Sites" }
if ($functionAppResource -eq $null)
{
New-AzResource -ResourceType 'Microsoft.Web/Sites' -ResourceName $functionAppName -kind 'functionapp' -Location $location -ResourceGroupName $resourceGroupName -Properties $functionAppSettings -force
}
#========Creating AppInsight Resource========
New-AzApplicationInsights -ResourceGroupName $resourceGroupName -Name $appInsightsName -Location $location
$resource = Get-AzResource -Name $appInsightsName -ResourceType "Microsoft.Insights/components"
$details = Get-AzResource -ResourceId $resource.ResourceId
$appInsightsKey = $details.Properties.InstrumentationKey
#========Retrieving Keys========
$keys = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -AccountName $storageAccount
$accountKey = $keys | Where-Object { $_.KeyName -eq 'Key1' } | Select-Object -ExpandProperty Value
$storageAccountConnectionString = 'DefaultEndpointsProtocol=https;AccountName='+$storageAccount+';AccountKey='+$accountKey
#========Defining Azure Function Settings========
$AppSettings =#{}
$AppSettings =#{'APPINSIGHTS_INSTRUMENTATIONKEY' = $appInsightsKey;
'AzureWebJobsDashboard' = $storageAccountConnectionString;
'AzureWebJobsStorage' = $storageAccountConnectionString;
'FUNCTIONS_EXTENSION_VERSION' = '~2';
'FUNCTIONS_WORKER_RUNTIME' = 'dotnet';
'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' = $storageAccountConnectionString;
'WEBSITE_CONTENTSHARE' = $storageAccount;}
Set-AzWebApp -Name $functionAppName -ResourceGroupName $resourceGroupName -AppSettings $AppSettings
The best way to do this from PowerShell is to use an ARM template, rather than try to create each resource individually. You can find an example template here. It also hooks up the app to github, but you can leave out that part if you just want an empty app

Add firewall rule to Azure Database for MySQL server from powershell

I have deployed Azure Database for MySQL server and I want to add a firewall rule to it. I need to do it from PowerShell because this step is part of the greater solution. I tried the following code:
$resource = Get-AzureRmResource -ResourceGroupName $ResourceGroup.Variables.ResourceGroup `
-ResourceType "Microsoft.DBforMySQL/servers" -ResourceName $MySQLServer.ResourceName
$props = $resource.Properties
$props | Add-Member #{ipV4FirewallSettings = [ordered] #{ "firewallRules" = #() } }
$props.ipV4FirewallSettings.firewallRules = $MySQLServer.FirewallRules
$props | Add-Member #{administratorLoginPassword = "Qwerty123!" }
Set-AzureRmResource -PropertyObject $props -ResourceGroupName $ResourceGroup.Variables.ResourceGroup `
-ResourceType "Microsoft.DBforMySQL/servers" -ResourceName $MySQLServer.ResourceName -Force
Where $MySQLServer.FirewallRules are from json file in the following format:
"FirewallRules" : [
{ "firewallRuleName" : "test", "rangeStart": "0.0.0.0", "rangeEnd": "0.0.0.0" },
{ "firewallRuleName" : "test2", "rangeStart": "0.0.0.1", "rangeEnd": "255.255.255.255" }
],
This code does not throw any error, but it's not adding rules to the resource.
I need a pointer to where I made a mistake or some documentation, how to handle such task properly.
You can use New-AzureRmResource command to add Mysql firewall rules:
PS C:\Users\jason> $b = New-Object Psobject -Property #{startIpAddress="172.0.0.1" ; endIpAddress="172.0.0.8"}
PS C:\Users\jason> $b
startIpAddress endIpAddress
-------------- ------------
172.0.0.1 172.0.0.8
PS C:\Users\jason> New-AzureRmResource -ResourceId "/subscriptions/b83c1ed3-xxxx-xxxx-xxxx-2b83a074c23f/resourceGroups/jasonmysql/providers/Microsoft.DBforMySQL/servers/jasonmysql/firewallRules/rule2" -Properties $b -ApiVer
sion 2017-04-30-preview -Force
Name : rule2
ResourceId : /subscriptions/b83c1ed3-xxxx-xxxx-xxxx-2b83a074c23f/resourceGroups/jasonmysql/providers/Microsoft.DBforMySQL/servers/jasonmysql/firewallRules/rule2
ResourceName : jasonmysql/rule2
ResourceType : Microsoft.DBforMySQL/servers/firewallRules
ResourceGroupName : jasonmysql
SubscriptionId : b83c1ed3-xxxx-xxxx-xxxx-2b83a074c23f
Properties : #{startIpAddress=172.0.0.1; endIpAddress=172.0.0.8}

Importing Bulk user CSV file

Been trying all day to import my CSV file to my AD on Windows Server 2012.
But keep getting all kind of errors.
The error I get is this:
"New-ADUser : Cannot validate argument on parameter 'Name'. The argument is null
or empty. Provide an argument that is n ot null or empty, and then try the
command again.
At line:3 char:90
+ ... ipalname -Name $_.name -DisplayName $_.name -GivenName $_.cn -SurName $_.sn -Dep ...
+ ~~~~~~~
+ CategoryInfo : InvalidData: (:) [New-ADUser], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.NewADUsery
Can't seem to find any solution to this, so some help would be great. Here is my files.
This is my script:
Import-Csv .\nb1.csv | ForEach-Object {
$userprincipalname = $_.SamAccountName + "#hirethistire.local"
New-ADUser -SamAccountName $_.SamAccountName -UserPrincipalName $userprincipalname -Name $_.name -DisplayName $_.name -GivenName $_.cn -SurName $_.sn -Department $_.Department -Path "CN_Users,DC=HireThisTire,DC=local"-AccountPassword (ConvertTo-SecureString "Microsoft~1;" -AsPlainText -force) -Enabled $true -PasswordNeverExpires $true -PassThru
}
Sample Of the CSV File:

merging two Jsons in powershell

I have the following two Objects , which have been got from 2 Json file using:
$Env = ConvertFrom-Json "$(get-content "C:\chef\environments.json")"
$Roles = ConvertFrom-Json "$(get-content "C:\chef\roles.json")"
Heres the out put after conversion :
PS C:\chef> $Env
run_list
--------
{recipe[djin_chef-max_any::default]}
PS C:\chef> $Roles
7-zip : #{home=%SYSTEMDRIVE%\7-zip}
cookbook_versions :
default : #{env=development}
modmon : #{env=dev}
paypal : #{artifact=%5BINTEGRATION%5D}
seven_zip : #{url=https://djcm-zip-local/djcm/chef}
task_sched : #{credentials=XN$q}
windows : #{password=K1N5}
I need to merge these two Json objects in powershell and I tried the following:
PS C:\chef> $Roles+$Env
Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'.
At line:1 char:1
+ $Roles+$Env
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Is there another elegant way of doing it if I am doing it wrong or why am I getting this error ?
$Env only has one property, so you could add a new member to $Roles:
$Roles | Add-Member -NotepropertyName run_list -NotePropertyValue $Env.run_list
This syntax work in PowerShell v3, but you listed v2 and v2 in your tags.. so for v2:
$Roles | Add-Member -MemberType NoteProperty -Name run_list -Value $Env.run_list

ConvertTo-Json on VMWare objects doesn't work

In powershell while converting VM objects to json ,
($json = ConvertTo-Json $vm -Compress)
i am getting "An item with the same key has already been added" exception.
PS SQLSERVER:\> C:\Users\admin\Desktop\inventory.ps1
ConvertTo-Json : An item with the same key has already been added.
At C:\Users\huradmin\Desktop\inventory.ps1:68 char:31
+ if($vm -ne $null){$json = ConvertTo-Json $vm -Compress; insertToElasticSearc ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [ConvertTo-Json], ArgumentException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertToJsonCommand
insertToElasticSearch : Cannot bind argument to parameter 'json' because it is null.
At C:\Users\admin\Desktop\inventory.ps1:68 char:89
+ ... icSearch -json $json -info:$true -Verbose:$true}
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [insertToElasticSearch], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,insertToElasticSearch
getVMHosts function returns a list of VM guests. Please find my code below.
function getVMHosts{
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$vcenter,
[Parameter(Mandatory=$False)]
[switch]$info=$false
)
try
{
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Importing VMWare modules" -verbose:$info
Get-Module -ListAvailable -Name "VMware.*" | Import-Module
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Connecting to Vcenter:$vcenter" -verbose:$info
[void]::$(Connect-VIServer -Server $vcenter -ErrorAction SilentlyContinue)
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting Data center servers" -verbose:$info
$DCs = Get-Datacenter
$VMs = $null
foreach($dc in $DCs)
{
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting VM servers for Data Center:$dc" -verbose:$info
$VMs=$VMs+ $(Get-Datacenter -Name $dc.Name | Get-VM -Verbose:$info| Select PowerState,Name, NumCpu,MemoryMB,GuestId,VMHost, #{N="IP Address";E={#($_.guest.IPAddress[0])}})
}
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Disconnecting from VCenter:$vcenter" -verbose:$info
Disconnect-VIServer -Server $vcenter -ErrorAction SilentlyContinue -Confirm:$false
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Returning VM Lists" -verbose:$info
return $VMs
}
catch
{
$errorMessage = "$($_.Exception.Message)`n$(($_|select -ExpandProperty invocationinfo).PositionMessage)"
Write-Warning -Message "Catched an exception in Function:$($MyInvocation.MyCommand)`n$errorMessage" -Verbose:$true
}
}
$vmHosts = getVMHosts -vcenter "vcenter"
$counter = 0
foreach($vm in $vmHosts)
{
if($vm -ne $null){$json = ConvertTo-Json $vm -Compress;insertToElasticSearch json $json -info:$true -Verbose:$true}
}
Try ConvertTo-JSON -Depth 1. Sounds like there are properties in the object that have the same name.
I don't have VCenter to verify the script, but I refactored yours a bit to make it more powershell-ly.
Notes:
CmdletBinding gives you -Verbose and other features
Any object not set to a variable is output to the pipeline by default
Return does not do what most developers would expect
function getVMHosts{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$vcenter,
)
try
{
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Importing VMWare modules"
Get-Module -ListAvailable -Name "VMware.*" | Import-Module
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Connecting to Vcenter:$vcenter"
[void]$(Connect-VIServer -Server $vcenter -ErrorAction SilentlyContinue)
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting Data center servers"
Get-Datacenter |
ForEach-Object {
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Getting VM servers for Data Center:$_"
Get-Datacenter -Name $_.Name |
Get-VM -Verbose:$Verbose|
Select PowerState, Name, NumCpu, MemoryMB, GuestId, VMHost, #{N="IP Address";E={#($_.guest.IPAddress[0])}}
}
Write-Verbose "$(get-date -Format "dd/MM/yyyy HH:mm") - Function:$($MyInvocation.MyCommand) - Disconnecting from VCenter:$vcenter"
[void]Disconnect-VIServer -Server $vcenter -ErrorAction SilentlyContinue -Confirm:$false
}
catch
{
$errorMessage = "$($_.Exception.Message)`n$(($_|select -ExpandProperty invocationinfo).PositionMessage)"
Write-Warning -Message "Exception caught in Function:$($MyInvocation.MyCommand)`n$errorMessage"
}
}
getVMHosts -vcenter "vcenter" |
ForEach-Object {
$json = ConvertTo-Json $_ -Compress;
insertToElasticSearch json $json -info:$true -Verbose:$true
}
}
As noam states there are objects in there causing this. Extract the base case as an example
get-vm <insertexamplevmname> | Select PowerState, Name, NumCpu, MemoryMB, GuestId, VMHost, #{N="IP Address";E={#($_.guest.IPAddress[0])}} | convertto-json -Depth 1
You will see that VMHost isn't just the name of the host it is running on but the actual host object which also has a Name property just like the VM has a Name.
So what you probably want is to extract the VMHost name as you have done for the IP addresses from the guest object.
get-vm <insertexamplevmname> | Select PowerState, Name, NumCpu, MemoryMB, GuestId, #{N="Hostname";E={#($_.VMhost.Name)}}, #{N="IP Address";E={#($_.guest.IPAddress[0])}} | convertto-json
After some fiddling, it appears to be a bug with convertto-json when the get-vm statement returns a single vm object. If more than one vm object is returned, convertto-json works. You can test yourself, replacing vm1 and vm2 with valid vm names:
get-vm -name 'vm1' | convertto-json -depth 1## fail
get-vm -name #('vm1') | convertto-json -depth 1 ## fail
get-vm -name 'vm2' | convertto-json -depth 1 ## fail
get-vm -name #('vm2') | convertto-json -depth 1 ## fail
get-vm -name #('vm1','vm2') | convertto-json -depth 1 ## success
get-vm -name #('vm2','vm1') | convertto-json -depth 1 ## success
One hackaround would be to ensure get-vm always returns two vms by including a known vm, then ignoring the known vm json element. Not recommending this solution, but may help someone in a bind.