diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/Commands.Automation.Test.csproj b/src/ResourceManager/Automation/Commands.Automation.Test/Commands.Automation.Test.csproj
index 05770ce7b707..10ed338d094d 100644
--- a/src/ResourceManager/Automation/Commands.Automation.Test/Commands.Automation.Test.csproj
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/Commands.Automation.Test.csproj
@@ -63,13 +63,20 @@
..\..\..\packages\Microsoft.Azure.Common.2.1.0\lib\net45\Microsoft.Azure.Common.NetFramework.dll
+
+ ..\..\..\packages\Microsoft.Azure.Management.Automation.2.0.0\lib\net40\Microsoft.Azure.Management.Automation.dll
+
False
..\..\..\packages\Microsoft.Azure.Management.Resources.2.18.11-preview\lib\net40\Microsoft.Azure.ResourceManager.dll
+
+ False
+ ..\..\..\packages\Microsoft.Azure.Test.Framework.1.0.5799.28345-prerelease\lib\net45\Microsoft.Azure.Test.Framework.dll
+
+ False
..\..\..\packages\Microsoft.Azure.Test.HttpRecorder.1.0.5799.28345-prerelease\lib\net45\Microsoft.Azure.Test.HttpRecorder.dll
- True
False
@@ -112,7 +119,9 @@
..\..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
-
+
+ False
+
@@ -135,6 +144,8 @@
+
+
@@ -195,6 +206,28 @@
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationScenarioTestsBase.cs b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationScenarioTestsBase.cs
new file mode 100644
index 000000000000..dff81da7fca9
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationScenarioTestsBase.cs
@@ -0,0 +1,65 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.Common.Authentication;
+using Microsoft.Azure.Management.Resources;
+using Microsoft.Azure.Subscriptions;
+using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using Microsoft.Azure.Test;
+using Microsoft.Azure.Management.Automation;
+using Microsoft.WindowsAzure.Commands.Test.Utilities.Common;
+
+namespace Microsoft.Azure.Commands.Automation.Test
+{
+ public abstract class AutomationScenarioTestsBase : RMTestBase
+ {
+ private EnvironmentSetupHelper helper;
+
+ protected AutomationScenarioTestsBase()
+ {
+ helper = new EnvironmentSetupHelper();
+ }
+
+ protected void SetupManagementClients()
+ {
+ var automationManagementClient = GetAutomationManagementClient();
+
+ helper.SetupManagementClients(automationManagementClient);
+ }
+
+ protected void RunPowerShellTest(params string[] scripts)
+ {
+ using (UndoContext context = UndoContext.Current)
+ {
+ context.Start(TestUtilities.GetCallingClass(2), TestUtilities.GetCurrentMethodName(2));
+
+ SetupManagementClients();
+
+ helper.SetupEnvironment(AzureModule.AzureResourceManager);
+
+ helper.SetupModules(AzureModule.AzureResourceManager,
+ "ScenarioTests\\" + this.GetType().Name + ".ps1",
+ helper.RMProfileModule,
+ helper.GetRMModulePath(@"AzureRM.Automation.psd1"));
+
+ helper.RunPowerShellTest(scripts);
+ }
+ }
+
+ protected AutomationManagementClient GetAutomationManagementClient()
+ {
+ return TestBase.GetServiceClient(new CSMTestEnvironmentFactory());
+ }
+ }
+}
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationTests.cs b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationTests.cs
new file mode 100644
index 000000000000..4b9fe27b4b7c
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationTests.cs
@@ -0,0 +1,78 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.WindowsAzure.Commands.ScenarioTest;
+using Xunit;
+
+namespace Microsoft.Azure.Commands.Automation.Test
+{
+ public class AutomationTests : AutomationScenarioTestsBase
+ {
+ [Fact(Skip = "Need x64 test framework.")]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ [Trait(Category.Service, Category.Automation)]
+ public void TestAutomationStartAndStopRunbook()
+ {
+ RunPowerShellTest("Test-AutomationStartAndStopRunbook -runbookPath ScenarioTests\\Resources\\Test-Workflow.ps1");
+ }
+
+ [Fact(Skip = "Need x64 test framework.")]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ [Trait(Category.Service, Category.Automation)]
+ public void TestAutomationPublishAndEditRunbook()
+ {
+ RunPowerShellTest("Test-AutomationPublishAndEditRunbook -runbookPath ScenarioTests\\Resources\\Test-Workflow.ps1 -editRunbookPath Resources\\Automation\\Test-WorkflowV2.ps1");
+ }
+
+ [Fact(Skip = "Need x64 test framework.")]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ [Trait(Category.Service, Category.Automation)]
+ public void TestAutomationConfigureRunbook()
+ {
+ RunPowerShellTest("Test-AutomationConfigureRunbook -runbookPath ScenarioTests\\Resources\\Write-DebugAndVerboseOutput.ps1");
+ }
+
+ [Fact]
+ [Trait(Category.Service, Category.Automation)]
+ [Trait(Category.RunType, Category.LiveOnly)]
+ public void TestAutomationSuspendAndResumeJob()
+ {
+ RunPowerShellTest("Test-AutomationSuspendAndResumeJob -runbookPath ScenarioTests\\Resources\\Use-WorkflowCheckpointSample.ps1");
+ }
+
+ [Fact]
+ [Trait(Category.Service, Category.Automation)]
+ [Trait(Category.RunType, Category.LiveOnly)]
+ public void TestAutomationStartRunbookOnASchedule()
+ {
+ RunPowerShellTest("Test-AutomationStartRunbookOnASchedule -runbookPath ScenarioTests\\Resources\\Test-Workflow.ps1");
+ }
+
+ [Fact(Skip = "Need x64 test framework.")]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ [Trait(Category.Service, Category.Automation)]
+ public void TestAutomationStartUnpublishedRunbook()
+ {
+ RunPowerShellTest("Test-AutomationStartUnpublishedRunbook -runbookPath ScenarioTests\\Resources\\Test-WorkFlowWithVariousParameters.ps1");
+ }
+
+ [Fact(Skip = "Need x64 test framework.")]
+ [Trait(Category.AcceptanceType, Category.CheckIn)]
+ [Trait(Category.Service, Category.Automation)]
+ public void TestAutomationRunbookWithParameter()
+ {
+ RunPowerShellTest("Test-RunbookWithParameter -runbookPath ScenarioTests\\Resources\\fastJob.ps1 @{'nums'='[1,2,3,4,5,6,7]'} 28");
+ }
+ }
+}
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationTests.ps1 b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationTests.ps1
new file mode 100644
index 000000000000..0cc8902689bd
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/AutomationTests.ps1
@@ -0,0 +1,363 @@
+# ----------------------------------------------------------------------------------
+#
+# Copyright Microsoft Corporation
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ----------------------------------------------------------------------------------
+
+<#
+.SYNOPSIS
+Checks whether the first string contains the second one
+#>
+
+$accountName='account'
+$location = "East US"
+
+function AssertContains
+{
+ param([string] $str, [string] $substr, [string] $message)
+
+ if (!$message)
+ {
+ $message = "Assertion failed because '$str' does not contain '$substr'"
+ }
+
+ if (!$str.Contains($substr))
+ {
+ throw $message
+ }
+
+ return $true
+}
+
+<#
+.SYNOPSIS
+Checks whether the runbook exists and if it exists, removes it and then imports a new runbook
+#>
+function CreateRunbook
+{
+ param([string] $runbookPath, [boolean] $byName=$false, [string[]] $tag, [string] $description)
+
+ $runbookName = gci $runbookPath | %{$_.BaseName}
+ $runbook = Get-AzureRmAutomationRunbook $accountName | where {$_.Name -eq $runbookName}
+ if ($runbook.Count -eq 1)
+ {
+ Remove-AzureRmAutomationRunbook $accountName -Name $runbookName -Force
+ }
+
+ if(!$byName)
+ {
+ return New-AzureRmAutomationRunbook $accountName -Path $runbookPath -Tag $tag -Description $description
+ }
+ else
+ {
+ return New-AzureRmAutomationRunbook $accountName -Name $runbookName -Tag $tag -Description $description
+ }
+}
+
+########################################################################### Automation Scenario Tests ###########################################################################
+
+######### $accountName and $subscriptionName should be provided in variables.yml in order to run the following Test Cases #######################
+
+<#
+.SYNOPSIS
+Waits for Job to be a specific status for approximately $numOfSeconds
+#>
+function WaitForJobStatus
+{
+ param([Guid] $Id, [Int] $numOfSeconds = 150, [String] $Status)
+
+ $timeElapse = 0
+ $interval = 3
+ $endStatus = @('completed','failed')
+ while($timeElapse -lt $numOfSeconds)
+ {
+ Wait-Seconds $interval
+ $timeElapse = $timeElapse + $interval
+ $job = Get-AzureRmAutomationJob -AutomationAccount $accountName -Id $Id
+ if($job.Status -eq $Status)
+ {
+ break
+ }
+ elseif($endStatus -contains $job.Status.ToLower())
+ {
+ Write-Output ("The Job with ID $($job.Id) reached $($job.Status) Status already.")
+ return
+ }
+ }
+ Assert-AreEqual $Status $job.Status "Job did not reach $Status status within $numOfSeconds seconds.";
+}
+
+<#
+.SYNOPSIS
+Tests Runbook with Parameters
+#>
+function Test-RunbookWithParameter
+{
+ param([string] $runbookPath, [HashTable] $parameters, [int]$expectedResult)
+
+ #Setup
+ $automationAccount = Get-AzureRmAutomationAccount -Name $accountName
+ Assert-NotNull $automationAccount "Automation account $accountName does not exist."
+
+ $runbook = CreateRunbook $runbookPath
+ Assert-NotNull $runbook "runBook $runbookPath does not import successfully."
+ $automationAccount | Publish-AzureRmAutomationRunbook -Name $runbook.Name
+
+ #Test
+ $job = $automationAccount | Start-AzureRmAutomationRunbook -Name $runbook.Name -Parameters $parameters
+ WaitForJobStatus -Id $job.Id -Status "Completed"
+ $jobOutput = $automationAccount | Get-AzureRmAutomationJobOutput -Id $job.Id -Stream Output
+ $automationAccount | Remove-AzureRmAutomationRunbook -Name $runbook.Name -Force
+ Assert-Throws { $automationAccount | Get-AzureRmAutomationRunbook -Name $runbook.Name}
+}
+
+<#
+.SYNOPSIS
+Tests of Start and Stop RunBook
+#>
+function Test-AutomationStartAndStopRunbook
+{
+ param([string] $runbookPath)
+
+ $automationAccount = Get-AzureRmAutomationAccount -Name $accountName
+ Assert-NotNull $automationAccount "Automation account $accountName does not exist."
+
+ $runbook = CreateRunbook $runbookPath
+ Assert-NotNull $runbook "runBook $runbookPath does not import successfully."
+ $automationAccount | Publish-AzureRmAutomationRunbook -Name $runbook.Name
+
+ #Test
+ $job = Start-AzureRmAutomationRunbook -Name $runbook.Name -AutomationAccountName $accountName
+ WaitForJobStatus -Id $job.Id -Status "Running"
+ $automationAccount | Stop-AzureRmAutomationJob -Id $job.Id
+ WaitForJobStatus -Id $job.Id -Status "Stopped"
+ $automationAccount | Remove-AzureRmAutomationRunbook -Name $runbook.Name -Force
+ Assert-Throws { $automationAccount | Get-AzureRmAutomationRunbook -Name $runbook.Name}
+}
+
+<#
+.SYNOPSIS
+Tests publishing runbook and editing runbook with and without Overwrite parameter
+#>
+function Test-AutomationPublishAndEditRunbook
+{
+ param([string] $runbookPath, [string] $editRunbookPath)
+
+ $runbook = CreateRunbook $runbookPath $true
+
+ #Publish Runbook
+ Publish-AzureRmAutomationRunbook $accountName -Name $runbook.Name
+ $publishedRunbook = Get-AzureRmAutomationRunbook $accountName -Name $runbook.Name
+ $runbookState = "Published"
+ Assert-AreEqual $publishedRunbook.State $runbookState "Runbook should be in $runbookState state"
+ $publishedRunbookDefn = Get-AzureRmAutomationRunbookDefinition $accountName -Name $runbook.Name
+
+ #Edit Runbook
+ Set-AzureRmAutomationRunbookDefinition $accountName -Name $runbook.Name -Path $runbookPath -Overwrite
+ $runbook = Get-AzureRmAutomationRunbook $accountName -Name $runbook.Name
+ $runbookState = "Edit"
+ Assert-AreEqual $runbook.State $runbookState "Runbook should be in $runbookState state"
+ $editedRunbookDefn = Get-AzureRmAutomationRunbookDefinition $accountName -Name $runbook.Name -Slot "Draft"
+ Assert-AreNotEqual $editedRunbookDefn.Content $publishedRunbookDefn.Content "Old content and edited content of the runbook shouldn't be equal"
+
+ Assert-Throws {Set-AzureRmAutomationRunbookDefinition $accountName -Name $runbook.Name -Path $editRunbookPath -PassThru -ErrorAction Stop}
+ Set-AzureRmAutomationRunbookDefinition $accountName -Name $runbook.Name -Path $editRunbookPath -Overwrite
+ $editedRunbookDefn2 = Get-AzureRmAutomationRunbookDefinition $accountName -Name $runbook.Name -Slot "Draft"
+ Assert-AreNotEqual $editedRunbookDefn2.Content $editedRunbookDefn.Content "Old content and edited content of the runbook shouldn't be equal"
+
+ Remove-AzureRmAutomationRunbook $accountName -Name $runbook.Name -Force
+ Assert-Throws {Get-AzureRmAutomationRunbook $accountName -Name $runbook.Name}
+
+}
+
+<#
+.SYNOPSIS
+Tests setting runbook configuration
+#>
+function Test-AutomationConfigureRunbook
+{
+ param([string] $runbookPath)
+
+ #Setup
+ $automationAccount = Get-AzureRmAutomationAccount -Name $accountName
+ Assert-NotNull $automationAccount "Automation account $accountName does not exist."
+ $runbook = CreateRunbook $runbookPath
+ Assert-NotNull $runbook "runbook ($runbookPath) isn't imported successfully."
+ Publish-AzureRmAutomationRunbook -Name $runbook.Name -AutomationAccountName $accountName
+
+ #Test
+
+ #Change the runbook configuration
+ $automationAccount | Set-AzureRmAutomationRunbook -Name $runbook.Name -LogVerbose $true -LogProgress $false
+ $runbook = $automationAccount | Get-AzureRmAutomationRunbook -Name $runbook.Name
+ Assert-NotNull $runbook "Runbook shouldn't be Null"
+ Assert-AreEqual $true $runbook.LogVerbose "Log Verbose mode should be true."
+ Assert-AreEqual $false $runbook.LogProgress "Log Progress mode should be false."
+
+ #Start runbook and wait for job complete
+ $job = $automationAccount | Start-AzureRmAutomationRunbook -Name $runbook.Name
+ WaitForJobStatus -Id $job.Id -Status "Completed"
+
+ #Check job output streams
+ $jobOutputs = $automationAccount | Get-AzureRmAutomationJobOutput -Id $job.Id -Stream "Output"
+ Assert-AreEqual 1 $jobOutputs.Count
+ AssertContains $jobOutputs[0].Text "output message" "The output stream is wrong."
+ #Verify that verbose streams are logged
+ $jobVerboseOutputs = Get-AzureRmAutomationJobOutput $accountName -Id $job.Id -Stream "Verbose"
+ Assert-AreEqual 1 $jobVerboseOutputs.Count
+ AssertContains $jobVerboseOutputs[0].Text "verbose message" "The verbose stream is wrong."
+ #Verify that progress stream isn't logged
+ $jobProgressOutputs = Get-AzureRmAutomationJobOutput -AutomationAccountName $accountName -Id $job.Id -Stream "Progress"
+ Assert-AreEqual 0 $jobProgressOutputs.Count
+
+ #Change the runbook configuration again and start the runbook
+ Set-AzureRmAutomationRunbook $accountName -Name $runbook.Name -LogVerbose $false -LogProgress $true
+ $job = Start-AzureRmAutomationRunbook $accountName -Name $runbook.Name
+ WaitForJobStatus -Id $job.Id -Status "Completed"
+ #Verify that progress stream is logged
+ $jobProgressOutputs = Get-AzureRmAutomationJobOutput $accountName -Id $job.Id -Stream "Progress"
+ Assert-AreNotEqual 0 $jobProgressOutputs.Count
+ Assert-AreEqual $jobProgressOutputs[0].Type "Progress"
+ #Verify that verbose streams aren't logged
+ $jobVerboseOutputs = Get-AzureRmAutomationJobOutput $accountName -Id $job.Id -Stream "Verbose"
+ Assert-AreEqual 0 $jobVerboseOutputs.Count
+
+ #Check whether the total number of jobs for the runbook is correct
+ $jobs = Get-AzureRmAutomationJob $accountName -RunbookName $runbook.Name
+ Assert-AreEqual 2 $jobs.Count "There should be 2 jobs in total for this runbook."
+
+ #Remove runbook
+ $automationAccount | Remove-AzureRmAutomationRunbook -Name $runbook.Name -Force
+ Assert-Throws {$automationAccount | Get-AzureRmAutomationRunbook -Name $runbook.Name}
+}
+
+<#
+.SYNOPSIS
+Tests suspending a started job and resuming a suspended job
+#>
+function Test-AutomationSuspendAndResumeJob
+{
+ param([string] $runbookPath)
+
+ #Setup
+ $automationAccount = Get-AzureRmAutomationAccount $accountName
+ Assert-NotNull $automationAccount "Automation account $accountName does not exist."
+ $runbook = CreateRunbook $runbookPath
+
+ #Test
+
+ $automationAccount | Publish-AzureRmAutomationRunbook -Name $runbook.Name
+ #Start, suspend, and then resume job
+ $job = Start-AzureRmAutomationRunbook $accountName -Name $runbook.Name
+ WaitForJobStatus -Id $job.Id -Status "Running"
+ Suspend-AzureRmAutomationJob $accountName -Id $job.Id
+ WaitForJobStatus -Id $job.Id -Status "Suspended"
+ $automationAccount | Resume-AzureRmAutomationJob -Id $job.Id
+ WaitForJobStatus -Id $job.Id -Status "Completed"
+
+ #Remove runbook
+ Remove-AzureRmAutomationRunbook -AutomationAccountName $accountName -Name $runbook.Name -Force
+ Assert-Throws {Get-AzureRmAutomationRunbook $accountName -Name $runbook.Name}
+}
+
+<#
+.SYNOPSIS
+Tests starting a runbook on a schedule
+#>
+function Test-AutomationStartRunbookOnASchedule
+{
+ param([string] $runbookPath)
+
+ #Setup
+ $automationAccount = Get-AzureRmAutomationAccount -Name $accountName
+ $runbook = CreateRunbook $runbookPath
+ Publish-AzureRmAutomationRunbook $accountName -Name $runbook.Name
+
+ #Test
+
+ #Create one time schedule
+ $oneTimeScheName = "oneTimeSchedule"
+ $schedule = Get-AzureRmAutomationSchedule $accountName | where {$_.Name -eq $oneTimeScheName}
+ if ($schedule.Count -eq 1)
+ {
+ Remove-AzureRmAutomationSchedule $accountName -Name $oneTimeScheName -Force
+ }
+ $startTime = (Get-Date).AddMinutes(7)
+ New-AzureRmAutomationSchedule $accountName -Name $oneTimeScheName -OneTime -StartTime $startTime
+ $oneTimeSchedule = Get-AzureRmAutomationSchedule $accountName -Name $oneTimeScheName
+ Assert-NotNull $oneTimeSchedule "$oneTimeScheName doesn't exist!"
+
+ #Create daily schedule
+ $dailyScheName = "dailySchedule"
+ $schedule = Get-AzureRmAutomationSchedule $accountName | where {$_.Name -eq $dailyScheName}
+ if ($schedule.Count -eq 1)
+ {
+ Remove-AzureRmAutomationSchedule $accountName -Name $dailyScheName -Force
+ }
+ $startTime = (Get-Date).AddDays(1)
+ $expiryTime = (Get-Date).AddDays(3)
+ New-AzureRmAutomationSchedule $accountName -Name $DailyScheName -StartTime $startTime -ExpiryTime $expiryTime -DayInterval 1
+ $dailySchedule = Get-AzureRmAutomationSchedule $accountName -Name $dailyScheName
+ Assert-NotNull $dailySchedule "$dailyScheName doesn't exist!"
+
+ $runbook = Register-AzureRmAutomationScheduledRunbook $accountName -Name $runbook.Name -ScheduleName $oneTimeScheName
+ Assert-AreEqual $oneTimeScheName $runbook.ScheduleNames "The runbook should be associated with $oneTimeScheName"
+ $runbook = Register-AzureRmAutomationScheduledRunbook $accountName -Name $runbook.Name -ScheduleName $dailyScheName
+ Assert-True { $runbook.ScheduleNames -Contains $dailyScheName} "The runbook should be associated with $dailyScheName"
+
+ #waiting for seven minutes
+ Wait-Seconds 420
+ $job = Get-AzureRmAutomationJob $accountName -Name $runbook.Name | where {$_.ScheduleName -eq $oneTimeScheName}
+ $jobSchedule = Get-AzureRmAutomationScheduledRunbook $accountName -RunbookName $runbook.Name -ScheduleName $oneTimeScheName
+ Assert-AreEqual 1 $jobSchedule.Count
+ Assert-AreEqual 1 $job.Count
+ WaitForJobStatus -Id $job.Id -Status "Completed"
+
+ #Edit schedule
+ $description = "Daily Schedule Description"
+ Set-AzureRmAutomationSchedule $accountName -Name $dailyScheName -Description $description
+ $dailySchedule = Get-AzureRmAutomationSchedule $accountName -Name $dailyScheName
+ Assert-AreEqual $description $dailySchedule.Description
+
+ Unregister-AzureRmAutomationScheduledRunbook $accountName -Name $runbook.Name -ScheduleName $dailyScheName
+ $jobSchedule = Get-AzureRmAutomationScheduledRunbook $accountName -RunbookName $runbook.Name -ScheduleName $dailyScheName
+ Assert-Null $jobSchedule "The runbook shouldn't have an association with $dailyScheName"
+
+ #Remove runbook and schedule
+ Remove-AzureRmAutomationSchedule $accountName -Name $oneTimeScheName -Force
+ Assert-Throws {$automationAccount | Get-AzureRmAutomationSchedule -Name $oneTimeScheName}
+ $automationAccount | Remove-AzureRmAutomationSchedule -Name $dailyScheName -Force
+ Assert-Throws {$automationAccount | Get-AzureRmAutomationSchedule -Name $dailyScheName}
+ Remove-AzureRmAutomationRunbook $accountName -Name $runbook.Name -Force
+ Assert-Throws {Get-AzureRmAutomationRunbook $accountName -Name $runbook.Name}
+}
+
+<#
+.SYNOPSIS
+Tests starting an unpublished runbook
+#>
+function Test-AutomationStartUnpublishedRunbook
+{
+ param([string] $runbookPath)
+
+ $tags = @("tag1","tag2")
+ $description = "Runbook Description"
+ $c = Get-Date
+ $runbookParameters = @{"a" = "stringParameter"; "b" = 123; "c" = $c}
+ $runbook = CreateRunbook $runbookPath $false $tags $description
+ Assert-NotNull $runbook "runBook $runbookPath does not import successfully."
+ Assert-NotNull $runbook.Tags "Tags of the runbook shouldn't be Null."
+ Assert-NotNull $runbook.Description "Description of the runbook shouldn't be Null."
+ Assert-Throws {Start-AzureRmAutomationRunbook $accountName -Name $runbook.Name -Parameters $runbookParameters -PassThru -ErrorAction Stop}
+
+ Remove-AzureRmAutomationRunbook $accountName -Name $runbook.Name -Force
+ Assert-Throws {Get-AzureRmAutomationRunbook $accountName -Name $runbook.Name -Parameters $runbookParameters -PassThru -ErrorAction Stop}
+}
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-WorkFlowWithVariousParameters.ps1 b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-WorkFlowWithVariousParameters.ps1
new file mode 100644
index 000000000000..62671711bd0c
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-WorkFlowWithVariousParameters.ps1
@@ -0,0 +1,7 @@
+workflow Test-WorkFlowWithVariousParameters
+{
+ param([string] $a, [int] $b, [DateTime] $c)
+ "a is " + $a
+ "b is " + $b
+ "c is " + $c
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-Workflow.ps1 b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-Workflow.ps1
new file mode 100644
index 000000000000..3f2a2cde1916
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-Workflow.ps1
@@ -0,0 +1,5 @@
+Workflow Test-Workflow {
+ get-date
+ start-sleep -s 40
+ get-date
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-WorkflowV2.ps1 b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-WorkflowV2.ps1
new file mode 100644
index 000000000000..7f2511572f23
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Test-WorkflowV2.ps1
@@ -0,0 +1,3 @@
+Workflow Test-Workflow {
+ get-date
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Use-WorkflowCheckpointSample.ps1 b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Use-WorkflowCheckpointSample.ps1
new file mode 100644
index 000000000000..2a3417513c8e
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Use-WorkflowCheckpointSample.ps1
@@ -0,0 +1,11 @@
+workflow Use-WorkflowCheckpointSample
+{
+ Write-Output "Before Checkpoint."
+ start-sleep -s 20
+
+ # A checkpoint is created.
+ Checkpoint-Workflow
+
+ # This line occurs after the checkpoint. The runbook will start here on resume.
+ Write-Output "After Checkpoint."
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Write-DebugAndVerboseOutput.ps1 b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Write-DebugAndVerboseOutput.ps1
new file mode 100644
index 000000000000..ff1eb3a97923
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/Write-DebugAndVerboseOutput.ps1
@@ -0,0 +1,6 @@
+workflow Write-DebugAndVerboseOutput
+{
+ "output message"
+ write-debug -message "debug message"
+ write-verbose -message "verbose message"
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/fastjob.ps1 b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/fastjob.ps1
new file mode 100644
index 000000000000..0a9c430faa6a
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/ScenarioTests/Resources/fastjob.ps1
@@ -0,0 +1,6 @@
+workflow fastJob
+{
+ param([int[]] $nums)
+ $sum=($nums | Measure-Object -Sum).Sum
+ echo $sum
+}
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation.Test/packages.config b/src/ResourceManager/Automation/Commands.Automation.Test/packages.config
index ea87477df30a..cb7e9dee7ed3 100644
--- a/src/ResourceManager/Automation/Commands.Automation.Test/packages.config
+++ b/src/ResourceManager/Automation/Commands.Automation.Test/packages.config
@@ -6,6 +6,7 @@
+
diff --git a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/NewAzureAutomationWebhook.cs b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/NewAzureAutomationWebhook.cs
index 9a38d81b3484..211adfdb3317 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/NewAzureAutomationWebhook.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/NewAzureAutomationWebhook.cs
@@ -64,7 +64,7 @@ public class NewAzureAutomationWebhook : AzureAutomationBaseCmdlet
///
/// Gets or sets the Runbook parameters
///
- [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true,
+ [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false,
HelpMessage = "The Runbook parameters name/value.")]
public IDictionary Parameters { get; set; }
diff --git a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RegisterAzureAutomationScheduledRunbook.cs b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RegisterAzureAutomationScheduledRunbook.cs
index 3cc9ce57f463..a76a5e401cae 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RegisterAzureAutomationScheduledRunbook.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RegisterAzureAutomationScheduledRunbook.cs
@@ -47,7 +47,7 @@ public class RegisterAzureAutomationScheduledRunbook : AzureAutomationBaseCmdlet
///
/// Gets or sets the runbook parameters.
///
- [Parameter(ParameterSetName = AutomationCmdletParameterSets.ByRunbookNameAndScheduleName, Mandatory = false, ValueFromPipelineByPropertyName = true,
+ [Parameter(ParameterSetName = AutomationCmdletParameterSets.ByRunbookNameAndScheduleName, Mandatory = false, ValueFromPipelineByPropertyName = false,
HelpMessage = "The runbook parameters.")]
public IDictionary Parameters { get; set; }
diff --git a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RemoveAzureAutomationDscConfiguration.cs b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RemoveAzureAutomationDscConfiguration.cs
new file mode 100644
index 000000000000..e008d76d0f3a
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RemoveAzureAutomationDscConfiguration.cs
@@ -0,0 +1,56 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.Commands.Automation.Properties;
+using System.Management.Automation;
+using System.Security.Permissions;
+
+namespace Microsoft.Azure.Commands.Automation.Cmdlet
+{
+ ///
+ /// Remove a DSC configuration
+ ///
+ [Cmdlet(VerbsCommon.Remove, "AzureRmAutomationDscConfiguration")]
+ public class RemoveAzureAutomationDscConfiguration : AzureAutomationBaseCmdlet
+ {
+ ///
+ /// Gets or sets the Configuration name.
+ ///
+ [Parameter(Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true,
+ HelpMessage = "The configuration name.")]
+ [Alias("ConfigurationName")]
+ [ValidateNotNullOrEmpty]
+ public string Name { get; set; }
+
+ [Parameter(Position = 3, HelpMessage = "Force confirmation of the removal of the configuration")]
+ public SwitchParameter Force { get; set; }
+
+ ///
+ /// Execute this cmdlet.
+ ///
+ [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
+ protected override void AutomationProcessRecord()
+ {
+ ConfirmAction(
+ Force.IsPresent,
+ string.Format(Resources.RemovingAzureAutomationDscConfigurationWarning, "DSC Configuration"),
+ string.Format(Resources.RemoveAzureAutomationResourceDescription, "DSC Configuration"),
+ Name,
+ () => this.AutomationClient.DeleteConfiguration(
+ this.ResourceGroupName,
+ this.AutomationAccountName,
+ this.Name));
+ }
+ }
+}
diff --git a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RemoveAzureAutomationDscNodeConfiguration.cs b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RemoveAzureAutomationDscNodeConfiguration.cs
new file mode 100644
index 000000000000..2a43a5a37ea2
--- /dev/null
+++ b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/RemoveAzureAutomationDscNodeConfiguration.cs
@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------------
+//
+// Copyright Microsoft Corporation
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ----------------------------------------------------------------------------------
+
+using Microsoft.Azure.Commands.Automation.Properties;
+using System.Management.Automation;
+using System.Security.Permissions;
+
+namespace Microsoft.Azure.Commands.Automation.Cmdlet
+{
+ ///
+ /// Remove a DSC configuration
+ ///
+ [Cmdlet(VerbsCommon.Remove, "AzureRmAutomationDscNodeConfiguration")]
+ public class RemoveAzureAutomationDscNodeConfiguration : AzureAutomationBaseCmdlet
+ {
+ ///
+ /// Gets or sets the Configuration name.
+ ///
+ [Parameter(Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true,
+ HelpMessage = "The node configuration name.")]
+ [Alias("NodeConfigurationName")]
+ [ValidateNotNullOrEmpty]
+ public string Name { get; set; }
+
+ [Parameter(Position = 3, HelpMessage = "Force confirmation of the removal of the node configuration")]
+ public SwitchParameter Force { get; set; }
+
+ [Parameter(Position = 4, HelpMessage = "Remove the node configuration even if the node configuration is mapped to one or more nodes")]
+ public SwitchParameter IgnoreNodeMappings { get; set; }
+
+ ///
+ /// Execute this cmdlet.
+ ///
+ [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
+ protected override void AutomationProcessRecord()
+ {
+ ConfirmAction(
+ Force.IsPresent,
+ string.Format(Resources.RemovingAzureAutomationResourceWarning, "DSC node configuration"),
+ string.Format(Resources.RemoveAzureAutomationResourceDescription, "DSC node configuration"),
+ Name,
+ () => this.AutomationClient.DeleteNodeConfiguration(
+ this.ResourceGroupName,
+ this.AutomationAccountName,
+ this.Name,
+ IgnoreNodeMappings.IsPresent));
+ }
+ }
+}
diff --git a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationDscCompilationJob.cs b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationDscCompilationJob.cs
index 53a8afe1cc11..884e3b705e42 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationDscCompilationJob.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationDscCompilationJob.cs
@@ -42,7 +42,7 @@ public class StartAzureAutomationDscCompilationJob : AzureAutomationBaseCmdlet
///
/// Gets or sets the configuration parameters.
///
- [Parameter(Mandatory = false, HelpMessage = "The compilation job parameters.")]
+ [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, HelpMessage = "The compilation job parameters.")]
public IDictionary Parameters { get; set; }
///
diff --git a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationRunbook.cs b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationRunbook.cs
index 04c562e53044..75c485f25181 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationRunbook.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationRunbook.cs
@@ -12,12 +12,21 @@
// limitations under the License.
// ----------------------------------------------------------------------------------
+using System;
using System.Collections;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Linq;
using System.Management.Automation;
+using System.Management.Automation.Runspaces;
using System.Security.Permissions;
+using System.Threading;
using Microsoft.Azure.Commands.Automation.Common;
+using Microsoft.Azure.Commands.Automation.Model;
using Job = Microsoft.Azure.Commands.Automation.Model.Job;
+using JobStream = Microsoft.Azure.Commands.Automation.Model.JobStream;
+using Microsoft.Azure.Commands.Automation.Properties;
namespace Microsoft.Azure.Commands.Automation.Cmdlet
@@ -25,14 +34,31 @@ namespace Microsoft.Azure.Commands.Automation.Cmdlet
///
/// Starts an Azure automation runbook.
///
- [Cmdlet(VerbsLifecycle.Start, "AzureRmAutomationRunbook", DefaultParameterSetName = AutomationCmdletParameterSets.ByRunbookName)]
- [OutputType(typeof(Job))]
+ [Cmdlet(VerbsLifecycle.Start, "AzureRmAutomationRunbook", DefaultParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob)]
+ [OutputType(typeof(Job), ParameterSetName = new []{ AutomationCmdletParameterSets.ByAsynchronousReturnJob })]
+ [OutputType(typeof(PSObject), ParameterSetName = new []{ AutomationCmdletParameterSets.BySynchronousReturnJobOutput })]
public class StartAzureAutomationRunbook : AzureAutomationBaseCmdlet
{
+ ///
+ /// True to wait for job output
+ ///
+ private bool wait;
+
+ ///
+ /// Timeout value
+ ///
+ private int timeout = Constants.MaxWaitSeconds;
+
+ ///
+ /// Polling interval
+ ///
+ private const int pollingIntervalInSeconds = 5;
+
///
/// Gets or sets the runbook name
///
- [Parameter(ParameterSetName = AutomationCmdletParameterSets.ByRunbookName, Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook name.")]
+ [Parameter(ParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob, Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook name.")]
+ [Parameter(ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook name.")]
[ValidateNotNullOrEmpty]
[Alias("RunbookName")]
public string Name { get; set; }
@@ -40,27 +66,102 @@ public class StartAzureAutomationRunbook : AzureAutomationBaseCmdlet
///
/// Gets or sets the runbook parameters.
///
- [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook parameters.")]
+ [Parameter(ParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob, Mandatory = false, HelpMessage = "The runbook parameters.")]
+ [Parameter(ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, Mandatory = false, HelpMessage = "The runbook parameters.")]
public IDictionary Parameters { get; set; }
///
/// Gets or sets the optional hybrid agent friendly name upon which the runbook should be executed.
///
- [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "Optional name of the hybrid agent which should execute the runbook")]
+ [Parameter(ParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob, Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "Optional name of the hybrid agent which should execute the runbook")]
+ [Parameter(ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "Optional name of the hybrid agent which should execute the runbook")]
[Alias("HybridWorker")]
public string RunOn { get; set; }
+
+ ///
+ /// Gets or sets the switch parameter to wait for job output
+ ///
+ [Parameter(Mandatory = false, ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, HelpMessage = "Wait for job to complete, suspend, or fail.")]
+ public SwitchParameter Wait
+ {
+ get { return this.wait; }
+ set { this.wait = value; }
+ }
+
+ ///
+ /// Gets or sets the switch parameter to wait for job output
+ ///
+ [Parameter(Mandatory = false, ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, HelpMessage = "Maximum time in seconds to wait for job completion. Default max wait time is 10800 seconds.")]
+ public int MaxWaitSeconds
+ {
+ get { return this.timeout; }
+ set { this.timeout = value; }
+ }
+
///
/// Execute this cmdlet.
///
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
protected override void AutomationProcessRecord()
{
- Job job = null;
+ var job = this.AutomationClient.StartRunbook(this.ResourceGroupName, this.AutomationAccountName, this.Name, this.Parameters, this.RunOn);
+
+ // if no wait, return job object
+ if (!this.Wait.IsPresent)
+ {
+ this.WriteObject(job);
+ return;
+ }
+
+ // wait for job completion
+ if (!PollJobCompletion(job.JobId))
+ {
+ throw new ResourceCommonException(typeof(Job), string.Format(CultureInfo.CurrentCulture, Resources.JobCompletionMaxWaitReached));
+ }
+
+ // retrieve job streams
+ var nextLink = string.Empty;
+ do
+ {
+ var jobOutputList = this.AutomationClient.GetJobStream(this.ResourceGroupName, this.AutomationAccountName, job.JobId, null, StreamType.Output.ToString(), ref nextLink);
+ foreach (var jobOutputRecord in jobOutputList)
+ {
+ var response = this.AutomationClient.GetJobStreamRecordAsPsObject(this.ResourceGroupName, this.AutomationAccountName, job.JobId, jobOutputRecord.StreamRecordId);
+ this.GenerateCmdletOutput(response);
+ }
+ } while (!string.IsNullOrEmpty(nextLink));
+ }
+
+ private bool PollJobCompletion(Guid jobId)
+ {
+ var timeoutIncrement = 0;
+ do
+ {
+ Thread.Sleep(TimeSpan.FromSeconds(pollingIntervalInSeconds));
+ timeoutIncrement += pollingIntervalInSeconds;
+
+ var job = this.AutomationClient.GetJob(this.ResourceGroupName, this.AutomationAccountName, jobId);
+ if (!IsJobTerminalState(job))
+ {
+ if (IsVerbose()) WriteVerbose(string.Format(Resources.JobProgressState, job.JobId, job.Status, DateTime.Now));
+ }
+ else
+ {
+ if (IsVerbose()) WriteVerbose(string.Format(Resources.JobTerminalState, job.JobId, job.Status, DateTime.Now));
+ return true;
+ }
+ } while (this.MaxWaitSeconds < 0 || timeoutIncrement <= this.MaxWaitSeconds);
- job = this.AutomationClient.StartRunbook(this.ResourceGroupName, this.AutomationAccountName, this.Name, this.Parameters, this.RunOn);
+ return false;
+ }
- this.WriteObject(job);
+ private static bool IsJobTerminalState(Job job)
+ {
+ return job.Status.Equals(JobState.Completed.ToString(), StringComparison.OrdinalIgnoreCase) ||
+ job.Status.Equals(JobState.Failed.ToString(), StringComparison.OrdinalIgnoreCase) ||
+ job.Status.Equals(JobState.Stopped.ToString(), StringComparison.OrdinalIgnoreCase) ||
+ job.Status.Equals(JobState.Suspended.ToString(), StringComparison.OrdinalIgnoreCase);
}
}
}
diff --git a/src/ResourceManager/Automation/Commands.Automation/Commands.Automation.csproj b/src/ResourceManager/Automation/Commands.Automation/Commands.Automation.csproj
index eb5ac70d3dce..c7b3e78a4ea8 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Commands.Automation.csproj
+++ b/src/ResourceManager/Automation/Commands.Automation/Commands.Automation.csproj
@@ -63,9 +63,9 @@
..\..\..\packages\Microsoft.Azure.Common.Authentication.1.5.2-preview\lib\net45\Microsoft.Azure.Common.Authentication.dll
True
-
+
False
- ..\..\..\packages\Microsoft.Azure.Management.Automation.0.50.2-prerelease\lib\portable-net45+wp8+wpa81+win\Microsoft.Azure.Management.Automation.dll
+ ..\..\..\packages\Microsoft.Azure.Management.Automation.2.0.0\lib\portable-net45+wp8+wpa81+win\Microsoft.Azure.Management.Automation.dll
False
@@ -164,10 +164,12 @@
+
+
diff --git a/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClient.cs b/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClient.cs
index 775f13b74d73..22194c7c10a4 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClient.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClient.cs
@@ -18,17 +18,20 @@
using System.Globalization;
using System.Linq;
using System.IO;
+using System.Management.Automation;
using System.Net;
using System.Security;
using System.Security.Cryptography.X509Certificates;
-using Microsoft.Azure.Commands.Automation.Cmdlet;
using Microsoft.Azure.Commands.Automation.Model;
using Microsoft.Azure.Commands.Automation.Properties;
using Microsoft.Azure.Management.Automation;
using Microsoft.Azure.Management.Automation.Models;
using Microsoft.Azure.Common.Authentication.Models;
using Newtonsoft.Json;
+using Microsoft.Azure.Common.Authentication;
+using Hyak.Common;
+using AutomationManagement = Microsoft.Azure.Management.Automation;
using AutomationAccount = Microsoft.Azure.Commands.Automation.Model.AutomationAccount;
using Module = Microsoft.Azure.Commands.Automation.Model.Module;
using Runbook = Microsoft.Azure.Commands.Automation.Model.Runbook;
@@ -43,11 +46,6 @@
namespace Microsoft.Azure.Commands.Automation.Common
{
- using AutomationManagement = Azure.Management.Automation;
- using Microsoft.Azure.Common.Authentication;
- using Hyak.Common;
-
-
public partial class AutomationClient : IAutomationClient
{
private readonly AutomationManagement.IAutomationManagementClient automationManagementClient;
@@ -996,6 +994,47 @@ public JobStreamRecord GetJobStreamRecord(string resourceGroupName, string autom
return new JobStreamRecord(response.JobStream, resourceGroupName, automationAccountName, jobId);
}
+ public object GetJobStreamRecordAsPsObject(string resourceGroupName, string automationAccountName, Guid jobId, string jobStreamId)
+ {
+ var response = this.automationManagementClient.JobStreams.Get(resourceGroupName, automationAccountName, jobId, jobStreamId);
+
+ if (response.JobStream.Properties == null || response.JobStream.Properties.Value == null) return null;
+
+ // PowerShell Workflow runbook jobs would have the below additional properties, remove them from job output
+ // we do not know the runbook type, remove will only remove if exists
+ response.JobStream.Properties.Value.Remove("PSComputerName");
+ response.JobStream.Properties.Value.Remove("PSShowComputerName");
+ response.JobStream.Properties.Value.Remove("PSSourceJobInstanceId");
+
+ var paramTable = new Hashtable();
+
+ foreach (var kvp in response.JobStream.Properties.Value)
+ {
+ object paramValue;
+ try
+ {
+ paramValue = ((object)PowerShellJsonConverter.Deserialize(kvp.Value.ToString()));
+ }
+ catch (CmdletInvocationException exception)
+ {
+ if (!exception.Message.Contains("Invalid JSON primitive"))
+ throw;
+
+ paramValue = kvp.Value;
+ }
+
+ // for primitive outputs, the record will be in form "value" : "primitive type value". Return the key and return the primitive type value
+ if (response.JobStream.Properties.Value.Count == 1 && response.JobStream.Properties.Value.ContainsKey("value"))
+ {
+ return paramValue;
+ }
+
+ paramTable.Add(kvp.Key, paramValue);
+ }
+
+ return paramTable;
+ }
+
public Job GetJob(string resourceGroupName, string automationAccountName, Guid Id)
{
var job = this.automationManagementClient.Jobs.Get(resourceGroupName, automationAccountName, Id).Job;
@@ -1637,15 +1676,6 @@ private IDictionary ProcessRunbookParameters(string resourceGrou
string.Format(CultureInfo.CurrentCulture, Resources.InvalidRunbookParameters));
}
- var hasJobStartedBy =
- filteredParameters.Any(filteredParameter => filteredParameter.Key == Constants.JobStartedByParameterName);
-
- if (!hasJobStartedBy)
- {
- filteredParameters.Add(Constants.JobStartedByParameterName,
- PowerShellJsonConverter.Serialize(Constants.ClientIdentity));
- }
-
return filteredParameters;
}
diff --git a/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClientDSC.cs b/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClientDSC.cs
index af9233e1857d..2923d3555e2d 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClientDSC.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Common/AutomationClientDSC.cs
@@ -300,6 +300,29 @@ public Model.DscConfiguration CreateConfiguration(
}
}
+ public void DeleteConfiguration(string resourceGroupName, string automationAccountName, string name)
+ {
+ Requires.Argument("ResourceGroupName", resourceGroupName).NotNull();
+ Requires.Argument("AutomationAccountName", automationAccountName).NotNull();
+ using (var request = new RequestSettings(this.automationManagementClient))
+ {
+ try
+ {
+ this.automationManagementClient.Configurations.Delete(resourceGroupName, automationAccountName, name);
+ }
+ catch (CloudException cloudException)
+ {
+ if (cloudException.Response.StatusCode == HttpStatusCode.NoContent)
+ {
+ throw new ResourceNotFoundException(
+ typeof(Model.DscConfiguration),
+ string.Format(CultureInfo.CurrentCulture, Resources.ConfigurationNotFound, name));
+ }
+ throw;
+ }
+ }
+ }
+
#endregion
#region DscMetaConfig Operations
@@ -568,7 +591,7 @@ public Model.DscNode GetDscNodeById(
IEnumerable dscNodes;
- if (!String.IsNullOrEmpty(status))
+ if (!string.IsNullOrEmpty(status))
{
dscNodes = AutomationManagementClient.ContinuationTokenHandler(
skipToken =>
@@ -727,7 +750,7 @@ string nodeConfigurationName
automationAccountName,
new DscNodePatchParameters
{
- Id = nodeId,
+ NodeId = nodeId,
NodeConfiguration = nodeConfiguration
}).Node;
@@ -1262,6 +1285,48 @@ public Model.NodeConfiguration CreateNodeConfiguration(
}
}
+ public void DeleteNodeConfiguration(string resourceGroupName, string automationAccountName, string name, bool ignoreNodeMappings)
+ {
+ Requires.Argument("ResourceGroupName", resourceGroupName).NotNull();
+ Requires.Argument("AutomationAccountName", automationAccountName).NotNull();
+ Requires.Argument("NodeConfigurationName", name).NotNull();
+
+ using (var request = new RequestSettings(this.automationManagementClient))
+ {
+ try
+ {
+ if (ignoreNodeMappings)
+ {
+ this.automationManagementClient.NodeConfigurations.Delete(resourceGroupName, automationAccountName, name);
+ }
+ else
+ {
+ var nodeList = this.ListDscNodesByNodeConfiguration(resourceGroupName, automationAccountName, name, null);
+ if (nodeList.Any())
+ {
+ throw new ResourceCommonException(
+ typeof (Model.NodeConfiguration),
+ string.Format(CultureInfo.CurrentCulture, Resources.CannotDeleteNodeConfiguration, name));
+ }
+ else
+ {
+ this.automationManagementClient.NodeConfigurations.Delete(resourceGroupName, automationAccountName, name);
+ }
+ }
+ }
+ catch (CloudException cloudException)
+ {
+ if (cloudException.Response.StatusCode == HttpStatusCode.NotFound)
+ {
+ throw new ResourceNotFoundException(
+ typeof(Model.NodeConfiguration),
+ string.Format(CultureInfo.CurrentCulture, Resources.NodeConfigurationNotFound, name));
+ }
+ throw;
+ }
+ }
+ }
+
#endregion
#region dsc reports
diff --git a/src/ResourceManager/Automation/Commands.Automation/Common/AutomationCmdletParameterSet.cs b/src/ResourceManager/Automation/Commands.Automation/Common/AutomationCmdletParameterSet.cs
index 2996d41e1ae9..071510a61831 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Common/AutomationCmdletParameterSet.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Common/AutomationCmdletParameterSet.cs
@@ -79,6 +79,16 @@ internal static class AutomationCmdletParameterSets
///
internal const string ByRunbookName = "ByRunbookName";
+ ///
+ /// The ByAsynchronousReturnJob.
+ ///
+ internal const string ByAsynchronousReturnJob = "ByAsynchronousReturnJob";
+
+ ///
+ /// The BySynchronousReturnJob.
+ ///
+ internal const string BySynchronousReturnJobOutput = "BySynchronousReturnJobOutput";
+
///
/// The Configuration name parameter set.
///
diff --git a/src/ResourceManager/Automation/Commands.Automation/Common/Constants.cs b/src/ResourceManager/Automation/Commands.Automation/Common/Constants.cs
index fd95c4bcd3ea..b35962ebf345 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Common/Constants.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Common/Constants.cs
@@ -63,6 +63,8 @@ public class AutomationAccountState
public const int JobSummaryLength = 80;
+ public const int MaxWaitSeconds = 10800;
+
// The template file is a json
public const string TemplateFile = @"https://eus2oaasibizamarketprod1.blob.core.windows.net/automationdscpreview/azuredeployV2.json";
diff --git a/src/ResourceManager/Automation/Commands.Automation/Common/IAutomationClient.cs b/src/ResourceManager/Automation/Commands.Automation/Common/IAutomationClient.cs
index 7795b99e17ef..704bccf7a9d1 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Common/IAutomationClient.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Common/IAutomationClient.cs
@@ -62,6 +62,8 @@ public interface IAutomationClient
IEnumerable ListNodeConfigurations(string resourceGroupName, string automationAccountName, string rollupStatus);
NodeConfiguration CreateNodeConfiguration(string resourceGroupName, string automationAccountName, string sourcePath, string nodeConfiguraionName, bool overWrite);
+
+ void DeleteNodeConfiguration(string resourceGroupName, string automationAccountName, string name, bool ignoreNodeMappings);
#endregion
#region Configurations
@@ -74,6 +76,8 @@ public interface IAutomationClient
DirectoryInfo GetConfigurationContent(string resourceGroupName, string automationAccountName, string configurationName, bool? isDraft, string outputFolder, bool overwriteExistingFile);
+ void DeleteConfiguration(string resourceGroupName, string automationAccountName, string name);
+
#endregion
#region AgentRegistrationInforamtion
@@ -237,6 +241,8 @@ IEnumerable GetJobStream(string resourceGroupName, string automationA
JobStreamRecord GetJobStreamRecord(string resourceGroupName, string automationAccountName, Guid jobId, string jobStreamId);
+ object GetJobStreamRecordAsPsObject(string resourceGroupName, string automationAccountName, Guid jobId, string jobStreamId);
+
#endregion
#region Certificates
diff --git a/src/ResourceManager/Automation/Commands.Automation/Model/AutomationAccount.cs b/src/ResourceManager/Automation/Commands.Automation/Model/AutomationAccount.cs
index 016d4b3ca972..e0b0e52c5f25 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Model/AutomationAccount.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Model/AutomationAccount.cs
@@ -48,6 +48,8 @@ public AutomationAccount(string resourceGroupName, AutomationManagement.Models.A
this.ResourceGroupName = automationAccount.Id.Substring(1).Split(Convert.ToChar("/"))[3];
}
+ this.SubscriptionId = automationAccount.Id.Substring(1).Split(Convert.ToChar("/"))[1];
+
this.AutomationAccountName = automationAccount.Name;
this.Location = automationAccount.Location;
@@ -73,6 +75,11 @@ public AutomationAccount()
{
}
+ ///
+ /// Gets or sets the Subscription ID
+ ///
+ public string SubscriptionId { get; set; }
+
///
/// Gets or sets the resource group name.
///
diff --git a/src/ResourceManager/Automation/Commands.Automation/Model/DscNode.cs b/src/ResourceManager/Automation/Commands.Automation/Model/DscNode.cs
index fdae4b164f0e..326b655623c3 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Model/DscNode.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Model/DscNode.cs
@@ -39,7 +39,7 @@ public DscNode(string resourceGroupName, string automationAccountName, Automatio
this.ResourceGroupName = resourceGroupName;
this.AutomationAccountName = automationAccountName;
this.Name = node.Name;
- this.Id = node.Id;
+ this.Id = node.NodeId.ToString("D");
this.IpAddress = node.Ip;
this.LastSeen = node.LastSeen.ToLocalTime();
this.RegistrationTime = node.RegistrationTime.ToLocalTime();
diff --git a/src/ResourceManager/Automation/Commands.Automation/Model/DscNodeReport.cs b/src/ResourceManager/Automation/Commands.Automation/Model/DscNodeReport.cs
index a0c675392531..cf24712505de 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Model/DscNodeReport.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Model/DscNodeReport.cs
@@ -36,7 +36,7 @@ public DscNodeReport(string resourceGroupName, string automationAccountName, str
Requires.Argument("ResourceGroupName", resourceGroupName).NotNull();
Requires.Argument("AutomationAccountName", automationAccountName).NotNull();
Requires.Argument("dscNodeReport", dscNodeReport).NotNull();
- Requires.Argument("dscNodeReport", dscNodeReport.Id).NotNull();
+ Requires.Argument("dscNodeReport", dscNodeReport.ReportId).NotNull();
this.ResourceGroupName = resourceGroupName;
this.AutomationAccountName = automationAccountName;
@@ -44,7 +44,7 @@ public DscNodeReport(string resourceGroupName, string automationAccountName, str
this.EndTime = dscNodeReport.EndTime;
this.LastModifiedTime = dscNodeReport.LastModifiedTime;
this.ReportType = dscNodeReport.Type;
- this.Id = dscNodeReport.Id.ToString("D");
+ this.Id = dscNodeReport.ReportId.ToString("D");
this.NodeId = nodeId;
this.Status = dscNodeReport.Status;
this.RefreshMode = dscNodeReport.RefreshMode;
diff --git a/src/ResourceManager/Automation/Commands.Automation/Model/Job.cs b/src/ResourceManager/Automation/Commands.Automation/Model/Job.cs
index 43f7fd95d66b..2fbf68cd60d9 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Model/Job.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Model/Job.cs
@@ -61,6 +61,7 @@ public Job(string resourceGroupName, string accountName, Azure.Management.Automa
this.EndTime = job.Properties.EndTime.HasValue ? job.Properties.EndTime.Value.ToLocalTime() : (DateTimeOffset?) null;
this.LastStatusModifiedTime = job.Properties.LastStatusModifiedTime;
this.HybridWorker = job.Properties.RunOn;
+ this.StartedBy = job.Properties.StartedBy;
this.JobParameters = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
foreach (var kvp in job.Properties.Parameters)
{
@@ -161,5 +162,10 @@ public Job()
/// Gets or sets the HybridWorker.
///
public string HybridWorker { get; set; }
+
+ ///
+ /// Gets or sets the StartedBy property.
+ ///
+ public string StartedBy { get; set; }
}
}
diff --git a/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.Designer.cs b/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.Designer.cs
index db513eb8c67d..68a054224f0c 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.Designer.cs
+++ b/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.34014
+// Runtime Version:4.0.30319.18449
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -96,6 +96,15 @@ internal static string AutomationOperationFailed {
}
}
+ ///
+ /// Looks up a localized string similar to The node configuration '{0}' is currently assigned to one or more nodes. Either specify the IgnoreNodeMappings parameter, or reassign these nodes to a different node configuration, to delete this node configuration..
+ ///
+ internal static string CannotDeleteNodeConfiguration {
+ get {
+ return ResourceManager.GetString("CannotDeleteNodeConfiguration", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The certificate already exists. Certificate name: {0}..
///
@@ -348,6 +357,15 @@ internal static string InvalidRunbookTypeForExtension {
}
}
+ ///
+ /// Looks up a localized string similar to Job completion maximum wait time reached..
+ ///
+ internal static string JobCompletionMaxWaitReached {
+ get {
+ return ResourceManager.GetString("JobCompletionMaxWaitReached", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The Job having Id: {0} was not found..
///
@@ -357,6 +375,15 @@ internal static string JobNotFound {
}
}
+ ///
+ /// Looks up a localized string similar to "Job progress state : Id {0}, state {1}, time {2}".
+ ///
+ internal static string JobProgressState {
+ get {
+ return ResourceManager.GetString("JobProgressState", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The job schedule was not found. Runbook name {0}. Schedule name {1}..
///
@@ -375,6 +402,15 @@ internal static string JobScheduleWithIdNotFound {
}
}
+ ///
+ /// Looks up a localized string similar to "Job terminal state : Id {0}, state {1}, time {2}".
+ ///
+ internal static string JobTerminalState {
+ get {
+ return ResourceManager.GetString("JobTerminalState", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Metaconfig already exists. Specify the parameter to force an overwrite. File: {0}.
///
@@ -555,6 +591,15 @@ internal static string RemoveDscNodeWarning {
}
}
+ ///
+ /// Looks up a localized string similar to Are you sure you want to remove the Azure Automation {0}? Note: Any DSC node configurations under this DSC configuration will not be removed..
+ ///
+ internal static string RemovingAzureAutomationDscConfigurationWarning {
+ get {
+ return ResourceManager.GetString("RemovingAzureAutomationDscConfigurationWarning", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Are you sure you want to remove the Azure Automation {0} ?.
///
diff --git a/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.resx b/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.resx
index 999997efde7f..176debb5b9aa 100644
--- a/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.resx
+++ b/src/ResourceManager/Automation/Commands.Automation/Properties/Resources.resx
@@ -173,6 +173,10 @@
Are you sure you want to remove the Azure Automation {0} ?
Automation
+
+ Are you sure you want to remove the Azure Automation {0}? Note: Any DSC node configurations under this DSC configuration will not be removed.
+ Automation
+
Resource exist.
Automation
@@ -384,6 +388,10 @@
The Dsc Configuration was not found. Configuration name {0}.
Automation
+
+ The node configuration '{0}' is currently assigned to one or more nodes. Either specify the IgnoreNodeMappings parameter, or reassign these nodes to a different node configuration, to delete this node configuration.
+ Automation
+
The Webhook with Name: {0} was not found.
Automation
@@ -421,4 +429,14 @@
ConfigurationData cannot be part of the job parameters. You can specify the ConfigurationData using the {0} switch
+
+ Job completion maximum wait time reached.
+ Automation
+
+
+ "Job progress state : Id {0}, state {1}, time {2}"
+
+
+ "Job terminal state : Id {0}, state {1}, time {2}"
+
\ No newline at end of file
diff --git a/src/ResourceManager/Automation/Commands.Automation/packages.config b/src/ResourceManager/Automation/Commands.Automation/packages.config
index f30921fc01ab..4e8ace659683 100644
--- a/src/ResourceManager/Automation/Commands.Automation/packages.config
+++ b/src/ResourceManager/Automation/Commands.Automation/packages.config
@@ -4,7 +4,7 @@
-
+