Assign the result of a query to a variable in powershell in Azure-Runbooks - azure

I have a runbook using automation in Azure. It is getting a single integer result from a table and it can return the correct value. The code I am using is below and it works.
$SQLServerCred = Get-AutomationPSCredential -Name "SqlCredential"
#Import the SQL Server Name from the Automation variable.
$SQL_Server_Name = Get-AutomationVariable -Name "SqlServer"
#Import the SQL DB from the Automation variable.
$SQL_DB_Name = Get-AutomationVariable -Name "Database"
$Query = "select max(je.ExecutionOrder) as LastStepExecuted
from PPoint.JobExecutionHistory je
where je.EventType = 'Start'
and je.JobRunId = PPoint.fnGetJobRunID()"
invoke-sqlcmd -ServerInstance "$SQL_Server_Name" -Database "$SQL_DB_Name" -Credential $SQLServerCred -Query "$Query" -Encrypt
The next step for me is to assign the result from the query to a variable and then evaluate it to see if it should call another runbook. So I want to have a variable named LastStep and assign it the integer result of LastStepExecuted from the query below. I then want to do something along this line (this is pseudocode)
if LastStep = 2147483647
call another runbook
else
do nothing - end the runbook
I have tried several ways to capture the LastStepExecuted in a variable but I can't figure it out. Can anyone help?
Any help or advice much appreciated.

You can use the Start-AzAutomationRunbook cmdlet to trigger a child runbook from the another run book inside the automation account.
$SQLServerCred = Get-AutomationPSCredential -Name "SqlCredential"
#Import the SQL Server Name from the Automation variable.
$SQL_Server_Name = Get-AutomationVariable -Name "SqlServer"
#Import the SQL DB from the Automation variable.
$SQL_DB_Name = Get-AutomationVariable -Name "Database"
$Query = "select max(je.ExecutionOrder) as LastStepExecuted
from PPoint.JobExecutionHistory je
where je.EventType = 'Start'
and je.JobRunId = PPoint.fnGetJobRunID()"
$LastStep=invoke-sqlcmd -ServerInstance "$SQL_Server_Name" -Database "$SQL_DB_Name" -Credential $SQLServerCred -Query "$Query" -Encrypt
if($LastStep -eq 2147483647)
{
Start-AzAutomationRunbook -AutomationAccountName "MyAutomationAccount" -Name "Test-ChildRunbook" -ResourceGroupName "LabRG" -DefaultProfile $AzureContext -Parameters $params -Wait
}
else
{
Write-Output "conditionfailed the value of $LastStep"
}
You can refer this documentation, about modular runbooks in azure automation.

Related

Query ObjectId of ConditionalAccessLocationCondition

