Skip to content

Commit 024d42f

Browse files
committed
2 parents a8d71d9 + 363e815 commit 024d42f

13 files changed

+248
-53
lines changed
Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
// ----------------------------------------------------------------------------------
2-
//
3-
// Copyright Microsoft Corporation
4-
// Licensed under the Apache License, Version 2.0 (the "License");
5-
// you may not use this file except in compliance with the License.
6-
// You may obtain a copy of the License at
7-
// http://www.apache.org/licenses/LICENSE-2.0
8-
// Unless required by applicable law or agreed to in writing, software
9-
// distributed under the License is distributed on an "AS IS" BASIS,
10-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11-
// See the License for the specific language governing permissions and
12-
// limitations under the License.
13-
// ----------------------------------------------------------------------------------
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
1414

1515
using System;
1616
using System.Management.Automation;
@@ -19,35 +19,35 @@
1919

2020
namespace Microsoft.Azure.Commands.Automation.Cmdlet
2121
{
22-
/// <summary>
23-
/// Gets azure automation job stream record for a given job.
24-
/// </summary>
22+
/// <summary>
23+
/// Gets azure automation job stream record for a given job.
24+
/// </summary>
2525
[Cmdlet(VerbsCommon.Get, "AzureRmAutomationJobOutputRecord")]
2626
[OutputType(typeof(JobStreamRecord))]
2727
public class GetAzureAutomationJobOutputRecord : AzureAutomationBaseCmdlet
2828
{
29-
/// <summary>
30-
/// Gets or sets the job id
31-
/// </summary>
29+
/// <summary>
30+
/// Gets or sets the job id
31+
/// </summary>
3232
[Parameter(Mandatory = true, Position = 2, ValueFromPipelineByPropertyName = true, HelpMessage = "The job Id")]
3333
public Guid JobId { get; set; }
3434

35-
/// <summary>
36-
/// Gets or sets the job stream record id
37-
/// </summary>
35+
/// <summary>
36+
/// Gets or sets the job stream record id
37+
/// </summary>
3838
[Alias("StreamRecordId")]
3939
[Parameter(Mandatory = true, Position = 3, ValueFromPipelineByPropertyName = true, HelpMessage = "The stream record id")]
4040
[ValidateNotNullOrEmpty]
4141
public string Id { get; set; }
42-
43-
/// <summary>
44-
/// Execute this cmdlet.
45-
/// </summary>
42+
43+
/// <summary>
44+
/// Execute this cmdlet.
45+
/// </summary>
4646
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
4747
protected override void AutomationProcessRecord()
4848
{
4949
var ret = this.AutomationClient.GetJobStreamRecord(this.ResourceGroupName, this.AutomationAccountName, this.JobId, this.Id);
5050
this.GenerateCmdletOutput(ret);
5151
}
5252
}
53-
}
53+
}

src/ResourceManager/Automation/Commands.Automation/Cmdlet/NewAzureAutomationWebhook.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class NewAzureAutomationWebhook : AzureAutomationBaseCmdlet
6464
/// <summary>
6565
/// Gets or sets the Runbook parameters
6666
/// </summary>
67-
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true,
67+
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false,
6868
HelpMessage = "The Runbook parameters name/value.")]
6969
public IDictionary Parameters { get; set; }
7070

src/ResourceManager/Automation/Commands.Automation/Cmdlet/RegisterAzureAutomationScheduledRunbook.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public class RegisterAzureAutomationScheduledRunbook : AzureAutomationBaseCmdlet
4747
/// <summary>
4848
/// Gets or sets the runbook parameters.
4949
/// </summary>
50-
[Parameter(ParameterSetName = AutomationCmdletParameterSets.ByRunbookNameAndScheduleName, Mandatory = false, ValueFromPipelineByPropertyName = true,
50+
[Parameter(ParameterSetName = AutomationCmdletParameterSets.ByRunbookNameAndScheduleName, Mandatory = false, ValueFromPipelineByPropertyName = false,
5151
HelpMessage = "The runbook parameters.")]
5252
public IDictionary Parameters { get; set; }
5353

src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationDscCompilationJob.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class StartAzureAutomationDscCompilationJob : AzureAutomationBaseCmdlet
4242
/// <summary>
4343
/// Gets or sets the configuration parameters.
4444
/// </summary>
45-
[Parameter(Mandatory = false, HelpMessage = "The compilation job parameters.")]
45+
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = false, HelpMessage = "The compilation job parameters.")]
4646
public IDictionary Parameters { get; set; }
4747

4848
/// <summary>

