/* This is the only way I can see to "check out" a runbook (get it from Published to Edit state) using the SDK. */ public static async Task CheckOutRunbook(AutomationRunbook runbook, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account) { CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); RunbookGetResponse response = await automationManagementClient.Runbooks.GetAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); if (response.Runbook.Properties.State != "Published") { return; } cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); RunbookContentResponse runbookContentResponse = await automationManagementClient.Runbooks.ContentAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); // Create draft properties RunbookCreateOrUpdateDraftParameters draftParams = new RunbookCreateOrUpdateDraftParameters(); draftParams.Properties = new RunbookCreateOrUpdateDraftProperties(); draftParams.Properties.Description = response.Runbook.Properties.Description; draftParams.Properties.LogProgress = response.Runbook.Properties.LogProgress; draftParams.Properties.LogVerbose = response.Runbook.Properties.LogVerbose; draftParams.Properties.RunbookType = response.Runbook.Properties.RunbookType; draftParams.Properties.Draft = new RunbookDraft(); draftParams.Tags = response.Runbook.Tags; draftParams.Name = runbook.Name; draftParams.Location = account.Location; cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); await automationManagementClient.Runbooks.CreateOrUpdateWithDraftAsync(resourceGroupName, account.Name, draftParams, cts.Token); RunbookDraftUpdateParameters draftUpdateParams = new RunbookDraftUpdateParameters() { Name = runbook.Name, Stream = runbookContentResponse.Stream.ToString() }; cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); await automationManagementClient.RunbookDraft.UpdateAsync(resourceGroupName, account.Name, draftUpdateParams, cts.Token); /* Ensure the correct sync status is detected */ if (runbook.localFileInfo != null) { RunbookDraft draft = await GetRunbookDraft(runbook.Name, automationManagementClient, resourceGroupName, account.Name); runbook.localFileInfo.LastWriteTime = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedLocal = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedCloud = draft.LastModifiedTime.LocalDateTime; } }
public static async Task DownloadRunbook(AutomationRunbook runbook, AutomationManagementClient automationManagementClient, string workspace, string resourceGroupName, AutomationAccount account) { CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); RunbookGetResponse response = await automationManagementClient.Runbooks.GetAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); RunbookDraftGetResponse draftResponse = null; RunbookContentResponse runbookContentResponse = null; cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); if (response.Runbook.Properties.State == "Published") { runbookContentResponse = await automationManagementClient.Runbooks.ContentAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); } else { runbookContentResponse = await automationManagementClient.RunbookDraft.ContentAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); draftResponse = await automationManagementClient.RunbookDraft.GetAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); } String runbookFilePath = System.IO.Path.Combine(workspace, runbook.Name + ".ps1"); File.WriteAllText(runbookFilePath, runbookContentResponse.Stream.ToString(), Encoding.UTF8); runbook.AuthoringState = AutomationRunbook.AuthoringStates.InEdit; runbook.localFileInfo = new FileInfo(runbookFilePath); /* This is the only way I can see to "check out" the runbook using the SDK. * Hopefully there's a better way but for now this works */ if (response.Runbook.Properties.State == "Published") { await UploadRunbookAsDraft(runbook, automationManagementClient, resourceGroupName, account); cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); draftResponse = await automationManagementClient.RunbookDraft.GetAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); } /* Ensures the correct sync status is detected */ if (draftResponse != null) { runbook.localFileInfo.LastWriteTime = draftResponse.RunbookDraft.LastModifiedTime.LocalDateTime; runbook.LastModifiedLocal = draftResponse.RunbookDraft.LastModifiedTime.LocalDateTime; runbook.LastModifiedCloud = draftResponse.RunbookDraft.LastModifiedTime.LocalDateTime; } }
public static async Task UploadRunbookAsDraft(AutomationRunbook runbook, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account) { RunbookCreateOrUpdateDraftProperties draftProperties; // Parse the script to determine if it is a PS workflow or native script String PSScriptText = File.ReadAllText(runbook.localFileInfo.FullName); System.Management.Automation.Language.Token[] AST; System.Management.Automation.Language.ParseError[] ASTError = null; var ASTScript = System.Management.Automation.Language.Parser.ParseInput(PSScriptText, out AST, out ASTError); // If the script starts with workflow, then create a PS Workflow script runbook or else create a native PS script runbook if (ASTScript.EndBlock.Extent.Text.ToLower().StartsWith("workflow")) { draftProperties = new RunbookCreateOrUpdateDraftProperties(Constants.RunbookType.Workflow, new RunbookDraft()); } else { draftProperties = new RunbookCreateOrUpdateDraftProperties(Constants.RunbookType.PowerShellScript, new RunbookDraft()); } // Get current properties if is not a new runbook and set these on the draft also so they are preserved. RunbookGetResponse response = null; CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); if (runbook.SyncStatus != AutomationAuthoringItem.Constants.SyncStatus.LocalOnly) { response = await automationManagementClient.Runbooks.GetAsync(resourceGroupName, account.Name, runbook.Name, cts.Token); draftProperties.Description = response.Runbook.Properties.Description; draftProperties.RunbookType = response.Runbook.Properties.RunbookType; } // Create draft properties RunbookCreateOrUpdateDraftParameters draftParams = new RunbookCreateOrUpdateDraftParameters(draftProperties); draftParams.Name = runbook.Name; draftParams.Location = account.Location; // If this is not a new runbook, set the existing properties of the runbook if (response != null) { draftParams.Tags = response.Runbook.Tags; draftParams.Properties.LogProgress = response.Runbook.Properties.LogProgress; draftParams.Properties.LogVerbose = response.Runbook.Properties.LogVerbose; } cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); await automationManagementClient.Runbooks.CreateOrUpdateWithDraftAsync(resourceGroupName, account.Name, draftParams, cts.Token); /* Update the runbook content from .ps1 file */ RunbookDraftUpdateParameters draftUpdateParams = new RunbookDraftUpdateParameters() { Name = runbook.Name, Stream = PSScriptText }; cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); await automationManagementClient.RunbookDraft.UpdateAsync(resourceGroupName, account.Name, draftUpdateParams, cts.Token); /* Ensure the correct sync status is detected */ RunbookDraft draft = await GetRunbookDraft(runbook.Name, automationManagementClient, resourceGroupName, account.Name); runbook.localFileInfo.LastWriteTime = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedLocal = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedCloud = draft.LastModifiedTime.LocalDateTime; }
public static async Task <Boolean> CreateStorageAccount(String authority, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account, string storageResourceGroup, string storageSubID, string storageAccount, string storageRGLocation) { try { // Get the token for the tenant on this subscription. var cloudtoken = AuthenticateHelper.RefreshTokenByAuthority(authority, Properties.Settings.Default.appIdURI); var subscriptionCreds = new TokenCloudCredentials(storageSubID, cloudtoken.AccessToken); var resourceManagementClient = new ResourceManagementClient(subscriptionCreds, new Uri(Properties.Settings.Default.appIdURI)); // Check if the resource group exists, otherwise create it. var rgExists = resourceManagementClient.ResourceGroups.CheckExistence(storageResourceGroup); if (!(rgExists.Exists)) { var resourceGroup = new ResourceGroup { Location = storageRGLocation }; await resourceManagementClient.ResourceGroups.CreateOrUpdateAsync(storageResourceGroup, resourceGroup); } // Create storage client and set subscription to work against var token = new Microsoft.Rest.TokenCredentials(cloudtoken.AccessToken); var storageManagementClient = new Microsoft.Azure.Management.Storage.StorageManagementClient(new Uri(Properties.Settings.Default.appIdURI), token); storageManagementClient.SubscriptionId = storageSubID; // Use Standard local replication as the sku since it is not critical to keep these modules replicated var storageParams = new StorageAccountCreateParameters() { Location = storageRGLocation, Kind = Kind.Storage, Sku = new Microsoft.Azure.Management.Storage.Models.Sku(SkuName.StandardLRS) }; // Create storage account CancellationToken cancelToken = new CancellationToken(); await storageManagementClient.StorageAccounts.CreateAsync(storageResourceGroup, storageAccount, storageParams, cancelToken); } catch (Exception Ex) { throw Ex; } return(true); }
public static async Task UploadModule(AuthenticationResult auth, AutomationModule module, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account, string storageResourceGroup, string storageSubID, string storageAccount) { // Update the module from powershell gallery if it exists, otherwise upload to storage if (!(await UploadFromGalleryIfExists(module.Name, module.localVersion, automationManagementClient, resourceGroupName, account))) { // Create storage client and set subscription to work against var token = new Microsoft.Rest.TokenCredentials(auth.AccessToken); var storageManagementClient = new Microsoft.Azure.Management.Storage.StorageManagementClient(new Uri(Properties.Settings.Default.appIdURI), token); storageManagementClient.SubscriptionId = storageSubID; // Get storage key and set up connection to storage account var storageKeys = storageManagementClient.StorageAccounts.ListKeys(storageResourceGroup, storageAccount); var storageKey = storageKeys.Keys.FirstOrDefault().Value; var storageConnection = "DefaultEndpointsProtocol=https;AccountName=" + storageAccount + ";AccountKey=" + storageKey; CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(storageConnection); // Zip and upload module to storage account var zipModule = ZipModuleFolder(module.localModulePath, module.Name); var blobClient = cloudStorageAccount.CreateCloudBlobClient(); await UploadModuleToStorageAccount(blobClient, Constants.moduleContainer, zipModule, module.Name.ToLower()); // Get sas token and upload to automation account var SaSURI = await GetSASToken(blobClient, Constants.moduleContainer, module.Name.ToLower()); await UploadModuleToAutomationAccount(module.Name, automationManagementClient, resourceGroupName, account, SaSURI); } }
private static async Task <Boolean> UploadFromGalleryIfExists(String moduleName, String version, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account) { // Get dependent modules so these can be imported first var dependencies = PowerShellGallery.GetGalleryModuleDependencies(moduleName, version); // If there are depdendent modules, recursively call this function until there are no dependencies. // This could get into an infinite loop if modules are uploaded that have a dependency on each other which // should never happen. Might want to catch this error in the future. if (dependencies.Count > 0) { foreach (var dependentModule in dependencies) { await UploadFromGalleryIfExists(dependentModule.moduleName, dependentModule.moduleVersion, automationManagementClient, resourceGroupName, account); } } // Get the gallery location of the module and import to automation account var galleryURI = PowerShellGallery.GetGalleryModuleUri(moduleName, version); if (galleryURI != null) { // If the module already exists in the automation account with the same version, skip var existingModule = await CheckModuleExists(moduleName, automationManagementClient, resourceGroupName, account.Name, version); if (!existingModule) { await UploadModuleToAutomationAccount(moduleName, automationManagementClient, resourceGroupName, account, galleryURI); } return(true); } return(false); }
private static async Task UploadModuleToAutomationAccount(String moduleName, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account, String URIString) { ModuleCreateOrUpdateParameters draftUpdateParams = new ModuleCreateOrUpdateParameters() { Name = moduleName, Location = account.Location, Properties = new ModuleCreateOrUpdateProperties() { ContentLink = new Microsoft.Azure.Management.Automation.Models.ContentLink() { Uri = new Uri(URIString) } } }; var cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); await automationManagementClient.Modules.CreateOrUpdateAsync(resourceGroupName, account.Name, draftUpdateParams, cts.Token); Module module = await GetModule(moduleName, automationManagementClient, resourceGroupName, account.Name); // Wait for upload to complete while (module.Properties.ProvisioningState != ModuleProvisioningState.Failed && module.Properties.ProvisioningState != ModuleProvisioningState.Succeeded) { await Task.Delay(10000); module = await GetModule(moduleName, automationManagementClient, resourceGroupName, account.Name); } // Check if there is an error. if (!(String.IsNullOrEmpty(module.Properties.Error.Message))) { throw new Exception(module.Properties.Error.Message.ToString()); } }
public static async Task DownloadConfiguration(AutomationDSC configuration, AutomationManagementClient automationManagementClient, string workspace, string resourceGroupName, AutomationAccount account) { CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); DscConfigurationGetResponse response = await automationManagementClient.Configurations.GetAsync(resourceGroupName, account.Name, configuration.Name, cts.Token); DscConfigurationGetResponse draftResponse = null; DscConfigurationGetContentResponse configurationContentResponse = null; cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); if (response.Configuration.Properties.State == "Published") { configurationContentResponse = await automationManagementClient.Configurations.GetContentAsync(resourceGroupName, account.Name, configuration.Name, cts.Token); } else { // Draft not supported yet } String configFilePath = System.IO.Path.Combine(workspace, configuration.Name + ".ps1"); File.WriteAllText(configFilePath, configurationContentResponse.Content.ToString(), Encoding.UTF8); configuration.localFileInfo = new FileInfo(configFilePath); if (response.Configuration.Properties.State == "Published") { await UploadConfigurationAsDraft(configuration, automationManagementClient, resourceGroupName, account); cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); draftResponse = await automationManagementClient.Configurations.GetAsync(resourceGroupName, account.Name, configuration.Name, cts.Token); } /* Ensures the correct sync status is detected */ if (draftResponse != null) { configuration.localFileInfo.LastWriteTime = draftResponse.Configuration.Properties.LastModifiedTime.LocalDateTime; configuration.LastModifiedLocal = draftResponse.Configuration.Properties.LastModifiedTime.LocalDateTime; configuration.LastModifiedCloud = draftResponse.Configuration.Properties.LastModifiedTime.LocalDateTime; } }
public static async Task UploadConfigurationAsDraft(AutomationDSC configuration, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account) { string fileContent = null; try { if (File.Exists(Path.GetFullPath(configuration.localFileInfo.FullName))) { fileContent = System.IO.File.ReadAllText(configuration.localFileInfo.FullName); } } catch (Exception) { // exception in accessing the file path throw new FileNotFoundException( string.Format( CultureInfo.CurrentCulture, Resources.LocalConfigurationFileNotFound)); } DscConfigurationCreateOrUpdateProperties draftProperties; draftProperties = new DscConfigurationCreateOrUpdateProperties(); // Get current properties if is not a new configuration and set these on the draft also so they are preserved. DscConfigurationGetResponse response = null; CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); if (configuration.SyncStatus != AutomationAuthoringItem.Constants.SyncStatus.LocalOnly) { response = await automationManagementClient.Configurations.GetAsync(resourceGroupName, account.Name, configuration.Name, cts.Token); } // Create properties DscConfigurationCreateOrUpdateParameters draftParams = new DscConfigurationCreateOrUpdateParameters(draftProperties); draftParams.Name = configuration.Name; draftParams.Location = account.Location; draftParams.Properties.Description = configuration.Description; // If this is not a new configuration, set the existing properties of the configuration if (response != null) { draftParams.Tags = response.Configuration.Tags; draftParams.Location = response.Configuration.Location; draftParams.Properties.LogVerbose = response.Configuration.Properties.LogVerbose; draftParams.Properties.Description = response.Configuration.Properties.Description; } cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); /* Update the configuration content from .ps1 file */ DscConfigurationCreateOrUpdateParameters draftUpdateParams = new DscConfigurationCreateOrUpdateParameters() { Name = configuration.Name, Location = draftParams.Location, Tags = draftParams.Tags, Properties = new DscConfigurationCreateOrUpdateProperties() { Description = draftParams.Properties.Description, LogVerbose = draftParams.Properties.LogVerbose, Source = new Microsoft.Azure.Management.Automation.Models.ContentSource() { ContentType = ContentSourceType.EmbeddedContent, Value = fileContent } } }; cts = new CancellationTokenSource(); cts.CancelAfter(TIMEOUT_MS); await automationManagementClient.Configurations.CreateOrUpdateAsync(resourceGroupName, account.Name, draftUpdateParams, cts.Token); /* Ensure the correct sync status is detected */ DscConfiguration draft = await GetConfigurationDraft(configuration.Name, automationManagementClient, resourceGroupName, account.Name); configuration.localFileInfo.LastWriteTime = draft.Properties.LastModifiedTime.LocalDateTime; configuration.LastModifiedLocal = draft.Properties.LastModifiedTime.LocalDateTime; configuration.LastModifiedCloud = draft.Properties.LastModifiedTime.LocalDateTime; }
public static async Task UploadRunbookAsDraft(AutomationRunbook runbook, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account) { RunbookCreateOrUpdateDraftProperties draftProperties = new RunbookCreateOrUpdateDraftProperties("Script", new RunbookDraft()); draftProperties.Description = runbook.Description; RunbookCreateOrUpdateDraftParameters draftParams = new RunbookCreateOrUpdateDraftParameters(draftProperties); draftParams.Name = runbook.Name; draftParams.Location = account.Location; await automationManagementClient.Runbooks.CreateOrUpdateWithDraftAsync(resourceGroupName, account.Name, draftParams); /* Update the runbook content from .ps1 file */ RunbookDraftUpdateParameters draftUpdateParams = new RunbookDraftUpdateParameters() { Name = runbook.Name, Stream = File.ReadAllText(runbook.localFileInfo.FullName) }; await automationManagementClient.RunbookDraft.UpdateAsync(resourceGroupName, account.Name, draftUpdateParams); /* Ensure the correct sync status is detected */ RunbookDraft draft = await GetRunbookDraft(runbook.Name, automationManagementClient, resourceGroupName, account.Name); runbook.localFileInfo.LastWriteTime = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedLocal = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedCloud = draft.LastModifiedTime.LocalDateTime; }
private async void accountsComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { try { AutomationAccount account = (AutomationAccount)accountsComboBox.SelectedValue; iseClient.currAccount = account; refreshTimer.Stop(); if (account != null) { /* Update Status */ UpdateStatusBox(configurationStatusTextBox, "Selected automation account: " + account.Name); if (iseClient.AccountWorkspaceExists()) { accountPathTextBox.Text = iseClient.currWorkspace; } /* Update Runbooks */ UpdateStatusBox(configurationStatusTextBox, "Getting runbook data..."); if (runbookListViewModel != null) { runbookListViewModel.Clear(); } runbookListViewModel = new ObservableCollection <AutomationRunbook>(await AutomationRunbookManager.GetAllRunbookMetadata(iseClient.automationManagementClient, iseClient.currWorkspace, iseClient.accountResourceGroups[iseClient.currAccount].Name, iseClient.currAccount.Name)); UpdateStatusBox(configurationStatusTextBox, "Done getting runbook data"); /* Update Assets */ //TODO: this is not quite checking what we need it to check if (!iseClient.AccountWorkspaceExists()) { UpdateStatusBox(configurationStatusTextBox, "Downloading assets..."); await downloadAllAssets(); UpdateStatusBox(configurationStatusTextBox, "Assets downloaded"); } /* Update PowerShell Module */ try { PSModuleConfiguration.UpdateModuleConfiguration(iseClient.currWorkspace); } catch { string message = "Could not configure the " + PSModuleConfiguration.ModuleData.ModuleName + " module.\r\n"; message += "This module is required for your runbooks to run locally.\r\n"; message += "Make sure it exists in your module path (env:PSModulePath)."; MessageBox.Show(message); } /* Update UI */ RunbooksListView.ItemsSource = runbookListViewModel; setRunbookSelectionButtonState(false); setRunbookAndAssetNonSelectionButtonState(true); assetsComboBox.SelectedValue = AutomationISE.Model.Constants.assetVariable; ButtonRefreshAssetList.IsEnabled = true; /* Enable source control sync in Azure Automation if it is set up for this automation account */ bool isSourceControlEnabled = await AutomationSourceControl.isSourceControlEnabled(iseClient.automationManagementClient, iseClient.accountResourceGroups[iseClient.currAccount].Name, iseClient.currAccount.Name); if (isSourceControlEnabled) { ButtonSourceControlRunbook.Visibility = Visibility.Visible; ButtonSourceControlRunbook.IsEnabled = true; } else { ButtonSourceControlRunbook.Visibility = Visibility.Collapsed; } /* Change current directory to new workspace location */ accountPathTextBox.Text = iseClient.currWorkspace; HostObject.CurrentPowerShellTab.Invoke("cd '" + iseClient.currWorkspace + "'"); refreshTimer.Start(); } } catch (Exception exception) { var detailsDialog = MessageBox.Show(exception.Message); } }
public static async Task UploadRunbookAsDraft(AutomationRunbook runbook, AutomationManagementClient automationManagementClient, string resourceGroupName, AutomationAccount account) { RunbookCreateOrUpdateDraftProperties draftProperties; // Parse the script to determine if it is a PS workflow or native script String PSScriptText = File.ReadAllText(runbook.localFileInfo.FullName); System.Management.Automation.Language.Token[] AST; System.Management.Automation.Language.ParseError[] ASTError = null; var ASTScript = System.Management.Automation.Language.Parser.ParseInput(PSScriptText, out AST, out ASTError); // If the script starts with workflow, then create a PS Workflow script runbook or else create a native PS script runbook if (ASTScript.EndBlock.Extent.Text.ToLower().StartsWith("workflow")) { draftProperties = new RunbookCreateOrUpdateDraftProperties(Constants.RunbookType.Workflow, new RunbookDraft()); } else { draftProperties = new RunbookCreateOrUpdateDraftProperties(Constants.RunbookType.PowerShellScript, new RunbookDraft()); } draftProperties.Description = runbook.Description; RunbookCreateOrUpdateDraftParameters draftParams = new RunbookCreateOrUpdateDraftParameters(draftProperties); draftParams.Name = runbook.Name; draftParams.Location = account.Location; await automationManagementClient.Runbooks.CreateOrUpdateWithDraftAsync(resourceGroupName, account.Name, draftParams); /* Update the runbook content from .ps1 file */ RunbookDraftUpdateParameters draftUpdateParams = new RunbookDraftUpdateParameters() { Name = runbook.Name, Stream = PSScriptText }; await automationManagementClient.RunbookDraft.UpdateAsync(resourceGroupName, account.Name, draftUpdateParams); /* Ensure the correct sync status is detected */ RunbookDraft draft = await GetRunbookDraft(runbook.Name, automationManagementClient, resourceGroupName, account.Name); runbook.localFileInfo.LastWriteTime = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedLocal = draft.LastModifiedTime.LocalDateTime; runbook.LastModifiedCloud = draft.LastModifiedTime.LocalDateTime; }