Framework/Core/ContinuousCompliance/CCAutomation.ps1
using namespace System.Management.Automation Set-StrictMode -Version Latest class CCAutomation: CommandBase { hidden [AutomationAccount] $AutomationAccount hidden [Runbook[]] $Runbooks = @() hidden [string] $RunbookName = "Continuous_Assurance_Runbook" hidden [RunbookSchedule[]] $RunbookSchedules = @() hidden [string] $ScheduleName = "Scan_Schedule" hidden [Variable[]] $Variables = @() hidden [UserConfig] $UserConfig hidden [string] $AzSDKCCRGName = "AzSDKCCRG" hidden [string] $deprecatedAccountName = "AzSDKCCAutomationAccount" hidden [PSObject] $OutputObject = @{} hidden [SelfSignedCertificate] $certificateDetail = [SelfSignedCertificate]::new() hidden [Hashtable] $reportStorageTags = @{} hidden [string] $exceptionMsg = "There was an error while configuring Automation Account." hidden [boolean] $isExistingADApp = $false hidden [boolean] $cleanupFlag = $true hidden [string] $updateCommandName = "Update-AzSDKContinuousAssurance" hidden [string] $removeCommandName = "Remove-AzSDKContinuousAssurance" hidden [string] $installCommandName = "Install-AzSDKContinuousAssurance" hidden [string] $certificateAssetName = "AzureRunAsCertificate" hidden [string] $connectionAssetName = "AzureRunAsConnection" CCAutomation( [string] $subscriptionId, [InvocationInfo] $invocationContext, [string] $AutomationAccountLocation,` [string] $ResourceGroupNames,` [string] $OMSWorkspaceId,` [string] $OMSSharedKey,` [string] $AzureADAppName) : Base($subscriptionId, $invocationContext) { $this.AutomationAccount = [AutomationAccount]@{ Name = "AzSDKContinuousAssurance"; Location = $AutomationAccountLocation; AzureADAppName = $AzureADAppName } $this.UserConfig = [UserConfig]@{ OMSCredential = [OMSCredential]@{ OMSWorkspaceId = $OMSWorkspaceId; OMSSharedKey = $OMSSharedKey }; ResourceGroupNames = $ResourceGroupNames } } CCAutomation( [string] $subscriptionId, [InvocationInfo] $invocationContext) : Base($subscriptionId, $invocationContext) { $this.AutomationAccount = [AutomationAccount]@{ Name = "AzSDKContinuousAssurance" } } [MessageData[]] InstallAzSDKContinuousAssurance() { [MessageData[]] $messages = @(); try { #region :check if resource provider is registered if((Get-AzureRmResourceProvider -ProviderNamespace "Microsoft.Automation" ` -Location $this.AutomationAccount.Location -ErrorAction Stop | Measure-Object).Count -eq 0) { $this.cleanupFlag = $false throw ($this.exceptionMsg + "Resource provider 'Microsoft.Automation' is not registered under location " + $this.AutomationAccount.Location) } #endregion #region :check if older CC version is installed and remove if exists and code to be removed after 06/30/2017 # $this.DeleteResourceGroup($this.AzSDKCCRGName); #endregion #region :create new resource group/check if RG exists# $this.AutomationAccount.ResourceGroup = [ConfigurationManager]::GetAzSdkConfigData().AzSDKRGName if((Get-AzureRmResourceGroup -Name $this.AutomationAccount.ResourceGroup -ErrorAction SilentlyContinue|Measure-Object).Count -eq 0) { $this.PublishCustomMessage("Started setting up Automation Account for Continuous Assurance (CA)"); $this.NewCCResourceGroup() } else { #check if automation account exists in RG and code to be updated after 06/30/2017 $existingAccount = Find-AzureRMresource -ResourceGroupName $this.AutomationAccount.ResourceGroup -ResourceType "Microsoft.Automation/automationAccounts" | where-object{$_.ResourceName -in ($this.AutomationAccount.Name,$this.deprecatedAccountName)} if(($existingAccount|Measure-Object).Count -gt 0) { $existingAccount | ForEach-Object{ $tags = $_.Tags #check if depricated version found (old accounts don't have tags) if($_.ResourceName-eq $this.deprecatedAccountName -or ` $tags.Count -eq 0 -or !$tags.Contains("AzSDKVersion") -or ` ([System.Version]$tags["AzSDKVersion"] -lt [System.Version]([ConfigurationManager]::GetAzSdkConfigData().UpdateCompatibleCCVersion))) { #remove depricated version $this.RemoveAzSDKContinuousAssurance($this.SubscriptionContext.SubscriptionId) } else { $this.cleanupFlag = $false #need to run update command throw("Automation Account [$($this.AutomationAccount.Name)] for Continuous Assurance already exists in this subscription.`nIf you want to update the existing account, please run the command: '"+$this.updateCommandName+"'.") } } } else { #update tags to existing RG $this.PublishCustomMessage("Started setting up Automation Account for Continuous Assurance (CA)"); $this.AutomationAccount.RGTags += @{ "AzSDKFeature" = "ContinuousAssurance"; "AzSDKVersion"=$this.GetCurrentModuleVersion(); "CreationTime"=$(get-date).ToUniversalTime().ToString("yyyyMMdd_HHmmss"); } Set-AzureRmResourceGroup -Name $this.AutomationAccount.ResourceGroup ` -Tag $this.AutomationAccount.RGTags ` -ErrorAction Stop $this.OutputObject.ResourceGroup = $null } } #endregion #region: Deploy empty Automation account $this.PublishCustomMessage("Creating Automation Account: [" + $this.AutomationAccount.Name + "]") $this.NewEmptyAutomationAccount() #endregion #region: Create SPN, Certificate $this.NewCCAzureRunAsAccount() #endregion #region: Create/reuse existing storage account (Added this before creating variables since it's value is used in it) $this.UserConfig.StorageAccountRG = $this.AutomationAccount.ResourceGroup $existingStorage = $this.CheckContinuousAssuranceStorage() if(($existingStorage|Measure-Object).Count -gt 0) { $this.UserConfig.StorageAccountName = $existingStorage.ResourceName $this.PublishCustomMessage("Found existing AzSDK storage account: ["+ $this.UserConfig.StorageAccountName +"], it will be used to store CA output reports.") } else { #create new storage $this.UserConfig.StorageAccountName = ("azsdk" + (Get-Date).ToUniversalTime().ToString("yyyyMMddHHmmss")) $this.PublishCustomMessage("Creating storage account ["+ $this.UserConfig.StorageAccountName +"] to store CA output reports.") $newStorage = [Helpers]::NewAzsdkCompliantStorage($this.UserConfig.StorageAccountName,$this.UserConfig.StorageAccountRG, $this.AutomationAccount.Location) if(!$newStorage) { $this.cleanupFlag = $false throw ($this.exceptionMsg + "Failed to create storage account for storing AzSDK CA output reports. Please try re-running the command.") } else { #apply tags $timestamp = $(get-date).ToUniversalTime().ToString("yyyyMMdd_HHmmss") $this.reportStorageTags += @{ "AzSDKFeature" = "ContinuousAssuranceStorage"; "CreationTime"=$timestamp; "LastModified"=$timestamp } Set-AzureRmStorageAccount -ResourceGroupName $newStorage.ResourceGroupName -Name $newStorage.StorageAccountName -Tag $this.reportStorageTags -Force -ErrorAction SilentlyContinue } } $this.OutputObject.StorageAccount = $this.CheckContinuousAssuranceStorage() | Select-Object Name,ResourceGroupName,Sku,Tags #endregion #region: Deploy Automation account items (runbooks, variables, schedules) $this.DeployCCAutomationAccountItems() #endregion #succefully installed $this.cleanupFlag = $false $this.PublishCustomMessage("Completed setup for AzSDK Continuous Assurance.`nYour subscription and resources (from the specified resource groups) will be scanned periodically from now on and the control evaluation results will be sent to the specified OMS workspace.`nYou should use the AzSDK OMS solution to view your subscription and resource health status.") $messages += [MessageData]::new("Below resources are created in resource group ["+$this.AutomationAccount.ResourceGroup+"] as part of Continuous Assurance",$this.OutputObject) } catch { $this.PublishException($_) #cleanup if exception occurs if($this.cleanupFlag) { $this.PublishCustomMessage("Error occured. Rolling back the changes.") if(![string]::IsNullOrWhiteSpace($this.AutomationAccount.ResourceGroup)) { $account = Get-AzureRMAutomationAccount -ResourceGroupName $this.AutomationAccount.ResourceGroup -Name $this.AutomationAccount.Name -ErrorAction silentlycontinue if(($account|Measure-Object).Count -gt 0) { $account | Remove-AzureRmAutomationAccount -Force -ErrorAction SilentlyContinue } } #clean AD App only if AD App was newly created if(![string]::IsNullOrWhiteSpace($this.AutomationAccount.AzureADAppName) -and !$this.isExistingADApp) { $ADApplication = Get-AzureRmADApplication -DisplayNameStartWith $this.AutomationAccount.AzureADAppName -ErrorAction SilentlyContinue | Where-Object -Property DisplayName -eq $this.AutomationAccount.AzureADAppName if($ADApplication) { Remove-AzureRmADApplication -ObjectId $ADApplication.ObjectId -Force -ErrorAction Stop } } } } return $messages; } [MessageData[]] UpdateAzSDKContinuousAssurance($ResourceGroupNames,$OMSWorkspaceId,$OMSSharedKey,$AzureADAppName,$UpdateCertificate) { [MessageData[]] $messages = @(); #set default account properties $this.AutomationAccount.Name = "AzSDKContinuousAssurance" $this.AutomationAccount.ResourceGroup = [ConfigurationManager]::GetAzSdkConfigData().AzSDKRGName #region :check if automation account is compatible for update $existingAccount = Find-AzureRMresource -ResourceGroupName $this.AutomationAccount.ResourceGroup -ResourceType "Microsoft.Automation/automationAccounts" | where-object{$_.ResourceName -in ($this.AutomationAccount.Name,$this.deprecatedAccountName)} $automationTags = @() if(($existingAccount|Measure-Object).Count -gt 0) { #check if depricated version found (old accounts don't have tags) $existingAccount | ForEach-Object{ $automationTags = $_.Tags if($automationTags.Count -eq 0 -or !$automationTags.Contains("AzSDKVersion") -or ` ([System.Version]$automationTags["AzSDKVersion"] -lt [System.Version]([ConfigurationManager]::GetAzSdkConfigData().UpdateCompatibleCCVersion))) { throw("Deprecated and uncompatible version of Continuous Assurance Automation Account [$($_.ResourceName)] found. Please remove this account using '"+$this.removeCommandName+"' command and install latest version using '"+$this.installCommandName+"' command with required parameters.") } } } else { throw("No Continuous Assurance Automation Account found. Please install using '"+ $this.installCommandName +"' command with required parameters.") } #endregion $this.PublishCustomMessage("Started updating Automation Account for Continuous Assurance"); #region :Remove existing and create new AzureRunAsConnection if AzureADAppName param is passed else update certificate if UpdateCertificate switch is present if(![string]::IsNullOrWhiteSpace($AzureADAppName)) { $this.PublishCustomMessage("Updating $($this.connectionAssetName) in Automation Account") $this.RemoveCCAzureRunAsAccount() $this.RemoveCCAzureRunAsCertificate() $this.AutomationAccount.AzureADAppName = $AzureADAppName $this.NewCCAzureRunAsAccount() } elseif($UpdateCertificate) { $this.PublishCustomMessage("Updating certificate in $($this.connectionAssetName)") $this.RemoveCCAzureRunAsCertificate() $this.UpdateCCAzureRunAsAccount("") } #endregion #region: create storage account if not present and update same in variables# $this.OutputObject.Variables = @() #This is added to initialize variables $newStorageName = $null #check if storage exists $existingStorage = $this.CheckContinuousAssuranceStorage() if(($existingStorage|Measure-Object).Count -gt 0) { #update/add tags $modifyTimestamp = $(get-date).ToUniversalTime().ToString("yyyyMMdd_HHmmss") $storageTags = $existingStorage.Tags if($storageTags.ContainsKey("LastModified")) { $storageTags["LastModified"] = $modifyTimestamp; } else { $storageTags.Add("LastModified",$modifyTimestamp) } if(!$storageTags.ContainsKey("AzSDKFeature")) { $storageTags.Add("AzSDKFeature","ContinuousAssuranceStorage") } if(!$storageTags.ContainsKey("CreationTime")) { $storageTags.Add("CreationTime",$modifyTimestamp) } Set-AzureRmStorageAccount -ResourceGroupName $existingStorage.ResourceGroupName -Name $existingStorage.ResourceName -Tag $storageTags -Force -ErrorAction SilentlyContinue $this.OutputObject.StorageAccountName = $existingStorage.ResourceName } else { #create default storage $newStorageName = ("azsdk" + (Get-Date).ToUniversalTime().ToString("yyyyMMddHHmmss")) $this.PublishCustomMessage("Creating Storage Account [$newStorageName] to store Continuous Assurance output reports") $newStorage = [Helpers]::NewAzsdkCompliantStorage($newStorageName, $this.AutomationAccount.ResourceGroup, $existingAccount.Location) if(!$newStorage) { throw ($this.exceptionMsg + "Failed to create AzSDK compliant Storage Account for output reports storage. Please run command again.") } else { #apply tags $timestamp = $(get-date).ToUniversalTime().ToString("yyyyMMdd_HHmmss") $this.reportStorageTags += @{ "AzSDKFeature" = "ContinuousAssuranceStorage"; "CreationTime"=$timestamp; "LastModified"=$timestamp } Set-AzureRmStorageAccount -ResourceGroupName $newStorage.ResourceGroupName -Name $newStorage.StorageAccountName -Tag $this.reportStorageTags -Force -ErrorAction SilentlyContinue } #update storage account variable with new value $varStorageName = [Variable]@{ Name = "ReportsStorageAccountName"; Value = $newStorage.StorageAccountName; IsEncrypted = $false; Description ="Name of Storage Account where output reports will be stored" } $this.UpdateVariable($varStorageName) $this.OutputObject.StorageAccountName = $newStorageName } #endregion #region :update user configurable variables which are present in params if(![string]::IsNullOrWhiteSpace($OMSWorkspaceId)) { $varOmsWSID = [Variable]@{ Name = "OMSWorkspaceId"; Value = $OMSWorkspaceId; IsEncrypted = $false; Description ="OMS Workspace Id" } $this.UpdateVariable($varOmsWSID) $this.PublishCustomMessage("Updating variable ["+$varOmsWSID.Name+"]") } if(![string]::IsNullOrWhiteSpace($OMSSharedKey)) { $varOMSSharedKey = [Variable]@{ Name = "OMSSharedKey"; Value = $OMSSharedKey; IsEncrypted = $true; Description ="OMS Workspace Shared Key" } $this.UpdateVariable($varOMSSharedKey) $this.PublishCustomMessage("Updating variable ["+$varOMSSharedKey.Name+"]") } if(![string]::IsNullOrWhiteSpace($ResourceGroupNames)) { $varAppRG = [Variable]@{ Name = "AppResourceGroupNames"; Value = $ResourceGroupNames; IsEncrypted = $false; Description ="Comma separated values of the different resource groups that has to be scanned" } $this.UpdateVariable($varAppRG) $this.PublishCustomMessage("Updating variable ["+$varAppRG.Name+"]") } #endregion #region :update runbook & schedule #unlink runbook from existing schedules $scheduledRunbooks = Get-AzureRmAutomationScheduledRunbook -AutomationAccountName $this.AutomationAccount.Name ` -ResourceGroupName $this.AutomationAccount.ResourceGroup | Where-Object {$_.RunbookName -eq $this.RunbookName} if(($scheduledRunbooks|Measure-Object).Count -gt 0) { $scheduledRunbooks | ForEach-Object { Unregister-AzureRmAutomationScheduledRunbook -RunbookName $_.RunbookName -ScheduleName $_.ScheduleName ` -ResourceGroupName $_.ResourceGroupName ` -AutomationAccountName $_.AutomationAccountName -ErrorAction Stop -Force | Out-Null }; } #remove existing and create new runbook $existingRunbook = Get-AzureRmAutomationRunbook -AutomationAccountName $this.AutomationAccount.Name ` -ResourceGroupName $this.AutomationAccount.ResourceGroup if(($existingRunbook|Measure-Object).Count -gt 0) { $existingRunbook | Remove-AzureRmAutomationRunbook -Force -ErrorAction Stop } #$this.PublishCustomMessage("Updating runbook - ["+ $this.RunbookName +"]") $this.NewCCRunbooks() #relink existing schedules with runbook #$this.PublishCustomMessage("Linking schedule - ["+$this.ScheduleName+"] to the runbook") if(($scheduledRunbooks|Measure-Object).Count -gt 0) { $scheduledRunbooks | ForEach-Object { Register-AzureRmAutomationScheduledRunbook -RunbookName $this.RunbookName -ScheduleName $_.ScheduleName ` -ResourceGroupName $_.ResourceGroupName ` -AutomationAccountName $_.AutomationAccountName -ErrorAction Stop | Out-Null }; } else { #create default schedule $this.NewCCSchedules() } #endregion #region :update last modified and version tag $modifyTimestamp = $(get-date).ToUniversalTime().ToString("yyyyMMdd_HHmmss") if($automationTags.ContainsKey("LastModified")) { $automationTags["LastModified"] = $modifyTimestamp; } else { $automationTags.Add("LastModified",$modifyTimestamp) } if($automationTags.ContainsKey("AzSDKVersion")) { $automationTags["AzSDKVersion"] = $this.GetCurrentModuleVersion(); } else { $automationTags.Add("AzSDKVersion",$this.GetCurrentModuleVersion()) } Set-AzureRmAutomationAccount -ResourceGroupName $this.AutomationAccount.ResourceGroup -Name $this.AutomationAccount.Name -Tags $automationTags -ErrorAction SilentlyContinue #endregion $this.PublishCustomMessage("Completed updating Automation Account for Continuous Assurance") $messages += [MessageData]::new("Below resources/automation account assets are updated in your subscription", $this.OutputObject) return $messages; } [MessageData[]] GetAzSDKContinuousAssurance() { [MessageData[]] $messages = @(); $this.AutomationAccount.ResourceGroup = [ConfigurationManager]::GetAzSdkConfigData().AzSDKRGName #Fetch automation account components $this.OutputObject.AutomationAccount = Get-AzureRmAutomationAccount -Name $this.AutomationAccount.Name -ResourceGroupName $this.AutomationAccount.ResourceGroup | Select-Object AutomationAccountName,Location,Plan,ResourceGroupName,State,Tags if($this.OutputObject.AutomationAccount) { $this.OutputObject.StorageAccount = $this.CheckContinuousAssuranceStorage() | Select-Object ResourceName,ResourceGroupName,Sku,Tags $this.OutputObject.Variables = Get-AzureRmAutomationVariable -AutomationAccountName $this.AutomationAccount.Name -ResourceGroupName $this.AutomationAccount.ResourceGroup | Select-Object Name,Description,Value,Encrypted $this.OutputObject.Runbooks = Get-AzureRmAutomationRunbook -AutomationAccountName $this.AutomationAccount.Name -ResourceGroupName $this.AutomationAccount.ResourceGroup | Select-Object Name,Description,RunbookType,State,JobCount,CreationTime,LastModifiedTime,LastModifiedBy $this.OutputObject.Schedules = Get-AzureRmAutomationSchedule -AutomationAccountName $this.AutomationAccount.Name -ResourceGroupName $this.AutomationAccount.ResourceGroup $this.OutputObject.AzureRunAsConnection = Get-AzureRmAutomationConnection -AutomationAccountName $this.AutomationAccount.Name -Name $this.connectionAssetName -ResourceGroupName $this.AutomationAccount.ResourceGroup | Select-Object Name,Description,ConnectionTypeName $this.OutputObject.AzureRunAsCertificate = Get-AzureRmAutomationCertificate -AutomationAccountName $this.AutomationAccount.Name -Name $this.certificateAssetName -ResourceGroupName $this.AutomationAccount.ResourceGroup | Select-Object Name,Description,CreationTime,ExpiryTime,LastModifiedTime $this.PublishCustomMessage("Below resources/automation account assets are present in subscription as part of Continuous Assurance") $this.PublishCustomMessage("Automation account : $($this.OutputObject.AutomationAccount.AutomationAccountName)") $this.PublishCustomMessage("Reports storage account: $($this.OutputObject.StorageAccount.ResourceName)") $this.PublishCustomMessage("Variable(s): ") $this.OutputObject.Variables | ForEach-Object{ $this.PublishCustomMessage("Name : " + $_.Name) if(!$_.Encrypted) { $this.PublishCustomMessage("Value : " + $_.Value) } else { $this.PublishCustomMessage("Value : The value is encrypted" ) } } $this.PublishCustomMessage("Runbook(s) : ") $this.OutputObject.Runbooks | ForEach-Object{ $this.PublishCustomMessage($_.Name) } $this.PublishCustomMessage("Schedule(s) :") $this.OutputObject.Schedules | ForEach-Object{ $this.PublishCustomMessage($_.Name) } $this.PublishCustomMessage("Connection name : $($this.OutputObject.AzureRunAsConnection.Name)") $this.PublishCustomMessage("Certificate name : $($this.OutputObject.AzureRunAsCertificate.Name)") $messages += [MessageData]::new("Below resources/automation account assets are present in subscription as part of Continuous Assurance", $this.OutputObject) } else { $this.PublishCustomMessage("No Continuous Assurance Automation Account is installed in this subscription") $messages += [MessageData]::new("No Continuous Assurance Automation Account is installed in this subscription") } return $messages } [MessageData[]] RemoveAzSDKContinuousAssurance($DeleteStorageReports) { [MessageData[]] $messages = @(); $this.PublishCustomMessage("This command will delete resources in your subscription which were installed by AzSDK Continuous Assurance") $this.AutomationAccount.ResourceGroup = [ConfigurationManager]::GetAzSdkConfigData().AzSDKRGName #Initialize variables for confirmation pop ups $title = "Confirm" $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "This means Yes" $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "This means No" $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) #below is hack for removing error due to strict mode - host variable is not assigned in the method $host = $host #filter accounts with old/new name $accounts = Get-AzureRMAutomationAccount -ResourceGroupName $this.AutomationAccount.ResourceGroup -ErrorAction silentlycontinue | where-object{$_.AutomationAccountName -in ($this.AutomationAccount.Name,$this.deprecatedAccountName)} if($accounts) { $accounts | ForEach-Object{ $accountConfirmMsg = "Are you sure you want to delete Continuous Assurance Automation Account '$($_.AutomationAccountName)'" # user confirmation $result = $host.ui.PromptForChoice($title, $accountConfirmMsg, $options, 1) if($result -eq 0) { #user selected yes Remove-AzureRmAutomationAccount -ResourceGroupName $_.ResourceGroupName -name $_.AutomationAccountName -Force -ErrorAction stop $messages += [MessageData]::new("Removed Automation Account [$($_.AutomationAccountName)] from resource group [$($this.AutomationAccount.ResourceGroup)]") $this.PublishCustomMessage("Removed Automation Account [$($_.AutomationAccountName)] from resource group [$($this.AutomationAccount.ResourceGroup)]") } else { #user selected no $messages += [MessageData]::new("You have chosen not to delete Automation Account [$($_.AutomationAccountName)]") $this.PublishCustomMessage("You have chosen not to delete Automation Account [$($_.AutomationAccountName)]") } } } else { #account not found $messages += [MessageData]::new("Continuous Assurance Automation Account doesn't exist in this subscription") $this.PublishCustomMessage("Continuous Assurance Automation Account doesn't exist in this subscription") } #remove storage account container if switch param is present $existingStorage = $this.CheckContinuousAssuranceStorage() if(($existingStorage|Measure-Object).Count -gt 0) { $containerName = "azsdkexecutionlogs" $keys = Get-AzureRmStorageAccountKey -ResourceGroupName $existingStorage.ResourceGroupName -Name $existingStorage.ResourceName $storageContext = New-AzureStorageContext -StorageAccountName $existingStorage.ResourceName -StorageAccountKey $keys[0].Value -Protocol Https $existingContainer = Get-AzureStorageContainer -Name $containerName -Context $storageContext -ErrorAction SilentlyContinue if($existingContainer) { #switch is present if($DeleteStorageReports) { #user confirmation before deleting container $storageConfirmMsg = "Are you sure you want to delete '$containerName' container in Storage Account '$($existingStorage.ResourceName)' which contains security scan logs/reports ?" $result = $host.ui.PromptForChoice($title, $storageConfirmMsg, $options, 1) if($result -eq 0) { #user selected yes $existingContainer | Remove-AzureStorageContainer -Force -ErrorAction SilentlyContinue if((Get-AzureStorageContainer -Name $containerName -Context $storageContext -ErrorAction SilentlyContinue|Measure-Object).Count -eq 0) { #deleted successfully in confirmation box $messages += [MessageData]::new("Removed '$containerName' container from [$($existingStorage.ResourceName)] Storage Account") $this.PublishCustomMessage("Removed '$containerName' container from [$($existingStorage.ResourceName)] Storage Account") } else { #error occured $messages += [MessageData]::new("Error occured while removing container [$containerName] from Storage Account [$($existingStorage.ResourceName)]. Please check your access permissions and try again.") $this.PublishCustomMessage("Error occured while removing container [$containerName] from Storage Account [$($existingStorage.ResourceName)]. Please check your access permissions and try again.") } } #user selected no in confirmation box else { $messages += [MessageData]::new("You have chosen not to delete container [$containerName] from Storage Account [$($existingStorage.ResourceName)]") $this.PublishCustomMessage("You have chosen not to delete container [$containerName] from Storage Account [$($existingStorage.ResourceName)]") } } #switch param is not present else { $this.PublishCustomMessage("You have chosen not to delete container [$containerName] from reports storage account [$($existingStorage.ResourceName)] so it is retained. It can be deleted by passing 'DeleteStorageReports' switch parameter to this function.") $messages += [MessageData]::new("You have chosen not to delete container [$containerName] from reports storage account [$($existingStorage.ResourceName)] so it is retained. It can be deleted by passing 'DeleteStorageReports' switch parameter to this function.") } } #switch is present but container not found elseif($DeleteStorageReports) { $messages += [MessageData]::new("Container [$containerName] not found in [$($existingStorage.ResourceName)] Storage Account") $this.PublishCustomMessage("Container [$containerName] container not found in [$($existingStorage.ResourceName)] Storage Account") } } #switch is present but no storage account found elseif($DeleteStorageReports) { $this.PublishCustomMessage("AzSDK reports storage account doesn't exist in resource group [$($this.AutomationAccount.ResourceGroup)]") $messages += [MessageData]::new("No AzSDK reports storage doesn't account exist in resource group [$($this.AutomationAccount.ResourceGroup)]") } #remove job collection if exists to make compatible with old accounts $jobCollectionName = "AzSDKCCJobCollection" $jobCollection = Get-AzureRmSchedulerJobCollection -ResourceGroupName $this.AutomationAccount.ResourceGroup -JobCollectionName $jobCollectionName -ErrorAction SilentlyContinue if($jobCollection) { #user confirmation $jobcollectionConfirmMsg = "Are you sure you want to delete Scheduler Job Collection '$jobCollectionName'?" $result = $host.ui.PromptForChoice($title, $jobcollectionConfirmMsg, $options, 1) if($result -eq 0) { #user selected yes $jobCollection | Remove-AzureRmSchedulerJobCollection -ErrorAction SilentlyContinue | Out-Null if((Find-AzureRmResource -ResourceNameEquals $jobCollectionName -ResourceGroupNameEquals $this.AutomationAccount.ResourceGroup -ResourceType "Microsoft.Scheduler/jobCollections"|Measure-Object).Count -eq 0) { $messages += [MessageData]::new("Removed Scheduler Job Collection [$jobCollectionName] from resource group [$($this.AutomationAccount.ResourceGroup)]") $this.PublishCustomMessage("Removed Scheduler Job Collection [$jobCollectionName] from resource group [$($this.AutomationAccount.ResourceGroup)]") } else { #error occured $messages += [MessageData]::new("Error occured while removing [$jobCollectionName)] Scheduler Job Collection from resource group [$($this.AutomationAccount.ResourceGroup)]. Please check your access permissions and try again.") $this.PublishCustomMessage("Error occured while removing [$jobCollectionName] Scheduler Job Collection from resource group [$($this.AutomationAccount.ResourceGroup)]. Please check your access permissions and try again.") } } else { $messages += [MessageData]::new("You have chosen not to delete Scheduler Job Collection [$jobCollectionName]") $this.PublishCustomMessage("You have chosen not to delete Scheduler Job Collection [$jobCollectionName]") } } return $messages } #region: Internal functions for install account hidden [void] DeleteResourceGroup($resourceGroupName) { if((Get-AzureRmResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue | Measure-Object).Count -gt 0) { Remove-AzureRmResourceGroup -Name $resourceGroupName -Force -ErrorAction Stop | Out-Null } } hidden [void] NewCCResourceGroup() { $this.AutomationAccount.RGTags += @{ "AzSDKFeature" = "ContinuousAssurance"; "AzSDKVersion"=$this.GetCurrentModuleVersion(); "CreationTime"=$(get-date).ToUniversalTime().ToString("yyyyMMdd_HHmmss"); } $newRG = New-AzureRmResourceGroup -Name $this.AutomationAccount.ResourceGroup -Location $this.AutomationAccount.Location ` -Tag $this.AutomationAccount.RGTags ` -ErrorAction Stop $this.OutputObject.ResourceGroup = $newRG | Select-Object ResourceGroupName,Location } hidden [void] DeployCCAutomationAccountItems() { #Adding below to make schedule available for adding in runbook config $ScanSchedule = [RunbookSchedule]@{ Name = $this.ScheduleName; Frequency = [ScheduleFrequency]::Day; Interval = 1; Description = "Scheduling job to scan subscription and app resource groups"; StartTime = ([System.DateTime]::Now.AddMinutes(10).ToString("yyyy-MM-dd'T'HH:mm:sszzz")); LinkedRubooks = @($this.RunbookName); Key = "Scan_Schedule" } $this.RunbookSchedules += @($ScanSchedule) #$this.PublishCustomMessage("Creating runbook - ["+ $this.RunbookName +"]") $this.NewCCRunbooks() #$this.PublishCustomMessage("Linking schedule - ["+$this.ScheduleName+"] to the runbook") $this.NewCCSchedules() #$this.PublishCustomMessage("Creating variables") $this.NewCCVariables() } hidden [void] NewEmptyAutomationAccount() { #Add tags $timestamp = $(get-date).ToUniversalTime().ToString("yyyyMMdd_HHmmss") $this.AutomationAccount.AccountTags += @{ "AzSDKFeature" = "ContinuousAssurance"; "AzSDKVersion"=$this.GetCurrentModuleVersion(); "CreationTime"=$timestamp; "LastModified"=$timestamp } $this.OutputObject.AutomationAccount = New-AzureRmAutomationAccount -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -Name $this.AutomationAccount.Name -Location $this.AutomationAccount.Location ` -Plan Basic -Tags $this.AutomationAccount.AccountTags -ErrorAction Stop | Select-Object AutomationAccountName,Location,Plan,ResourceGroupName,State,Tags } hidden [void] NewCCRunbooks() { $CCRunbook = [Runbook]@{ Name = $this.RunbookName; Type = "PowerShell"; Description = "This runbook is responsible for running SVT and SS-Health commands and keep all modules updated"; LogProgress = $false; LogVerbose = $false; Key="Continuous_Assurance_Runbook" } $this.Runbooks += @($CCRunbook) $this.Runbooks | ForEach-Object{ $filePath = $this.AddConfigValues($_.Name+".ps1"); Import-AzureRmAutomationRunbook -Name $_.Name -Description $_.Description -Type $_.Type ` -Path $filePath ` -LogProgress $_.LogProgress -LogVerbose $_.LogVerbose ` -AutomationAccountName $this.AutomationAccount.Name ` -ResourceGroupName $this.AutomationAccount.ResourceGroup -Published -ErrorAction Stop #cleanup Remove-Item -Path $filePath -Force } $this.OutputObject.Runbooks = $this.Runbooks | Select-Object Name,Description,Type } hidden [void] NewCCSchedules() { if($this.RunbookSchedules.count -eq 0) { $ScanSchedule = [RunbookSchedule]@{ Name = $this.ScheduleName; Frequency = [ScheduleFrequency]::Day; Interval = 1; Description = "Scheduling job to scan subscription and app resource groups"; StartTime = ([System.DateTime]::Now.AddMinutes(10).ToString("yyyy-MM-dd'T'HH:mm:sszzz")); LinkedRubooks = @($this.RunbookName); Key = "Scan_Schedule" } $this.RunbookSchedules += @($ScanSchedule) } $this.RunbookSchedules | ForEach-Object{ $scheduleName = $_.Name New-AzureRmAutomationSchedule -AutomationAccountName $this.AutomationAccount.Name -Name $_.Name ` -ResourceGroupName $this.AutomationAccount.ResourceGroup -StartTime $_.StartTime ` -Description $_.Description -DayInterval $_.Interval -ErrorAction Stop $_.LinkedRubooks | ForEach-Object{ Register-AzureRmAutomationScheduledRunbook -RunbookName $_ -ScheduleName $scheduleName ` -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name -ErrorAction Stop } } $this.OutputObject.Schedules = $this.RunbookSchedules } hidden [void] NewCCVariables() { $varAppRG = [Variable]@{ Name = "AppResourceGroupNames"; Value = $this.UserConfig.ResourceGroupNames; IsEncrypted = $false; Description ="Comma separated values of the different resource groups that has to be scanned" } $varOmsWSID = [Variable]@{ Name = "OMSWorkspaceId"; Value = $this.UserConfig.OMSCredential.OMSWorkspaceId; IsEncrypted = $false; Description ="OMS Workspace Id" } $varOmsWSKey = [Variable]@{ Name = "OMSSharedKey"; Value = $this.UserConfig.OMSCredential.OMSSharedKey; IsEncrypted = $true; Description ="OMS Workspace Shared Key" } $varStorageName = [Variable]@{ Name = "ReportsStorageAccountName"; Value = $this.UserConfig.StorageAccountName; IsEncrypted = $false; Description ="Name of storage account where output reports will be stored" } $this.Variables += @($varAppRG,$varOmsWSID,$varOmsWSKey,$varStorageName) $this.Variables|ForEach-Object{ New-AzureRmAutomationVariable -Name $_.Name -Encrypted $_.IsEncrypted ` -Description $_.Description -Value $_.Value ` -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name -ErrorAction Stop #$this.PublishCustomMessage("Name : "+$_.Name) } $this.OutputObject.Variables = $this.Variables | Select-Object Name,Description } hidden [void] NewCCAzureRunAsAccount() { #Handle the case when user hasnt specified the AAD App name for CA. if([string]::IsNullOrWhiteSpace($this.AutomationAccount.AzureADAppName)) { $subscriptionScope = "/subscriptions/{0}" -f $this.SubscriptionContext.SubscriptionId $azsdkRoleAssignments = Get-AzureRmRoleAssignment -Scope $subscriptionScope -RoleDefinitionName Reader | Where-Object { $_.DisplayName -like 'AzSDK_CA_SPN_*' } $cnt = ($azsdkRoleAssignments | Measure-Object).Count if($cnt -gt 0) { $this.PublishCustomMessage("Found $cnt previously configured runtime accounts for AzSDK CA. Checking if one of them can be used.") foreach($azsdkRoleAssignment in $azsdkRoleAssignments) { $this.PublishCustomMessage("Checking account: ["+ $azsdkRoleAssignment.DisplayName +"]...") try { $this.UpdateCCAzureRunAsAccount($azsdkRoleAssignment.DisplayName); $this.PublishCustomMessage("Successfully configured AzSDK CA Automation Account with this runtime account.") #Returning from here as the below steps of updating automation account are already completed as part of the UpdateCCAzureRunAsAccount command. #TODO to refactor the common code #set this flag to identify whether clean up AD App is needed in case of exception $this.isExistingADApp = $true return; } catch { $this.PublishCustomMessage("Unable to use the account. You may not have Owner permission on the AAD application.") } } } #Compute the new AAD CA App name. As there was no preconfigured CA AAD App on this subscription or didnt have the permission to update the existing AAD app $CARunAsAccountName = ("AzSDK_CA_SPN_" + (Get-Date).ToUniversalTime().ToString("yyyyMMddHHmmss")) $this.AutomationAccount.AzureADAppName = $CARunAsAccountName; } $pfxFilePath = $null $thumbPrint = $null $ADApplication = $null try { $ADApplication = Get-AzureRmADApplication -DisplayNameStartWith $this.AutomationAccount.AzureADAppName | Where-Object -Property DisplayName -eq $this.AutomationAccount.AzureADAppName if($ADApplication) { $this.PublishCustomMessage("Found AAD application in the directory: ["+ $this.AutomationAccount.AzureADAppName +"]") #set this flag to identify whether clean up AD App is needed in case of exception $this.isExistingADApp = $true } else { $this.PublishCustomMessage("Creating new AAD application: ["+ $this.AutomationAccount.AzureADAppName +"]") #create new AD App $ADApplication = New-AzureRmADApplication -DisplayName $this.AutomationAccount.AzureADAppName ` -HomePage ("https://" + $this.AutomationAccount.AzureADAppName) ` -IdentifierUris ("https://" + $this.AutomationAccount.AzureADAppName) -ErrorAction Stop #create new SP $this.PublishCustomMessage("Creating new service principal (SPN) for the AAD application.`nThis will be used as the runtime account for AzSDK CA.") New-AzureRMADServicePrincipal -ApplicationId $ADApplication.ApplicationId -ErrorAction Stop | Out-Null } $this.PublishCustomMessage("Genrating new credential for AzSDK CA SPN.") $selfsignedCertificate = [ActiveDirectoryHelper]::NewSelfSignedCertificate($this.AutomationAccount.AzureADAppName,$this.certificateDetail.CertStartDate,$this.certificateDetail.CertEndDate,$this.certificateDetail.Provider) #create password $secureCertPassword = [Helpers]::NewSecurePassword() $pfxFilePath = $env:TEMP+ "\temp.pfx" Export-PfxCertificate -Cert $selfsignedCertificate -Password $secureCertPassword -FilePath $pfxFilePath | Out-Null $publicCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(,$selfsignedCertificate.GetRawCertData()) #Authenticating AAD App service principal with newly created certificate credential [ActiveDirectoryHelper]::UpdateADAppCredential($ADApplication.ApplicationId,$publicCert,$this.certificateDetail.CredStartDate,$this.certificateDetail.CredEndDate,"False") $this.PublishCustomMessage("Configuring permissions for AzSDK CA SPN.`nAdding SPN to Reader role at subscription scope and Contributor role at "+ $this.AutomationAccount.ResourceGroup +" resource group scope.") $NewSPNRole = $null $RetryCount = 0; While ($null -eq $NewSPNRole -and $RetryCount -le 6) { #Assign RBAC to SPN - contributor at RG and reader at subscription level Start-Sleep 10 New-AzureRMRoleAssignment -RoleDefinitionName Reader -ServicePrincipalName $ADApplication.ApplicationId -ErrorAction SilentlyContinue | Out-Null New-AzureRMRoleAssignment -Scope (Get-AzureRmResourceGroup -Name $this.AutomationAccount.ResourceGroup -ErrorAction Stop).ResourceId -RoleDefinitionName Contributor -ServicePrincipalName $ADApplication.ApplicationId -ErrorAction SilentlyContinue | Out-Null $NewSPNRole = Get-AzureRMRoleAssignment -ServicePrincipalName $ADApplication.ApplicationId -ErrorAction SilentlyContinue $RetryCount++; } $thumbPrint = $publicCert.thumbPrint.ToString() #create certificate asset $this.PublishCustomMessage("Configuring AzSDK CA Automation account with SPN credential.") $newCertificateAsset = $this.NewCCCertificate($pfxFilePath,$secureCertPassword) # Create a Automation connection asset. This connection uses the service principal. $newConnectionAsset = $this.NewCCConnection($ADApplication.ApplicationId,$thumbPrint) $this.OutputObject.AzureADAppName = $this.AutomationAccount.AzureADAppName $this.OutputObject.AzureRunAsConnection = $newConnectionAsset | Select-Object Name,Description,ConnectionTypeName } catch { $this.PublishCustomMessage("There was an error while setting up the AzSDK CA Automation Account. You may not have the required permissions."); throw; } finally { #cleanup pfx file if($pfxFilePath) { Remove-Item -Path $pfxFilePath -Force -ErrorAction SilentlyContinue } #cleanup certificate $CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreName]::My,[System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser) $CertStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) if($thumbPrint) { $tempCert = $CertStore.Certificates.Find("FindByThumbprint",$thumbPrint,$FALSE) if($tempCert) { $CertStore.Remove($tempCert[0]) } } } } hidden [string] AddConfigValues([string]$fileName) { $outputFilePath = "$Env:LOCALAPPDATA\$fileName"; $ccRunbook = $this.LoadServerConfigFile($fileName) #append escape character (`) before '$' symbol $policyStoreUrl = [ConfigurationManager]::GetAzSdkSettings().OnlinePolicyStoreUrl.Replace('$',"``$") $ccRunbook | Foreach-Object { $temp1 = $_ -replace "\[#SubscriptionID#\]",$this.SubscriptionContext.SubscriptionId; $temp2 = $temp1 -replace "\[#AutomationAccountRG#\]",$this.AutomationAccount.ResourceGroup; $temp3 = $temp2 -replace "\[#AutomationAccountName#\]",$this.AutomationAccount.Name; $temp4 = $temp3 -replace "\[#RunbookName#\]",($this.Runbooks|Where-Object{$_.Key -eq "Continuous_Assurance_Runbook"}|Select-Object -ExpandProperty Name); $temp5 = $temp4 -replace "\[#ScanScheduleName#\]",($this.RunbookSchedules|Where-Object{$_.Key -eq "Scan_Schedule"}|Select-Object -ExpandProperty Name); $temp6 = $temp5 -replace "\[#UseOnlinePolicyStore#\]",$this.ConvertBooleanToString([ConfigurationManager]::GetAzSdkSettings().UseOnlinePolicyStore); $temp7 = $temp6 -replace "\[#OnlinePolicyStoreUrl#\]",$policyStoreUrl; $temp8 = $temp7 -replace "\[#EnableAADAuthForOnlinePolicyStore#\]",$this.ConvertBooleanToString([ConfigurationManager]::GetAzSdkSettings().EnableAADAuthForOnlinePolicyStore); $temp8 -replace "\[#ModuleName#\]",$this.GetModuleName(); } | Out-File $outputFilePath return $outputFilePath } hidden [string] ConvertBooleanToString($boolvalue) { switch($boolvalue) { "true"{ return "true" } "false"{ return "false"} } return "false" #adding this to prevent error all path doesn't return value" } #endregion #region: Internal functions for update account hidden [void] UpdateVariable($VariableObj) { #remove existing and create new variable $existingVar = Get-AzureRmAutomationVariable -AutomationAccountName $this.AutomationAccount.Name -ResourceGroupName $this.AutomationAccount.ResourceGroup -Name $VariableObj.Name -ErrorAction SilentlyContinue if(($existingVar|Measure-Object).Count -gt 0) { $existingVar|Remove-AzureRmAutomationVariable -ErrorAction Stop } $newVariable = New-AzureRmAutomationVariable -Name $VariableObj.Name ` -Description $VariableObj.Description` -Encrypted $VariableObj.IsEncrypted ` -Value $VariableObj.Value ` -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name -ErrorAction Stop $this.OutputObject.Variables += ($newVariable | Select-Object Name,Description,Value) } hidden [void] RemoveCCAzureRunAsAccount() { #remove existing azurerunasconnection if((Get-AzureRmAutomationConnection -Name $this.connectionAssetName -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name -ErrorAction SilentlyContinue|Measure-Object).Count -gt 0) { Remove-AzureRmAutomationConnection -ResourceGroupName $this.AutomationAccount.ResourceGroup` -AutomationAccountName $this.AutomationAccount.Name -Name $this.connectionAssetName -Force -ErrorAction stop } } hidden [void] RemoveCCAzureRunAsCertificate() { #remove existing certificate $isCertPresent = Get-AzureRmAutomationCertificate -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name -Name $this.certificateAssetName -ErrorAction SilentlyContinue if($isCertPresent) { Remove-AzureRmAutomationCertificate -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name -Name $this.certificateAssetName -ErrorAction SilentlyContinue } } hidden [void] UpdateCCAzureRunAsAccount([string] $azSDKADAppName) { $pfxFilePath = $null $thumbPrint = $null try { #fetch existing AD App used in connection $appID = "" if([string]::IsNullOrWhiteSpace($azSDKADAppName)) { $connection = Get-AzureRmAutomationConnection -AutomationAccountName $this.AutomationAccount.Name ` -ResourceGroupName $this.AutomationAccount.ResourceGroup -Name $this.connectionAssetName -ErrorAction Stop $appID = $connection.FieldDefinitionValues.ApplicationId $this.AutomationAccount.AzureADAppName = (Get-AzureRmADApplication -ApplicationId $connection.FieldDefinitionValues.ApplicationId -ErrorAction stop).DisplayName } else { $ADAppObject = Get-AzureRmADApplication -DisplayNameStartWith $azSDKADAppName -ErrorAction stop $this.AutomationAccount.AzureADAppName = $azSDKADAppName #Assuming that the APP would always be there as this value is populated from the roleassignment itself. $appID = $ADAppObject[0].ApplicationId } #create new self-signed certificate $selfsignedCertificate = [ActiveDirectoryHelper]::NewSelfSignedCertificate($this.AutomationAccount.AzureADAppName,$this.certificateDetail.CertStartDate,$this.certificateDetail.CertEndDate,$this.certificateDetail.Provider) #create password $secureCertPassword = [Helpers]::NewSecurePassword() $pfxFilePath = $env:TEMP+ "\temp.pfx" Export-PfxCertificate -Cert $selfsignedCertificate -Password $secureCertPassword -FilePath $pfxFilePath | Out-Null $publicCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(,$selfsignedCertificate.GetRawCertData()) #Authenticating AAD App service principal with newly created certificate credential [ActiveDirectoryHelper]::UpdateADAppCredential($appID,$publicCert,$this.certificateDetail.CredStartDate,$this.certificateDetail.CredEndDate,"False") $thumbPrint = $publicCert.thumbPrint #create certificate asset $newCertificateAsset = $this.NewCCCertificate($pfxFilePath,$secureCertPassword) # Remove existing connection $this.RemoveCCAzureRunAsAccount() # Create a Automation connection asset named AzureRunAsConnection in the Automation account. This connection uses the updated service principal. $newConnectionAsset = $this.NewCCConnection($appID,$thumbPrint) $this.OutputObject.AzureADAppName = $this.AutomationAccount.AzureADAppName $this.OutputObject.AzureRunAsConnection = $newConnectionAsset | Select-Object Name,Description,ConnectionTypeName $this.OutputObject.AzureRunAsCertificate = $newCertificateAsset | Select-Object Name,Description,CreationTime,ExpiryTime,LastModifiedTime } catch { $this.PublishCustomMessage("There was an error while updating credentials for CA AAD application. You may not have the Owner permission on it."); throw; } finally { #cleanup pfx file if($pfxFilePath) { Remove-Item -Path $pfxFilePath -Force -ErrorAction SilentlyContinue } #cleanup certificate $CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreName]::My,[System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser) $CertStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) if($thumbPrint) { $tempCert = $CertStore.Certificates.Find("FindByThumbprint",$thumbPrint,$FALSE) if($tempCert) { $CertStore.Remove($tempCert[0]) } } } } hidden [PSObject] NewCCConnection($appId,$thumbPrint) { $tenantID = (Get-AzureRmContext -ErrorAction Stop).Tenant.Id $ConnectionFieldValues = @{"ApplicationId" = $appID; "TenantId" = $tenantID; "CertificateThumbprint" = $thumbPrint; "SubscriptionId" = $this.SubscriptionContext.SubscriptionId} $newConnectionAsset = New-AzureRmAutomationConnection -ResourceGroupName $this.AutomationAccount.ResourceGroup ` -AutomationAccountName $this.AutomationAccount.Name -Name $this.connectionAssetName -ConnectionTypeName AzureServicePrincipal ` -ConnectionFieldValues $ConnectionFieldValues -Description "This connection authenticates runbook with service principal" -ErrorAction stop return $newConnectionAsset } hidden [PSObject] NewCCCertificate($pfxFilePath,$secureCertPassword) { $newCertificateAsset = New-AzureRmAutomationCertificate -ResourceGroupName $this.AutomationAccount.ResourceGroup -AutomationAccountName $this.AutomationAccount.Name ` -Path $pfxFilePath -Name $this.certificateAssetName -Password $secureCertPassword -Exportable -ErrorAction Stop return $newCertificateAsset } #endregion #region: Common internal functions hidden [PSObject] CheckContinuousAssuranceStorage() { #check if tags exist $existingStorage = Find-AzureRmResource -TagName "AzSDKFeature" -TagValue "ContinuousAssuranceStorage" -ExpandProperties| Where-Object {$_.ResourceType -eq "Microsoft.Storage/storageAccounts" -and $_.ResourceGroupName -eq $this.AutomationAccount.ResourceGroup} if(($existingStorage|Measure-Object).Count -eq 0) { #check from name $existingStorage = Find-AzureRmResource -ResourceGroupNameEquals $this.AutomationAccount.ResourceGroup -ResourceNameContains "azsdk" -ResourceType "Microsoft.Storage/storageAccounts" } if(($existingStorage|Measure-Object).Count -gt 1) { throw("Multiple storage accounts found in resource group [$($this.AutomationAccount.ResourceGroup)]. This is not expected."+ " Please backup the required logs/reports from storage. Delete resource group [$($this.AutomationAccount.ResourceGroup)] and install latest version using command: '"+$this.installCommandName+"'.") } return $existingStorage } #endregion } |