I am writing a script to write to Azure, I basically want to find a user, create a network location, create a conditional access policy. This is what I have so far. The trouble is that the $secmon_guid and $location_policy_guid do not work. If I manually put the values in, it works.
# Run these commands first to connect and install without the #
Install-Module -Name AzureAD -AllowClobber -Force # Answer Y to install NuGet. Run once on workstation running script.
Install-Module -Name Microsoft.Graph.Identity.SignIns -Force # Install this to allow us to setup a trusted location. Run once on workstation running script.
Install-Module MSOnline -Force #Allow us to edit users. Run once on workstation running script.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine #Set execution policy to allow our script to do things.
Import-Module -Name AzureAD #The following 3 commands are ran for each client.
Connect-AzureAD # Use GA credentials from Glue
Connect-MsolService #Reauthenticate if necessary.
Get-AzureADMSConditionalAccessPolicy #This will list out all of the existing CA policies. This is a good opportunity to get them into documentation.
Connect-MgGraph #This enabled graph, you will need to approve the request in the popup window.
#Set variable for account name
Set-Variable -name "account" -Value "secmon"
#Create named location for the IP address
$ipRanges = New-Object -TypeName Microsoft.Open.MSGraph.Model.IpRange
$ipRanges.cidrAddress = "IP ADDR"
New-AzureADMSNamedLocationPolicy -OdataType "#microsoft.graph.ipNamedLocation" -DisplayName "Blackpoint IP Address for SecMon" -IsTrusted $true -IpRanges $ipRanges
#Disable MFA for secmon
Get-MsolUser -SearchString "secmon" | Set-MsolUser -StrongAuthenticationRequirements #()
#Get the Azure AD GUID for use later
$secmon_guid = Get-MsolUser -SearchString "secmon" | Select ObjectID
#Name the policy
$name = "Allow Secmon Only from Blackpoint IP"
#Enable the policy. Set to Disabled to test.
$state = "Enabled"
#Get location GUID and save to variable
$location_policy_guid = Get-AzureADMSNamedLocationPolicy | Where-Object -Property DisplayName -Contains 'Blackpoint IP Address for SecMon' | Select-Object -Property Id
#Working on this
#Create the overarching condition set for CA, this is the container.
$conditions = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessConditionSet
#Include all applications - This might be able to be removed?
$conditions.Applications = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessApplicationCondition
$conditions.Applications.IncludeApplications = 'All'
#Create the user condition and include secmon
$conditions.Users = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessUserCondition
$conditions.Users.IncludeUsers = $secmon_guid
#Add new location policy to CA policy
$conditions.Locations = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessLocationCondition
$conditions.Locations.IncludeLocations = $location_policy_guid
#Grant access control to CA policy
$controls = New-Object -TypeName Microsoft.Open.MSGraph.Model.ConditionalAccessGrantControls
$controls._Operator = "OR"
$controls.BuiltInControls = "block"
#End work
New-AzureADMSConditionalAccessPolicy `
-DisplayName $name `
-State $state `
-Conditions $conditions `
-GrantControls $controls
The error I get is due to poorly formatted GUID's, the values I am pulling are not correct. How can I fix this? Any help is greatly appreciated!
New-AzureADMSConditionalAccessPolicy : Error occurred while executing NewAzureADMSConditionalAccessPolicy
Code: BadRequest
Message: 1054: Invalid location value: #{Id=1234GUID}.
InnerError:
RequestId: 5678GUID
Where you define the variables, you need to use -ExpandProperty on the select-object statement e.g:
$secmon_guid = Get-MsolUser -SearchString "secmon" | Select -ExpandProperty ObjectID
Otherwise, you would have to access your current variable like so:
$conditions.Users.IncludeUsers = $secmon_guid.ObjectID

Parse Excel variables into powershell

