Register-AzAutomationScheduledRunbook giving an error at Powers-shell script - powershell-4.0

I am new to power-shell and cmldt.
I registered a power-shell script(.ps1) name like "Invoke-xxDevopDt" as azure RunBooks that associated with an azure Automation Account.This RunBooks scheduled to run at another data for do some work. Below is my power-shell script(.ps1) which i used to register "Invoke-xxDevopDt" (RunBook) by using 'Register-AzAutomationScheduledRunbook' as below
param (
[Parameter(Mandatory = $false)]
[string]$resourceGroup='rsgrp_test',
[Parameter(Mandatory = $false)]
[string]$automationAccountName='Azureaucnt-test',
[Parameter(Mandatory = $false)]
[datetime]$startDateTime='01/05/2021 6:00:00 PM' ,
[Parameter(Mandatory = $false)]
[string]$vmResourceIds='/subscriptions/5454-7d8e-xxx-b628-yy3232/resourceGroups/TSTDEV/providers/Microsoft.Compute/virtualMachines/TSTDevVM',
[Parameter(Mandatory = $false)]
[String] $IncludedKBs='4588962',
[Parameter(Mandatory = $false)]
[datetime] $vmRestartDateTime='01/06/2021 4:00:00 PM',
[Parameter(Mandatory = $false)]
[String] $description = "Automated trigger from C# Program"
)
#do Login-AzAccount to login azure subcription
....
#setting data for
$AddMinutes = 7
# creating an scheduling and registering
$scheduleName = "testAPP_devopsSchedule_" + "$(Get-Date $vmRestartDateTime -UFormat %Y-%m-%d_%A_%H-%M)"
$devopsSchedule = New-AzAutomationSchedule -ResourceGroupName $resourceGroup `
-AutomationAccountName $automationAccountName `
-Name $scheduleName `
-StartTime ($vmRestartDateTime.AddMinutes($AddMinutes)) `
-Onetime -Verbose
#Createing a param for registering another PS-script as RunBook with automation-acnt
$params = #{"serverList"=$vmResourceIds;"testMessage"="test";"description"="Invoking Automation from Automation Account"}
#register 'Invoke-xxDevopDt' is like a ps-script at runBook of automation-acnt
Register-AzAutomationScheduledRunbook –AutomationAccountName $automationAccountName `
–Name 'Invoke-xxDevopDt' –ScheduleName $devopsSchedule.Name –Parameters $params `
-ResourceGroupName $resourceGroup
But, above ps-script gave an ERROR as "Register-AzAutomationScheduledRunbook : A positional parameter cannot be found that accepts argument 'Invoke-xxDevopDt'."
Pls help
Thanks,

Add an answer to close this issue.
At our side, we can successfully register it. And as per op's comment, the issue is due to white space, and now it can work.

Related

No parallelization despite the use of a runspace pool with powershell 5.1