src/ResourceManager/Automation/Commands.Automation/Cmdlet/StartAzureAutomationRunbook.cs

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,55 +12,156 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15+
using System;
1516
using System.Collections;
1617
using System.Collections.Generic;
18+
using System.Collections.ObjectModel;
19+
using System.Globalization;
20+
using System.Linq;
1721
using System.Management.Automation;
22+
using System.Management.Automation.Runspaces;
1823
using System.Security.Permissions;
24+
using System.Threading;
1925
using Microsoft.Azure.Commands.Automation.Common;
26+
using Microsoft.Azure.Commands.Automation.Model;
2027
using Job = Microsoft.Azure.Commands.Automation.Model.Job;
28+
using JobStream = Microsoft.Azure.Commands.Automation.Model.JobStream;
29+
using Microsoft.Azure.Commands.Automation.Properties;
2130

2231

2332
namespace Microsoft.Azure.Commands.Automation.Cmdlet
2433
{
2534
/// <summary>
2635
/// Starts an Azure automation runbook.
2736
/// </summary>
28-
[Cmdlet(VerbsLifecycle.Start, "AzureRmAutomationRunbook", DefaultParameterSetName = AutomationCmdletParameterSets.ByRunbookName)]
29-
[OutputType(typeof(Job))]
37+
[Cmdlet(VerbsLifecycle.Start, "AzureRmAutomationRunbook", DefaultParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob)]
38+
[OutputType(typeof(Job), ParameterSetName = new []{ AutomationCmdletParameterSets.ByAsynchronousReturnJob })]
39+
[OutputType(typeof(PSObject), ParameterSetName = new []{ AutomationCmdletParameterSets.BySynchronousReturnJobOutput })]
3040
public class StartAzureAutomationRunbook : AzureAutomationBaseCmdlet
3141
{
42+
/// <summary>
43+
/// True to wait for job output
44+
/// </summary>
45+
private bool wait;
46+
47+
/// <summary>
48+
/// Timeout value
49+
/// </summary>
50+
private int timeout = Constants.MaxWaitSeconds;
51+
52+
/// <summary>
53+
/// Polling interval
54+
/// </summary>
55+
private const int pollingIntervalInSeconds = 5;
56+
3257
/// <summary>
3358
/// Gets or sets the runbook name
3459
/// </summary>
35-
[Parameter(ParameterSetName = AutomationCmdletParameterSets.ByRunbookName, Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook name.")]
60+
[Parameter(ParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob, Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook name.")]
61+
[Parameter(ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook name.")]
3662
[ValidateNotNullOrEmpty]
3763
[Alias("RunbookName")]
3864
public string Name { get; set; }
3965

4066
/// <summary>
4167
/// Gets or sets the runbook parameters.
4268
/// </summary>
43-
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "The runbook parameters.")]
69+
[Parameter(ParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob, Mandatory = false, HelpMessage = "The runbook parameters.")]
70+
[Parameter(ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, Mandatory = false, HelpMessage = "The runbook parameters.")]
4471
public IDictionary Parameters { get; set; }
4572

4673
/// <summary>
4774
/// Gets or sets the optional hybrid agent friendly name upon which the runbook should be executed.
4875
/// </summary>
49-
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "Optional name of the hybrid agent which should execute the runbook")]
76+
[Parameter(ParameterSetName = AutomationCmdletParameterSets.ByAsynchronousReturnJob, Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "Optional name of the hybrid agent which should execute the runbook")]
77+
[Parameter(ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "Optional name of the hybrid agent which should execute the runbook")]
5078
[Alias("HybridWorker")]
5179
public string RunOn { get; set; }
5280

81+
82+
/// <summary>
83+
/// Gets or sets the switch parameter to wait for job output
84+
/// </summary>
85+
[Parameter(Mandatory = false, ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, HelpMessage = "Wait for job to complete, suspend, or fail.")]
86+
public SwitchParameter Wait
87+
{
88+
get { return this.wait; }
89+
set { this.wait = value; }
90+
}
91+
92+
/// <summary>
93+
/// Gets or sets the switch parameter to wait for job output
94+
/// </summary>
95+
[Parameter(Mandatory = false, ParameterSetName = AutomationCmdletParameterSets.BySynchronousReturnJobOutput, HelpMessage = "Maximum time in seconds to wait for job completion. Default max wait time is 10800 seconds.")]
96+
public int MaxWaitSeconds
97+
{
98+
get { return this.timeout; }
99+
set { this.timeout = value; }
100+
}
101+
53102
/// <summary>
54103
/// Execute this cmdlet.
55104
/// </summary>
56105
[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
57106
protected override void AutomationProcessRecord()
58107
{
59-
Job job = null;
108+
var job = this.AutomationClient.StartRunbook(this.ResourceGroupName, this.AutomationAccountName, this.Name, this.Parameters, this.RunOn);
109+
110+
// if no wait, return job object
111+
if (!this.Wait.IsPresent)
112+
{
113+
this.WriteObject(job);
114+
return;
115+
}
116+
117+
// wait for job completion
118+
if (!PollJobCompletion(job.JobId))
119+
{
120+
throw new ResourceCommonException(typeof(Job), string.Format(CultureInfo.CurrentCulture, Resources.JobCompletionMaxWaitReached));
121+
}
122+
123+
// retrieve job streams
124+
var nextLink = string.Empty;
125+
do
126+
{
127+
var jobOutputList = this.AutomationClient.GetJobStream(this.ResourceGroupName, this.AutomationAccountName, job.JobId, null, StreamType.Output.ToString(), ref nextLink);
128+
foreach (var jobOutputRecord in jobOutputList)
129+
{
130+
var response = this.AutomationClient.GetJobStreamRecordAsPsObject(this.ResourceGroupName, this.AutomationAccountName, job.JobId, jobOutputRecord.StreamRecordId);
131+
this.GenerateCmdletOutput(response);
132+
}
133+
} while (!string.IsNullOrEmpty(nextLink));
134+
}
135+
136+
private bool PollJobCompletion(Guid jobId)
137+
{
138+
var timeoutIncrement = 0;
139+
do
140+
{
141+
Thread.Sleep(TimeSpan.FromSeconds(pollingIntervalInSeconds));
142+
timeoutIncrement += pollingIntervalInSeconds;
143+
144+
var job = this.AutomationClient.GetJob(this.ResourceGroupName, this.AutomationAccountName, jobId);
145+
if (!IsJobTerminalState(job))
146+
{
147+
if (IsVerbose()) WriteVerbose(string.Format(Resources.JobProgressState, job.JobId, job.Status, DateTime.Now));
148+
}
149+
else
150+
{
151+
if (IsVerbose()) WriteVerbose(string.Format(Resources.JobTerminalState, job.JobId, job.Status, DateTime.Now));
152+
return true;
153+
}
154+
} while (this.MaxWaitSeconds < 0 || timeoutIncrement <= this.MaxWaitSeconds);
60155

61-
job = this.AutomationClient.StartRunbook(this.ResourceGroupName, this.AutomationAccountName, this.Name, this.Parameters, this.RunOn);
156+
return false;
157+
}
62158

63-
this.WriteObject(job);
159+
private static bool IsJobTerminalState(Job job)
160+
{
161+
return job.Status.Equals(JobState.Completed.ToString(), StringComparison.OrdinalIgnoreCase) ||
162+
job.Status.Equals(JobState.Failed.ToString(), StringComparison.OrdinalIgnoreCase) ||
163+
job.Status.Equals(JobState.Stopped.ToString(), StringComparison.OrdinalIgnoreCase) ||
164+
job.Status.Equals(JobState.Suspended.ToString(), StringComparison.OrdinalIgnoreCase);
64165
}
65166
}
66167
}

src/ResourceManager/Automation/Commands.Automation/Common/AutomationClient.cs

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,20 @@
1818
using System.Globalization;
1919
using System.Linq;
2020
using System.IO;
21+
using System.Management.Automation;
2122
using System.Net;
2223
using System.Security;
2324
using System.Security.Cryptography.X509Certificates;
24-
using Microsoft.Azure.Commands.Automation.Cmdlet;
2525
using Microsoft.Azure.Commands.Automation.Model;
2626
using Microsoft.Azure.Commands.Automation.Properties;
2727
using Microsoft.Azure.Management.Automation;
2828
using Microsoft.Azure.Management.Automation.Models;
2929
using Microsoft.Azure.Common.Authentication.Models;
3030
using Newtonsoft.Json;
31+
using Microsoft.Azure.Common.Authentication;
32+
using Hyak.Common;
3133

34+
using AutomationManagement = Microsoft.Azure.Management.Automation;
3235
using AutomationAccount = Microsoft.Azure.Commands.Automation.Model.AutomationAccount;
3336
using Module = Microsoft.Azure.Commands.Automation.Model.Module;
3437
using Runbook = Microsoft.Azure.Commands.Automation.Model.Runbook;
@@ -43,11 +46,6 @@
4346

4447
namespace Microsoft.Azure.Commands.Automation.Common
4548
{
46-
using AutomationManagement = Azure.Management.Automation;
47-
using Microsoft.Azure.Common.Authentication;
48-
using Hyak.Common;
49-
50-
5149
public partial class AutomationClient : IAutomationClient
5250
{
5351
private readonly AutomationManagement.IAutomationManagementClient automationManagementClient;
@@ -996,6 +994,47 @@ public JobStreamRecord GetJobStreamRecord(string resourceGroupName, string autom
996994
return new JobStreamRecord(response.JobStream, resourceGroupName, automationAccountName, jobId);
997995
}
998996

997+
public object GetJobStreamRecordAsPsObject(string resourceGroupName, string automationAccountName, Guid jobId, string jobStreamId)
998+
{
999+
var response = this.automationManagementClient.JobStreams.Get(resourceGroupName, automationAccountName, jobId, jobStreamId);
1000+
1001+
if (response.JobStream.Properties == null || response.JobStream.Properties.Value == null) return null;
1002+
1003+
// PowerShell Workflow runbook jobs would have the below additional properties, remove them from job output
1004+
// we do not know the runbook type, remove will only remove if exists
1005+
response.JobStream.Properties.Value.Remove("PSComputerName");
1006+
response.JobStream.Properties.Value.Remove("PSShowComputerName");
1007+
response.JobStream.Properties.Value.Remove("PSSourceJobInstanceId");
1008+
1009+
var paramTable = new Hashtable();
1010+
1011+
foreach (var kvp in response.JobStream.Properties.Value)
1012+
{
1013+
object paramValue;
1014+
try
1015+
{
1016+
paramValue = ((object)PowerShellJsonConverter.Deserialize(kvp.Value.ToString()));
1017+
}
1018+
catch (CmdletInvocationException exception)
1019+
{
1020+
if (!exception.Message.Contains("Invalid JSON primitive"))
1021+
throw;
1022+
1023+
paramValue = kvp.Value;
1024+
}
1025+
1026+
// for primitive outputs, the record will be in form "value" : "primitive type value". Return the key and return the primitive type value
1027+
if (response.JobStream.Properties.Value.Count == 1 && response.JobStream.Properties.Value.ContainsKey("value"))
1028+
{
1029+
return paramValue;
1030+
}
1031+
1032+
paramTable.Add(kvp.Key, paramValue);
1033+
}
1034+
1035+
return paramTable;
1036+
}
1037+
9991038
public Job GetJob(string resourceGroupName, string automationAccountName, Guid Id)
10001039
{
10011040
var job = this.automationManagementClient.Jobs.Get(resourceGroupName, automationAccountName, Id).Job;
@@ -1637,15 +1676,6 @@ private IDictionary<string, string> ProcessRunbookParameters(string resourceGrou
16371676
string.Format(CultureInfo.CurrentCulture, Resources.InvalidRunbookParameters));
16381677
}
16391678

1640-
var hasJobStartedBy =
1641-
filteredParameters.Any(filteredParameter => filteredParameter.Key == Constants.JobStartedByParameterName);
1642-
1643-
if (!hasJobStartedBy)
1644-
{
1645-
filteredParameters.Add(Constants.JobStartedByParameterName,
1646-
PowerShellJsonConverter.Serialize(Constants.ClientIdentity));
1647-
}
1648-
16491679
return filteredParameters;
16501680
}
16511681

src/ResourceManager/Automation/Commands.Automation/Common/AutomationCmdletParameterSet.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ internal static class AutomationCmdletParameterSets
7979
/// </summary>
8080
internal const string ByRunbookName = "ByRunbookName";
8181

82+
/// <summary>
83+
/// The ByAsynchronousReturnJob.
84+
/// </summary>
85+
internal const string ByAsynchronousReturnJob = "ByAsynchronousReturnJob";
86+
87+
/// <summary>
88+
/// The BySynchronousReturnJob.
89+
/// </summary>
90+
internal const string BySynchronousReturnJobOutput = "BySynchronousReturnJobOutput";
91+
8292
/// <summary>
8393
/// The Configuration name parameter set.
8494
/// </summary>

src/ResourceManager/Automation/Commands.Automation/Common/Constants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ public class AutomationAccountState
6363

6464
public const int JobSummaryLength = 80;
6565

66+
public const int MaxWaitSeconds = 10800;
67+
6668
// The template file is a json
6769
public const string TemplateFile = @"https://eus2oaasibizamarketprod1.blob.core.windows.net/automationdscpreview/azuredeployV2.json";
6870

0 commit comments

Comments
 (0)