I am trying to create a powershell that will grab the varibles from an excel sheet and then add them to the powersehll command.
in the excel sheet i have 3 columns i am interested in the data from (Name , resourcegroup, location)
And then for each line with this i want it to parse into into the varible field for the powershell
I have created the powershell to do what i need but it would be better if it could loop through and pull this as I am just running the command again with different machine info manually added from the excel.
With #Theo Help
I am working with this version of the script now
Import-Csv -Path 'c:\scripts\vmtest.csv' | ForEach-Object {
# combine the VMName with suffix '-Snapshot'
$snapshotName = $vm.name + "-Snapshot"
$SnapshotStorage = "Azure-Snapshots"
$vm = Get-AzVM -ResourceGroupName $_.ResourceGroup -Name $_.Name
# using splatting for better readability
$configParams = #{
SourceUri = $vm.StorageProfile.OsDisk.ManagedDisk.Id
Location = $_.location
CreateOption = 'copy'
}
$snapshot = New-AzSnapshotConfig #configParams
New-AzSnapshot -Snapshot $snapshot -SnapshotName $snapshotname -ResourceGroupName $snapshotstorage
}
If as you have commented, you now have the data stored in a CSV file that might look something like this:
Name,ResourceGroup,Location
PRD-ITM001,SJAVIRTUALMACHINES,uksouth
TST-GRSSQL001,SJAVIRTUALMACHINES,uksouth
it has become very simple to import that data and loop through the records like below:
Import-Csv -Path 'c:\scripts\vmtest.csv' | ForEach-Object {
# combine the VMName with suffix '-Snapshot'
$snapshotName = '{0}-Snapshot' -f $_.Name
$SnapshotStorage = "Azure-Snapshots"
$vm = Get-AzVM -ResourceGroupName $_.ResourceGroup -Name $_.Name
# using splatting for better readability
$configParams = #{
SourceUri = $vm.StorageProfile.OsDisk.ManagedDisk.Id
Location = $_.Location
CreateOption = 'copy'
}
$snapshot = New-AzSnapshotConfig #configParams
New-AzSnapshot -Snapshot $snapshot -SnapshotName $snapshotName -ResourceGroupName $_.ResourceGroup
}
Note that the above code assumes your CSV uses the (default) comma as delimiter character. If in your case this is some other character, append parameter -Delimiter followed by the character the csv uses.
Inside a ForEach-Object {..} loop, the $_ automatic variable references the current record from the csv
I used Splatting for better readability of the code. This helps on cmdlets that take a long list of parameters and eliminates the use of the backtick.
Based on the above shared requirement, we understood that you want to pull the values of ResourceGroupName, VMName from the excel sheet & also you want to use those values in the script further.
Using PSExcel Module, We have written the below PowerShell Script which will pull the ResourceGroupName, VMName from excel & it will run Get-AzVM Cmdlet.
Before running the below PowerShell script , run the below cmdlet Save-Azcontext cmdlet it will saves the current authentication information for use in other PowerShell sessions.
Connect-AzAccount
Save-AzContext -Path C:\test.json
Here is the PowerShell script:
$currentDir = "C:\Program Files\WindowsPowerShell\Modules" ##pass the path of the PSexcel Module
Import-Module $currentDir"\PSExcel"
Import-AzContext -Path C:\test.json ##passing the azcontext file path which was saved earlier
$ExcelFile = "Give here the path of the current folder where scripts are stored"
$objExcel = New-Excel -Path $ExcelFile
$WorkBook = $objExcel|Get-Workbook
ForEach($Worksheet in #($Workbook.Worksheets)){
$totalNoOfRecords = $Worksheet.Dimension.Rows
$totalNoOfItems = $totalNoOfRecords-1
# Declare the starting positions first row and column names
$rowNo,$colResourceGroupName = 1,1
$rowNo,$colVMName = 1,2
if ($totalNoOfRecords -gt 1){
#Loop to get values from excel file
for($i=1;$i -le ($totalNoOfRecords-1);$i++){
$ResourceGroupName=$Worksheet.Cells.Item($rowNo+$i,$colResourceGroupName).Value
$VMName=$Worksheet.Cells.Item($rowNo+$i,$colVMName).Value
Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName |select -Property Name,ResourceGroupName,Location
}
}
}
Here is the sample output for reference:
For more information ,you refer this blog post on How to Read excel file using PSExcel Module in PowerShell.

Azure Automations - Can we pass parameters from a powershell main runbook to a python child