We are working on a Powershell script that, among other things, performs a job import of multiple computers via a REST API. The normal job import also works flawlessly and gets an XML with all necessary information passed as parameter.
Now we want to parallelize this job import, so that several of these imports can take place at the same time to reduce the time of the import with a high number of computers.
For this purpose, we use a runspace pool and pass a worker - which contains the code for the job import - as well as all necessary parameters to the respective Powershell instance. Unfortunately, this doesn't seem to work, since even after measuring the import time, we couldn't see any speedup due to the parallelization of the job import. The measured time is always about the same as if we would perform the job import sequentially - i.e. without parallelization.
Here is the relevant code snippet:
function changeApplicationSequenceFromComputer {
param (
[Parameter(Mandatory=$True )]
[string]$tenant = $(throw "Parameter tenant is missing"),
[Parameter(Mandatory=$True)]
[string]$newSequenceName = $(throw "Parameter newSequenceName is missing")
)
# Other things before parallelization
# Passing all local functions and imported modules in runspace pool to call it from worker
$InitialSessionState = [initialsessionstate]::CreateDefault()
Get-ChildItem function:/ | Where-Object Source -like "" | ForEach-Object {
$functionDefinition = Get-Content "Function:\$($_.Name)"
$sessionStateFunction = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $_.Name, $functionDefinition
$InitialSessionState.Commands.Add($sessionStateFunction)
}
# Using a synchronized Hashtable to pass necessary global variables for logging purpose
$Configuration = [hashtable]::Synchronized(#{})
$Configuration.ScriptPath = $global:ScriptPath
$Configuration.LogPath = $global:LogPath
$Configuration.LogFileName = $global:LogFileName
$InitialSessionState.ImportPSModule(#("$global:ScriptPath\lib\MigrationFuncLib.psm1"))
# Worker for parallelized job-import in for-each loop below
$Worker = {
param($currentComputerObjectTenant, $currentComputerObjectDisplayName, $newSequenceName, $Credentials, $Configuration)
$global:ScriptPath = $Configuration.ScriptPath
$global:LogPath = $Configuration.LogPath
$global:LogFileName = $Configuration.LogFileName
try {
# Function handleComputerSoftwareSequencesXml creates the xml that has to be uploaded for each computer
# We already tried to create the xml outside of the worker and pass it as an argument, so that the worker just imports it. Same result.
$importXml = handleComputerSoftwareSequencesXml -tenant $currentComputerObjectTenant -computerName $currentComputerObjectDisplayName -newSequence $newSequenceName -Credentials $Credentials
$Result = job-import $importXml -Server localhost -Credentials $Credentials
# sleep 1 just for testing purpose
Log "Result from Worker: $Result"
} catch {
$Result = $_.Exception.Message
}
}
# Preparatory work for parallelization
$cred = $Credentials
$MaxRunspacesProcessors = ($env:NUMBER_OF_PROCESSORS) * $multiplier # we tried it with just the number of processors as well as with a multiplied version.
Log "Number of Processors: $MaxRunspacesProcessors"
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxRunspacesProcessors, $InitialSessionState, $Host)
$RunspacePool.Open()
$Jobs = New-Object System.Collections.ArrayList
foreach ($computer in $computerWithOldApplicationSequence) {
# Different things to do before parallelization, i.e. define some variables
# Parallelized job-import
Log "Creating or reusing runspace for computer '$currentComputerObjectDisplayName'"
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
Log "Before worker"
$PowerShell.AddScript($Worker).AddArgument($currentComputerObjectTenant).AddArgument($currentComputerObjectDisplayName).AddArgument($newSequenceName).AddArgument($cred).AddArgument($Configuration) | Out-Null
Log "After worker"
$JobObj = New-Object -TypeName PSObject -Property #{
Runspace = $PowerShell.BeginInvoke()
PowerShell = $PowerShell
}
$Jobs.Add($JobObj) | Out-Null
# For logging in Worker
$JobIndex = $Jobs.IndexOf($JobObj)
Log "$($Jobs[$JobIndex].PowerShell.EndInvoke($Jobs[$JobIndex].Runspace))"
}
<#
while ($Jobs.Runspace.IsCompleted -contains $false) {
Log "Still running..."
Start-Sleep 1
}
#>
# Closing/Disposing pool
} # End of the function
The rest of the script looks like this (simplified):
# Parameter passed when calling the script
param (
[Parameter(Mandatory=$True)]
[string]$newSequenceName = $(throw "Parameter target is missing"),
[Parameter(Mandatory=$True)]
[float]$multiplier= $(throw "Parameter multiplier is missing")
)
# 'main' block
$timeToRun = (Measure-Command{
changeApplicationSequenceFromComputer -tenant "testTenant" -newSequenceName $newSequenceName
}).TotalSeconds
Log "Total time to run with multiplier $($multiplier) is $timeToRun"
Any ideas why the job import is obviously only executed sequentially despite runspace pool and corresponding parallelization?
We have found the error. The foreach contained the following code block:
# For logging in Worker
$JobIndex = $Jobs.IndexOf($JobObj)
Log "$($Jobs[$JobIndex].PowerShell.EndInvoke($Jobs[$JobIndex].Runspace))"
This had to be created outside the foreach so that the code looks like this:
function changeApplicationSequenceFromComputer {
param (
[Parameter(Mandatory=$True )]
[string]$tenant = $(throw "Parameter tenant is missing"),
[Parameter(Mandatory=$True)]
[string]$newSequenceName = $(throw "Parameter newSequenceName is missing")
)
# ... Everything as before
$Jobs.Add($JobObj) | Out-Null
} #end of foreach
$Results = #()
foreach($Job in $Jobs ){
$Results += $Job.PowerShell.EndInvoke($Job.Runspace)
}
So the EndInvoke() has to be called outside the foreach.

