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");
                $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 subscription. If you want to update existing account, please run command '"+$this.updateCommandName+"' with required parameters")
                        }
                    }
                }
                else
                {
                    #update tags to existing RG
                    
                    $this.PublishCustomMessage("Started setting up Automation Account for Continuous Assurance");

                    $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 Storage Account ["+ $this.UserConfig.StorageAccountName +"], it will be used to store Continuous Assurance 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 Continuous Assurance 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 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
                } 
            }
            
            $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 setting up Automation Account for Continuous Assurance")
            $messages += [MessageData]::new("Below resources are created in resource group ["+$this.AutomationAccount.ResourceGroup+"] as part of Continuous Assurance",$this.OutputObject)
        }
        catch
        {
            #cleanup if exception occurs
            if($this.cleanupFlag)
            {
                $this.PublishCustomMessage("Error occured. Rolling back the changes.")
                #$this.DeleteResourceGroup($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
                }
                if(!$this.isExistingADApp)
                {
                    #clean AD App only if AD App was newly created
                    $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
                    }
                }
            }
        
            $this.PublishException($_)
        }

        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
               }
            $this.UpdateVariable($varOmsWSID)
            $this.PublishCustomMessage("Updating variable ["+$varOmsWSID.Name+"]")
        }
        if(![string]::IsNullOrWhiteSpace($OMSSharedKey))
        {
            $varOMSSharedKey = [Variable]@{
            Name = "OMSSharedKey";
            Value = $OMSSharedKey;
            IsEncrypted = $true
            }
            $this.UpdateVariable($varOMSSharedKey)
            $this.PublishCustomMessage("Updating variable ["+$varOMSSharedKey.Name+"]")
        }
        if(![string]::IsNullOrWhiteSpace($ResourceGroupNames))
        {
            $varAppRG = [Variable]@{
            Name = "AppResourceGroupNames";
            Value = $ResourceGroupNames;
            IsEncrypted = $false
            }
            $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 = @();
        $messages += [MessageData]::new("This command will delete resources in your subscription which were installed by AzSDK Continuous Assurance")
        $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()
    {
        $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 azure active directory application - ["+ $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 azure active directory 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")
                New-AzureRMADServicePrincipal -ApplicationId $ADApplication.ApplicationId -ErrorAction Stop | Out-Null                            
            }
            $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("Adding service principal to Reader RBAC role at subscription level and Contributor RBAC role at "+ $this.AutomationAccount.ResourceGroup +" resource group level")
            $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("Adding certificate - ["+ $this.certificateAssetName +"] and connection - ["+ $this.connectionAssetName +"] in Automation Account")

            $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
        }        
        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)
    {            
        $updatedVariable = Set-AzureRmAutomationVariable -Name $VariableObj.Name `
        -Encrypted $VariableObj.IsEncrypted `
        -Value $VariableObj.Value `
        -ResourceGroupName $this.AutomationAccount.ResourceGroup `
        -AutomationAccountName $this.AutomationAccount.Name -ErrorAction Stop 
        
        $this.OutputObject.Variables += ($updatedVariable | 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()
    {
        $pfxFilePath = $null
        $thumbPrint = $null
        try
        {
            #fetch existing AD App used in connection
            
            $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
        
            #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
        }        
        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-AzureRmSubscription -SubscriptionId $this.SubscriptionContext.SubscriptionId -ErrorAction Stop).TenantId
        $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 '"+$this.installCommandName+"' command with required parameters.")    
        }
        return $existingStorage
    }
    #endregion
}