I'm facing an issue for which i have not found the solution.
I trying to execute a python runbook from my parent which is in powershell.
The list of commands i tried in my powershell runbook :
Start-AzureRmAutomationRunbook -ResourceGroupName "" -AutomationAccountName "" -Name "" -Parameters $params
Start-AutomationRunbook -Name "" -Parameters $params
Start-AzAutomationRunbook -ResourceGroupName "" -AutomationAccountName "" -Name "" - Parameters $params
Here is my variable params :
$params = #{"args"="Hello"}
If anyone has the solution I'll be grateful ! I tried all day without success.
Thank you in advance for your help
The -Parameters option doesn't work for Python runbooks . You can use the -Parameters option of Start-AutomationRunbook from inside another runbook.
The workaround is to use the internal Automation module Start-AutomationRunbook instead of the external Start-AzureRmAutomation module.
Create a new runbook named Start-PythonRunbook as follow :
Param(
[parameter(Mandatory=$true)] [string]$runbook,
[string]$args
)
Start-AutomationRunbook -Name $runbook -Parameters $args
Now you can start this runbook using the Start-AzureRmAutomationRunbook cmdlet :
Start-AzureRmAutomationRunbook -ResourceGroupName <RGName>
-AutomationAccountName <AAName>
-Name Start-PythonRunbook `
-Parameters #{ "runbook" = "<pythonrunbookname>"; "args" = "arg1 arg2 arg3 arg4 arg5 arg6" }
You can check this GitHub discussion for more information.

Write-SqlTableData to import CSV into a Azure SQL Server table

The below command creates a new table, test for me but it doesn't insert any data into it.
Write-SqlTableData -TableName test -ServerInstance myservername -DatabaseName mydb -SchemaName dbo -Credential $mycreds -InputData $data -Force
This is the error message I get:
Write-SqlTableData : Cannot access destination table '[Mydb].[dbo].
[test]'.
At line:1 char:1
+ Write-SqlTableData -TableName test -ServerInstance myinstance
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: ([dbo].[test]:Table) [Write-SqlTableData], InvalidOperationException
+ FullyQualifiedErrorId : WriteToTableFailure,Microsoft.SqlServer.Management.PowerShell.WriteSqlTableData
Any ideas are appreciated.
UPDATE
This is the code to populate data.
$data = import-csv 'C:\Users\azure-user\myfile.csv'
This is what the file looks like -
"State","ProviderNbr","Entity","Address","City","Zip","Phone","Ownership","StarRating"
"AL","017000","ALABAMA DEPARTMENT OF HEALTH HOME CARE","201 MONROE STREET, SUITE 1200", "ALABAMA", "32423", "3233233233", "Alabama", "4"
This is a weird one - as you say, in Azure Read-SqlTableData works but Write-SqlTableData doesn't. From the discussion on MSDN here I think its something to do with the Azure environment making it hard for the cmdlet to interpret the 'ServerInstance' parameter.
Example 5 in Microsoft's Write-SqlTableData documentation "Write data to an existing table of an Azure SQL Database" shows the way forwards - we need to instantiate an SMO reference to the Table and feed that to the cmdlet instead. Unfortunately the example Microsoft gives contains a small error (you can't do $table = $db.Tables["MyTable1"] to get the table, it doesn't work)
Here's a modified version of that example:
$csvPath = "C:\Temp\mycsv.csv"
$csvDelimiter = ","
# Set your connection string to Azure SQL DB.
$connString = 'Data Source=server;Persist Security Info=True'
$cred = Get-Credential -Message "Enter your SQL Auth credentials"
$cred.Password.MakeReadOnly()
$sqlcred = New-Object -TypeName System.Data.SqlClient.SqlCredential -ArgumentList $cred.UserName,$cred.Password
# Create a SqlConnection and finally get access to the SMO Server object.
$sqlcc = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $connString,$sqlcred
$sc = New-Object -TypeName Microsoft.SqlServer.Management.Common.ServerConnection -ArgumentList $sqlcc
$srv = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server -ArgumentList $sc
# Get access to table 'MyTable1' on database 'MyDB'.
# Note: both objects are assumed to exists already.
$db = $srv.Databases["MyDB"]
$tableSmo = $db.Tables | Where-Object {$_.Name -eq "MyTable1"}
# leading comma makes an array with one item
# this makes PowerShell pass the entire contents of the file directly to the Write-SqlTableData cmdlet, which in turn can do a bulk-insert
, (Import-Csv -Path $pcsvPath -Delimiter $csvDelimiter) | Write-SqlTableData -InputObject $tableSmo
$sc.Disconnect()
If you're doing integrated security you can miss off all the $cred and $sqlcred stuff and just create the SqlConnection using $sqlcc = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $connString
Note: This worked for me with "A SQL Server running on a VM in Azure", in that I was having the same error as you, 'cannot access destination table' and this approach fixed it. I haven't tested it with an "Azure SQL Database" i.e. SQL Server as a service. But from the microsoft documentation it sounds like this should work.
According to my test, we can not use the command Write-SqlTableData to import CSV file to Azure SQL and we just can use it to import CSV file to on-premise SQL
So if you want to import CSV file to Azure SQL with powershell, you can use the command Invoke-SQLCmdto insert record on by one. For example:
$Database = ''
$Server = '.database.windows.net'
$UserName = ''
$Password = ''
$CSVFileName = ''
$text = "CREATE TABLE [dbo].[Colors2](
[id] [int] NULL,
[value] [nvarchar](30) NULL
) "
Invoke-SQLCmd -ServerInstance $Server -Database $Database -Username $UserName -Password $Password -Query $text
$CSVImport = Import-CSV -Path $CSVFileName
ForEach ($CSVLine in $CSVImport){
$Id =[int] $CSVLine.Id
$Vaule=$CSVLine.value
$SQLInsert = "INSERT INTO [dbo].[Colors2] (id, value)
VALUES('$Id', '$Vaule');"
Invoke-SQLCmd -ServerInstance $Server -Database $Database -Username $UserName -Password $Password -Query $SQLInsert
}
Invoke-SQLCmd -ServerInstance $Server -Database $Database -Username $UserName -Password $Password -Query "select * from [dbo].[Colors2]"
Besides, you also can use other ways ( such as BULK INSERT ) to implement it. For further information, please refer to https://learn.microsoft.com/en-us/sql/relational-databases/import-export/import-data-from-excel-to-sql?view=azuresqldb-current.

How do I translate a set of powershell commands into a script that I can run when I want?

I have a set of commands I can use in azure powershell. The commands create a resource group, app service, etc. I want to bundle them up so that I can just type one command into a terminal and run all of the deployment in one go.
# Ask user for work item id
$workItemId = Read-Host -Prompt "Enter the Work Item ID"
# Set Variables
$appdirectory="C:\Users\Charles\Desktop\Timesheet App\Discover\Client\build"
$webappname="discoverTest$workItemId"
$location="West Europe"
# Create a resource group.
New-AzResourceGroup -Name discoverTest$workItemId -Location $location
# Create an App Service plan in `Free` tier.
New-AzAppServicePlan -Name $webappname -Location $location `
-ResourceGroupName discoverTest$workItemId -Tier Free
# Create a web app.
New-AzWebApp -Name $webappname -Location $location -AppServicePlan $webappname `
-ResourceGroupName discoverTest$workItemId
# Get publishing profile for the web app
$xml = [xml](Get-AzWebAppPublishingProfile -Name $webappname `
-ResourceGroupName discoverTest$workItemId `
-OutputFile null)
# Extract connection information from publishing profile
$username = $xml.SelectNodes("//publishProfile[#publishMethod=`"FTP`"]/#userName").value
$password = $xml.SelectNodes("//publishProfile[#publishMethod=`"FTP`"]/#userPWD").value
$url = $xml.SelectNodes("//publishProfile[#publishMethod=`"FTP`"]/#publishUrl").value
# Upload files recursively
Set-Location $appdirectory
$webclient = New-Object -TypeName System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($username,$password)
$files = Get-ChildItem -Path $appdirectory -Recurse #Removed IsContainer condition
foreach ($file in $files)
{
$relativepath = (Resolve-Path -Path $file.FullName -Relative).Replace(".\", "").Replace('\', '/')
$uri = New-Object System.Uri("$url/$relativepath")
if($file.PSIsContainer)
{
$uri.AbsolutePath + "is Directory"
$ftprequest = [System.Net.FtpWebRequest]::Create($uri);
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::MakeDirectory
$ftprequest.UseBinary = $true
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($username,$password)
$response = $ftprequest.GetResponse();
$response.StatusDescription
continue
}
"Uploading to " + $uri.AbsoluteUri + " from "+ $file.FullName
$webclient.UploadFile($uri, $file.FullName)
}
$webclient.Dispose()
$workItemId = Read-Host -Prompt "Enter the Work Item ID"
Remove-AzResourceGroup -Name "discoverTest$workItemId" -Force
# print variable
Write-Host $variable
I want to be able to run a single command and have the full deployment process executed.
There are two ways to realize your needs, as below.
Extract all parameters you used in these PowerShell command lines as the arguments for a PowerShell Script <your-script-name>.ps1 which includes all same commands as yours. Please refer to the existing SO thread How to handle command-line arguments in PowerShell to know how to do. Then, you just need to run <your-script-name>.ps1 with these arguments in a terminal which had pre-installed Azure PowerShell Module.
Follow the blog Four ways to package a non-GUI PowerShell script as an executable file to make an executable file with the current set of commands.
Normally, I think the first way is better and be recommended.

Resources