Add switch button in Azure Runbook

Can we add a radio button or switch button which execute two different functions when selected either of them in Azure RunBook say below VMNAME param. similar to Run on settings (Azure/ Hybrid Worker).
I did add a bool parameters, but it didn't show up in UI
param (
[Parameter(Mandatory=$false)]
[string] $subscriptionID = "testsubscription",
[Parameter(Mandatory=$false)]
[string] $resourceGroupName = "RG-test",
[Parameter(Mandatory=$false)]
[string] $vmName = "testserver"
[Parameter (Mandatory = $false)]
[bool]$WhatIf
)
Can we add a radio button or switch button which execute two different functions when selected either of them in Azure RunBook.
I am not sure whether it is supported currently. If you have any idea, you coud give your feedback to Azure team.
but you could use bool type or switch paramter to control which function should be executed.
I did add a bool parameters, but it didn't show up in UI
bool parameters could be show up in the UI as dropdownlist. Please with following code.
param (
[Parameter(Mandatory=$false)]
[string] $subscriptionID = "testsubscription",
[Parameter(Mandatory=$false)]
[string] $resourceGroupName = "RG-test",
[Parameter(Mandatory=$false)]
[string] $vmName = "testserver", # miss "," in your code.
[Parameter (Mandatory = $false)]
[bool]$WhatIf
)

Permanent WMI Event consumer doesnt get triggered, temporary does

I am trying to create a permanent WMI Event subscription to run a script when a USB Drive gets connected.
Here is how I create the Filter, Consumer and the binding:
$computer = "xxx"
$filterNS = "root\cimv2"
$wmiNS = "root\subscription"
$query = "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'"
$filterName = "TestFilter"
$filterPath = Set-WmiInstance -Class __EventFilter `
-ComputerName $computer -Namespace $wmiNS -Arguments `
#{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL";
Query=$query}
$consumerPath = Set-WmiInstance -Class CommandLineEventConsumer `
-ComputerName $computer -Namespace $wmiNS `
-Arguments #{
name="TestConsumer";
ExecutablePath= "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
CommandLineTemplate = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -executionpolicy bypass -file D:\\reassignDriveletter.ps1"
}
Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `
-Namespace $wmiNS -arguments #{Filter=$filterPath; Consumer=$consumerPath} |
out-null
Everything gets created without errors, i can see the Filter, consumer and binding in WMI event viewer but if i attach a USB drive nothing happens (first line of the script writes a log entry, thats how i know).
For testing purposes i created a normal event subscription like this:
$job = Register-WmiEvent -Query "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'" -SourceIdentifier usb -Timeout 1000 -Action $scriptblock
which works absolutely fine.
Am I missing something obvious? I´d be grateful for any help.
Regards
Update: Just tested on a non-domain joined Win7 Computer and the same code works fine. (My workstation is Win8.1 domain-joined for the record). I will test on the target system and report back.
OK, after a couple of days of trial and error, I ended up adapting/using a different approach found here.
This will launch a script stored in D:\scripts everytime a USB is connected and it's permanent.
$filter = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance()
$filter.QueryLanguage = "WQL"
$filter.Query = "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'"
$filter.Name = "USBFilter"
$filter.EventNamespace = 'root\cimv2'
$result = $filter.Put()
$filterPath = $result.Path
$consumer = ([wmiclass]"\\.\root\subscription:CommandLineEventConsumer").CreateInstance()
$consumer.Name = 'USBConsumer'
$consumer.CommandLineTemplate = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe –ExecutionPolicy Bypass -file D:\scripts\reassignDriveletter.ps1"
$consumer.ExecutablePath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$consumer.WorkingDirectory = "D:\scripts"
$result = $consumer.Put()
$consumerPath = $result.Path
$bind = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance()
$bind.Filter = $filterPath
$bind.Consumer = $consumerPath
$result = $bind.Put()
$bindPath = $result.Path
To delete these Perma Events, do this:
([wmi]$filterPath).Delete()
([wmi]$consumerPath).Delete()
([wmi]$bindPath).Delete()
My test script created a folder every time a USB drive was plugged in, so I could test it and it worked.
I'm running Windows 8.1 btw.

Use ValidateSet with the contents loaded from a CSV file

I really like the way that ValidateSet works. It proposes the options as a list while you type your Cmdlet in the PowerShell ISE.
I would like to know if it's possible to retrieve values from a CSV-file (Import-CSV) and use them in the Param block so they become available in the drop down box of the PowerShell ISE when constructing the Cmdlet arguments? A bit in the same way that $Type works now, but then with values from the import file.
Function New-Name {
Param (
[parameter(Position=0, Mandatory=$true)]
[ValidateSet('Mailbox','Distribution','Folder','Role')]
[String]$Type,
[parameter(Position=1,Mandatory=$true)]
[String]$Name
)
Process { 'Foo' }
}
Here is something you can start with:
function New-Name {
param (
[parameter(Position=0, Mandatory=$true)]
[String]$Name
)
dynamicparam {
$attributes = new-object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Mandatory = $true
$attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
$values = #('MailBox', 'Tralala', 'Trilili') # your Import-Csv here
$ValidateSet = new-object System.Management.Automation.ValidateSetAttribute($values)
$attributeCollection.Add($ValidateSet)
$dynParam1 = new-object -Type System.Management.Automation.RuntimeDefinedParameter("Type", [string], $attributeCollection)
$paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add("Type", $dynParam1)
return $paramDictionary
}
process { 'Foo' }
}
Credits where credits are due, this largely comes from the following article from the Scripting Guy.
The code isn't pretty, but it does what you want.
I know this post is quite old, but with Powershell 6.2 and above you can use a .NET class at the beginning of the script and have the set controlled by a csv for example.
This article hear does an excellent job of explaining:
https://adamtheautomator.com/powershell-validateset/
I prefer TabExpansion++ module though this doesn't technically validate, it has some nice functionality...
Here's an example of an msbuild overloaded command to add some intellisense for projects
Register-ArgumentCompleter -CommandName "msbuild" -ParameterName "target" -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
$projectName = $fakeBoundParameter['project']
$projectFile = Join-Path (Get-Location) $projectName
$projectXml = [xml](Get-Content $projectFile)
$targets = $projectXml.Project.Target | Where-Object { $_.Name.ToString().StartsWith($wordToComplete) }
foreach($target in $projectXml.Project.Target)
{
New-CompletionResult -CompletionText "$($target.Name)"
}
}

Server Management - Need script to monitor free space on server

Need script to monitor free space on server and if the free memory space goes done certain threshold send alert mail.
PS- I think the solution will be Power Shell + Windows Timer Job. I am new to Power Shell though.
You can get free disk space using a command like this:
([wmi]"\\$computer\root\cimv2:Win32_logicalDisk.DeviceID='$drive'").FreeSpace
And you cand send an email using the function below:
function Send-EMail
{
param (
[parameter(Mandatory = $false)][string] $EmailTo = "<Your destination email>",
[parameter(Mandatory = $false)][string] $EmailFrom = "<The sending email address>",
[parameter(Mandatory = $false)][string] $EmailSubject = "Disk space problem",
[parameter(Mandatory = $false)][string] $SMTPServer = "<your smtp server>"
)
$MailMessage = New-Object System.Net.Mail.MailMessage
$MailMessage.From = ($EmailFrom)
$MailMessage.To.Add($EmailTo)
$MailMessage.Subject = $EmailSubject
$MailMessage.Body = $EmailBody
$MailMessage.IsBodyHTML = $true
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25)
$SMTPClient.Send($MailMessage)
}
Now combine these two functions in a PowerShell script that you can schedule with Windows scheduller.
You need to deal with WMI objects.
http://technet.microsoft.com/en-us/library/dd315295.aspx
Scripting might not be necessary. If you are on Wk3 Server you can enable quota management on volumes. W2k8 has extended quota management to volume folders also.
http://technet.microsoft.com/en-us/library/cc733029.aspx

Resources