Framework/Core/InClusterCA/ContinuousAssuranceForKubernetes.ps1

class KubernetesClusterCA : AzCommandBase {

    [PSObject] $ResourceObject;
    [string]  $ResourceGroupName;
    [string]  $ResourceName;
    [string]  $LAWorkspaceId;
    [string]  $LASharedSecret;
    [string]  $ResourceType = "Microsoft.ContainerService/managedClusters";
    [string]  $nameSpace = "azsk-scanner"
    [string]  $serviceAccountName = "azsk-scanner-account"
    [string]  $clusterRoleName = "azsk-scanner-clusterrole"
    [string]  $configMapName = "azsk-config"
    [string]  $clusterRoleBindingName = "azsk-scanner-rolebinding"
    [string]  $cronJobName = "azsk-ca-job"
    [string]  $deploymentFileBaseUrl = ""
    [string]  $deploymentFileName =  "deploy_azsk_ca_job.yml"
    [string]  $configMapFileName =  "update_azsk_ca_job_configmap.yml"
    [string]  $runtimeAccountFileName = "update_azsk_ca_job_runtime.yml"
    [string]  $jobSpecificationsFileName = "update_azsk_ca_job_spec.yml"

    KubernetesClusterCA(
        [string] $subscriptionId, `
        [string] $ResourceGroupName, `
        [string] $ResourceName, `
        [InvocationInfo] $invocationContext) : Base($subscriptionId, $invocationContext)
        {
            if(-not [string]::IsNullOrWhiteSpace($ResourceGroupName))
            {
                $this.ResourceGroupName = $ResourceGroupName;
            }

            if(-not [string]::IsNullOrWhiteSpace($ResourceName))
            {
                $this.ResourceName = $ResourceName;
            }

            $this.CheckPrerequisites();
            $this.GetResourceObject();
            $this.SetKubernetesContext();
            $this.deploymentFileBaseUrl = [Constants]::AKSBaseConfigurationUrl
        }

        hidden [bool] CheckPrerequisites()
        {
            # This method verifies that if both Azure CLI and Kubernetes CLI are installed or not
            $IsPrerequisitesPresent = $false
            try{
                $azCliCmdOutput  = az --help --output json
            }catch{
                $azCliCmdOutput = $null
            }
            
            try{
                $aksCliCmdOuput = kubectl help
            }catch{
                $aksCliCmdOuput = $null
            }
            

            if($null -ne $azCliCmdOutput -and $null -ne $aksCliCmdOuput ){
                $IsPrerequisitesPresent = $true
            }else{
                if($null -eq $azCliCmdOutput){
                    $IsPrerequisitesPresent = $false
                    $this.PublishCustomMessage("Azure CLI is not presnet in machine, you need to install Azure CLI. To install, please refer: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest", [MessageType]::Warning)         
                }
                if($null -eq $aksCliCmdOuput){
                    $IsPrerequisitesPresent = $false
                    $this.PublishCustomMessage( "The Kubernetes command-line client 'kubectl' is not present in machine, it is required to connect to the Kubernetes cluster. To install, please run command: 'az aks install-cli' ", [MessageType]::Warning)
                }
            }
            # If any one of Azure CLI and Kubernetes CLI are not present in Users Machine, throw error
            if(-not  $IsPrerequisitesPresent){
                throw ([SuppressedException]::new(("Both Azure CLI and Kubernetes client 'kubectl' must be present in machine to perform any operation."), [SuppressedExceptionType]::InvalidOperation))
            }

            return $IsPrerequisitesPresent

        }
    
        hidden [PSObject] GetResourceObject()
        {
            if (-not $this.ResourceObject)
            {
                # Get App Service details
                $this.ResourceObject = Get-AzResource -Name $this.ResourceName  `
                                                                                    -ResourceType $this.ResourceType `
                                                                                    -ResourceGroupName $this.ResourceGroupName
                if(-not $this.ResourceObject)
                {
                    throw ([SuppressedException]::new(("Resource '$($this.ResourceName)' not found under Resource Group '$($this.ResourceGroupName)'"), [SuppressedExceptionType]::InvalidOperation))
                }

            }
            return $this.ResourceObject;
        }

        [Void] InstallKubernetesContinuousAssurance($LAWorkspaceId, $LASharedSecret)
        {
            # Get the App Insight Key
            $AppInsightKey = [RemoteReportHelper]::GetAIOrgTelemetryKey()
            if([string]::IsNullOrWhiteSpace($AppInsightKey))
            {
                $AppInsightKey = 'None'
            }else
            {
                $AppInsightKey = $AppInsightKey.Trim()
            }
            
            # Check if LAWorkspaceId and LASharedSecret param are passed by user
            if(-not [string]::IsNullOrWhiteSpace($LAWorkspaceId) -and -not [string]::IsNullOrWhiteSpace($LASharedSecret))
            {
                $this.LAWorkspaceId = $LAWorkspaceId
                $this.LASharedSecret = $LASharedSecret
            }else{
               # Get values from local settings file
                $this.GetLASettings()
            }
           
            # Check if LA ID and Keys are non-empty

            if([string]::IsNullOrWhiteSpace($this.LAWorkspaceId))
            {
                $this.LAWorkspaceId = 'None'
            }else
            {
                $this.LAWorkspaceId = $this.LAWorkspaceId.Trim()
            }

            if([string]::IsNullOrWhiteSpace($this.LASharedSecret))
            {
                $this.LASharedSecret = 'None'
            }else
            {
                $this.LASharedSecret = $this.LASharedSecret.Trim()
            }

            # Prepare job schedule
            $Schedule = '"0 #h#/24 * * *"' 
            $jobHrs = ((Get-Date).ToUniversalTime().Hour + 1)%24
            $Schedule = $Schedule -replace '\#h\#', $jobHrs
            
            # Download deployment file from server and store it in temp location
            $deploymentFileUrl =  $this.deploymentFileBaseUrl + $this.deploymentFileName
            $InClusterCATempFolderPath = Join-Path $([Constants]::AzSKTempFolderPath) "InClusterCA";
            if(-not (Test-Path -Path $InClusterCATempFolderPath -PathType Container)){
                New-Item -ItemType Directory -Force -Path $InClusterCATempFolderPath
            }
            $filePath = Join-Path $($InClusterCATempFolderPath)  $($this.deploymentFileName)
            Invoke-RestMethod  -Method Get -Uri $deploymentFileUrl -OutFile $filePath 
            
            # Bootstrap basic properties like App Insight Key and job schedule in deployment file
            
            (Get-Content $filePath) -replace '\#AppInsightKey\#', $AppInsightKey | Set-Content $filePath -Force
            (Get-Content $filePath) -replace '\#Schedule\#', $Schedule | Set-Content $filePath -Force
            (Get-Content $filePath) -replace '\#RGName\#', $this.ResourceGroupName | Set-Content $filePath -Force
            (Get-Content $filePath) -replace '\#ResourceName\#', $this.ResourceName | Set-Content $filePath -Force
            (Get-Content $filePath) -replace '\#SubscriptionID\#', $this.SubscriptionContext.SubscriptionId | Set-Content $filePath -Force
            (Get-Content $filePath) -replace '\#LAWSId\#', $this.LAWorkspaceId | Set-Content $filePath -Force
            (Get-Content $filePath) -replace '\#LAWSSharedKey\#', $this.LASharedSecret | Set-Content $filePath -Force   

            # create ca scan job
            
            $this.PublishCustomMessage("Setting up AzSK Continuous Assurance in Kubernetes cluster...", [MessageType]::Warning)
        
            kubectl apply -f $filePath
            
            # Check if job created successfully
            
            $jobName = kubectl get cronjob $this.cronJobName --namespace $this.nameSpace -o jsonpath='{.metadata.name}' 
            if($null -ne $jobName)
            {
                $this.PublishCustomMessage("AzSK Continuous Assurance setup completed.", [MessageType]::Update)
                $this.PublishCustomMessage("Your cluster will be scanned periodically by AzSK CA.", [MessageType]::Warning)
                $this.PublishCustomMessage("The first CA scan job will be triggered within next 60 mins. You can check control evaluation results (job logs of 'azsk-ca-job') after that.", [MessageType]::Warning)
                $this.PublishCustomMessage("All security control evaluation results will also be sent to App Insight if an instrumentation key was provided during setup above.", [MessageType]::Warning)
                $this.PublishCustomMessage("For more info, please see docs: https://aka.ms/devopskit/inclusterca", [MessageType]::Warning)
            }else
            {
                $this.PublishCustomMessage("AzSK Continuous Assurance setup could not be completed. Please check error logs above for next steps." , [MessageType]::Error)
            }
            
        }
        
        [Void] SetKubernetesContext()
        {
            # Try to read existing context present in local
            try
            {
                $contexts = kubectl config view -o jsonpath='{.contexts[*].name}'
                $contextList = $contexts.Split("")
                Write-Host "Found the following cluster contexts in your local setup:" -ForegroundColor "Yellow"
                $contextList | ForEach-Object {
                    Write-Host $_
                        }
                $choice = Read-Host -Prompt 'To use one of the contexts above enter "Y". To use/create a new context enter "N"'
                if($choice -eq "Y")
                {
                    $contextName = Read-Host -Prompt "Enter the name of the context for setting up CA"
                    kubectl config use-context $contextName
                }
            }
            catch
            {
                # If any error occur while reading existing context, try to fetch new credentials and set context
                $choice = "N"
            }
            
            if($choice -ne "Y")
            {
                # Login

                az login *> $null
                
                # Set Subscription Context
            
                az account set --subscription $this.SubscriptionContext.SubscriptionId

                # Fetch credentails of the cluster in which ca scan need to be setup

                az aks get-credentials --resource-group $this.ResourceGroupName --name $this.ResourceName
            }
        
        }
        
        [void] GetLASettings() {
            # Get LA settings if not passed
            [LogAnalyticsHelper]::SetLAWSDetails()
            $settings = [ConfigurationManager]::GetAzSKSettings()
            $this.LAWorkspaceId = $settings.LAWSId
            $this.LASharedSecret = $settings.LAWSSharedKey
        }

        [Void] RemoveKubernetesContinuousAssurance($DownloadJobLogs, $Force)
        {

            $deploymentFileUrl =  $this.deploymentFileBaseUrl + $this.deploymentFileName
            
            $choice = "N"
            if(-not $Force){
                $choice = Read-Host -Prompt 'This action will remove AzSK Continuous Assurance from cluster. Do you want to continue "Y/N"'
            }
            else{
                $choice = "Y"
            }
            if($choice -eq "Y"){
            
                # download all logs to local before deleting job
            
                if($DownloadJobLogs -eq "Yes"){
                    $response = kubectl get pods --namespace $this.nameSpace -o json
                    if($null -ne $response){
                        $allJObPods = $response | ConvertFrom-Json | Select-Object -ExpandProperty items
                        $jobPodsCount =    ($allJObPods | Measure-Object).Count
                        if($jobPodsCount -gt 0){
                            $moduleName = $this.GetModuleName();
                            $this.PublishCustomMessage("Downloading existing job's log to local machine..." , [MessageType]::Warning)
                            $baseFolder = Join-Path $([Constants]::AzSKAppFolderPath) "Logs" | Join-Path -ChildPath "ClusterScans\Kubernetes"| Join-Path -ChildPath $($this.SubscriptionContext.SubscriptionId )| Join-Path -ChildPath $($this.ResourceGroupName)| Join-Path -ChildPath $($this.ResourceName)
                            If(!(test-path $baseFolder))
                            {
                                New-Item -ItemType Directory -Force -Path $baseFolder | Out-Null
                            }
                            $allJObPods | ForEach-Object {
                            $jobPodName = $_.metadata.name
                            $jobTimeStamp = $_.metadata.creationTimestamp
                            $dateTime = $jobTimeStamp.split("T")
                            $date = $dateTime[0]
                            $time = $dateTime[1]
                            $fileName = $date.Replace("-","")+"_"+ $time.Replace(":","") + ".txt"
                            $fileName = Join-Path $baseFolder $fileName
                            Kubectl logs $jobPodName --namespace $this.nameSpace| Out-File $fileName -Force
                            }
                            $this.PublishCustomMessage("All previous job logs have been exported to: $($baseFolder)" , [MessageType]::Update)
                        }else{
                            $this.PublishCustomMessage("No previous job's log found." , [MessageType]::Warning)
                        }
                    }else{
                        $this.PublishCustomMessage("Some error occurred while fetching previous job logs." , [MessageType]::Error)
                        $choice = Read-Host -Prompt 'Do you want to remove AzSK Continuous Assurance from cluster without downloading previous logs "Y/N"'
                        if($choice -ne "Y"){
                            return;
                        }
                    }

                }
                else
                {
                    $this.PublishCustomMessage("You have choosen not to download previous logs." , [MessageType]::Warning)
                }
            
                $this.PublishCustomMessage("Removing AzSK Continuous Assurance from cluster..."  , [MessageType]::Warning)
            
                $response = kubectl delete -f $deploymentFileUrl

                if($null -ne $response){
                    $this.PublishCustomMessage("Successfully removed AzSK Continuous Assurance from cluster."  , [MessageType]::Update)
                }else{
                    $this.PublishCustomMessage("Some error occurred while removing AzSK Continuous Assurance from cluster." , [MessageType]::Error)
                }
            
            }
            
        }
        
        [Void] GetKubernetesContinuousAssurance()
        {
            $this.PublishCustomMessage("`nCheck 01: Presence of required configuration settings.",[MessageType]::Info)
            $configJson = kubectl get configmaps $this.configMapName --namespace $this.nameSpace -o json
            if($null -ne $configJson){
                $configJson = $configJson| ConvertFrom-Json | Select-Object data
                $configMaps = $configJson.data
                $presentConfig = $null
                if($null -eq $configMaps){     
                    $this.PublishCustomMessage("`nConfiguration settings for your cluster are missing.",[MessageType]::Error)
                }else{
                        $presentConfig = @{
                        "AppInsightKey " = $configMaps.APP_INSIGHT_KEY
                        "SubscriptionId " = $configMaps.SUBSCRIPTION_ID
                        "ResourceName " = $configMaps.RESOURCE_NAME
                        "ResourceGroupName" = $configMaps.RG_NAME
                        }
                        $this.PublishCustomMessage("Configuration settings for your cluster are as follows:")
                        foreach($key in $presentConfig.keys)
                        {
                            Write-Host "$($key) : $($presentConfig.Item($key))" -ForegroundColor Yellow
                        }
                }
            
                if($null -eq $configMaps -or $null -eq $configMaps.APP_INSIGHT_KEY){
                    $this.PublishCustomMessage("`nApplication Inight key is not present. Scan logs will not be sent to telelmetry.",[MessageType]::Warning)
                }else{
                    $this.PublishCustomMessage("`nScan logs will be sent to Application Insight: $($configMaps.APP_INSIGHT_KEY)",[MessageType]::Update)
                }

                if($null -eq $configMaps -or $null -eq $configMaps.LA_WS_ID -or $null -eq $configMaps.LA_WS_SHAREDKEY){
                    $this.PublishCustomMessage("`nLA Workspace details are not present. Scan logs will not be sent to LA workspace.",[MessageType]::Warning)
                }else{
                    $this.PublishCustomMessage("`nScan logs will be sent to LA workspace: $($configMaps.LA_WS_ID)",[MessageType]::Update)
                }

            }else{
                $this.PublishCustomMessage("Unable to fetch Cluster's Configuration settings.",[MessageType]::Error)
            }

        
            $this.PublishCustomMessage("`nCheck 02: Validate runtime permissions to scan cluster.",[MessageType]::Info)
            $isRuntimeAccountValid = $true
            $svcAccountJson = kubectl get ServiceAccount $this.serviceAccountName --namespace $this.nameSpace -o json
            if($null -ne $svcAccountJson){
                $svcAccountJson = $svcAccountJson | ConvertFrom-Json | Select-Object metadata
                $svcAccountUID = $svcAccountJson.metadata.uid
                if($null -eq $svcAccountUID){
                    $this.PublishCustomMessage("Service account is not present.",[MessageType]::Error)
                    $isRuntimeAccountValid = $false
                }
            }else{
                $this.PublishCustomMessage("Unable to fetch Service account details.",[MessageType]::Error)
                $isRuntimeAccountValid = $false
            }
         
        
            $requiredApiGroups = "*"
            $requiredResources = @("pods","deployments","nodes","serviceaccounts","configmaps","clusterrolebindings")
            $requiredVerbs = @("get","watch","list")
        
            $clusterRoleJson = kubectl get ClusterRole $this.clusterRoleName --namespace $this.nameSpace -o json
            if($null -ne $clusterRoleJson){
                $clusterRoleJson = $clusterRoleJson | ConvertFrom-Json | Select-Object rules
                $clusterRoleRules = $clusterRoleJson.rules
            
                if(Compare-Object $clusterRoleRules.resources $requiredResources){
                    $this.PublishCustomMessage("Required resources permission is not correctly configured.",[MessageType]::Error)
                    $isRuntimeAccountValid = $false
                }
                if(Compare-Object $clusterRoleRules.verbs  $requiredVerbs){
                    $this.PublishCustomMessage("Required verbs permission is not correctly configured.",[MessageType]::Error)
                    $isRuntimeAccountValid = $false
                }
                if(Compare-Object $clusterRoleRules.apiGroups  $requiredApiGroups){
                    $this.PublishCustomMessage("Required api groups permission is not correctly configured.",[MessageType]::Error)
                    $isRuntimeAccountValid = $false
                }
            }else{
                $this.PublishCustomMessage("Unable to fetch Cluster role details.",[MessageType]::Error)
                $isRuntimeAccountValid = $false
            }

        
            $roleBindingJson = kubectl get ClusterRoleBinding $this.clusterRoleBindingName --namespace $this.nameSpace -o json
            if($null -ne  $roleBindingJson){
                $roleBindingJson  = $roleBindingJson  | ConvertFrom-Json | Select-Object roleRef, subjects
                $roleRef = $roleBindingJson.roleRef
                $subjects = $roleBindingJson.subjects
            
                if($roleRef.kind -ne "ClusterRole" -or $roleRef.name -ne $this.clusterRoleName ){
                    $this.PublishCustomMessage("Required Cluster role binding is not properly configured. ",[MessageType]::Error)
                    $isRuntimeAccountValid = $false
                }
            
                if($subjects.kind -ne "ServiceAccount" -or $subjects.name -ne $this.serviceAccountName){
                    $this.PublishCustomMessage("Required Cluster role binding is not properly configured. ",[MessageType]::Error)
                    $isRuntimeAccountValid = $false
                }
            }else{
                $this.PublishCustomMessage("Unable to fetch Cluster role details.",[MessageType]::Error)
                $isRuntimeAccountValid = $false
            }

        
            if($isRuntimeAccountValid){
                $this.PublishCustomMessage("Required runtime account for scanning cluster is properly configured.", [MessageType]::Update)
            }else{
                $this.PublishCustomMessage("Required runtime account for scanning cluster is not properly configured.",[MessageType]::Error)
            }
        
            $this.PublishCustomMessage("`ncheck 03: Check if latest image is used.",[MessageType]::Info)
            $controlSettings = $this.LoadServerConfigFile("ControlSettings.json");
            $requiredImagePath = $controlSettings.InClusterCA.AKS.RequiredImagePath
            $reuiredImageTag = $controlSettings.InClusterCA.AKS.RequiredImageTag
            $requiredImage = $requiredImagePath + ":"+ $reuiredImageTag
            $cronJobJson = kubectl get CronJob $this.cronJobName --namespace $this.nameSpace -o json | ConvertFrom-Json
        
            if($null -ne $cronJobJson){
                $jobContainer = $cronJobJson.spec.jobTemplate.spec.template.spec.containers[0]
        
                # Get image
                if($jobContainer.image -ne $requiredImage)
                {
                    $this.PublishCustomMessage("Job is not running with latest image.",[MessageType]::Error)
                    
                }else{
                    $this.PublishCustomMessage("Job is running with latest image." , [MessageType]::Update)
                }
            }else{
                $this.PublishCustomMessage("AzSK CA job is not present in cluster.",[MessageType]::Error)
                # return if job not found, because other check depends on CA job specification
                return
            }
        
            $this.PublishCustomMessage("`nCheck 04: Job scan interval.",[MessageType]::Info)
            # Get last job, schedule and logs retention
            $jobSpec = $cronJobJson.spec
            $jobSchedule = $jobSpec.schedule
            if(-not [string]::IsNullOrWhiteSpace($jobSchedule)){
            $startIndex = $jobSchedule.IndexOf("/")
            $lastIndex = $jobSchedule.IndexOf("*")
            $jobSchedule = $jobSchedule.Substring($startIndex+1, $lastIndex - $startIndex-1)
        
            }else{
                $jobSchedule = "Not Found"
            }
            $this.PublishCustomMessage("Current scan interval for job is: $($jobSchedule)" , [MessageType]::Update)
            
            $this.PublishCustomMessage("`nCheck 05: Job's recent schdeule.",[MessageType]::Info)
            $lastScheduleTime = "NA"
            if([Helpers]::CheckMember($cronJobJson,"status.lastScheduleTime")){
                $lastScheduleTime = $cronJobJson.status.lastScheduleTime
            }
            $this.PublishCustomMessage("Job's last scheduled time: $($lastScheduleTime)"  , [MessageType]::Update)
            
            $this.PublishCustomMessage("`nCheck 06: Log retention.",[MessageType]::Info)
            $this.PublishCustomMessage("Log retention period: $($jobSpec.successfulJobsHistoryLimit)" , [MessageType]::Update)
        }
        
        [Void] UpdateKubernetesContinuousAssurance($AppInsightKey, $LAWorkspaceId, $LASharedSecret, $FixRuntimeAccount, $LogRetentionInDays,  $ScanIntervalInHours, $ImageTag)
        {
            $updateConfigDetails = -not ( [String]::IsNullOrEmpty($LAWorkspaceId) -and [String]::IsNullOrEmpty($AppInsightKey) -and [String]::IsNullOrEmpty($LASharedSecret))
        
            if($updateConfigDetails){
            
                $configJson = kubectl get configmaps $this.configMapName --namespace $this.nameSpace -o json
                if($null -ne $configJson){
                    $configJson = $configJson| ConvertFrom-Json | Select-Object data
                    $configMaps = $configJson.data
                    
                    if(-not [String]::IsNullOrEmpty($AppInsightKey)){
                        $newAppInsightKey = $AppInsightKey
                    }elseif ($null -ne $configMaps -and $null -ne $configMaps.APP_INSIGHT_KEY) {
                        $newAppInsightKey =   $configMaps.APP_INSIGHT_KEY
                    }
                    else{        
                        $newAppInsightKey = 'None'
                    }
                    
                    if(-not [String]::IsNullOrEmpty($LAWorkspaceId)){
                        $newLAWSId = $LAWorkspaceId
                    }elseif ($null -ne $configMaps -and $null -ne $configMaps.LA_WS_ID) {
                        $newLAWSId =   $configMaps.LA_WS_ID
                    }
                    else{        
                        $newLAWSId = 'None'
                    }

                    if(-not [String]::IsNullOrEmpty($LASharedSecret)){
                        $newLAWSKey = $LAWorkspaceId
                    }elseif ($null -ne $configMaps -and $null -ne $configMaps.LA_WS_SHAREDKEY) {
                        $newLAWSKey =   $configMaps.LA_WS_SHAREDKEY
                    }
                    else{        
                        $newLAWSKey = 'None'
                    }

                    # Download deployment file from server and store it in temp location
                    $deploymentFileUrl =  $this.deploymentFileBaseUrl +  $this.configMapFileName
                    $InClusterCATempFolderPath = Join-Path $([Constants]::AzSKTempFolderPath) "InClusterCA";
                    if(-not (Test-Path -Path $InClusterCATempFolderPath -PathType Container)){
                        New-Item -ItemType Directory -Force -Path $InClusterCATempFolderPath
                    }

                    $filePath = Join-Path $($InClusterCATempFolderPath) $this.configMapFileName
                    Invoke-RestMethod  -Method Get -Uri $deploymentFileUrl -OutFile $filePath 
                    (Get-Content $filePath) -replace '\#AppInsightKey\#', $newAppInsightKey | Set-Content $filePath -Force
                    (Get-Content $filePath) -replace '\#RGName\#', $this.ResourceGroupName | Set-Content $filePath -Force
                    (Get-Content $filePath) -replace '\#ResourceName\#', $this.ResourceName | Set-Content $filePath -Force
                    (Get-Content $filePath) -replace '\#SubscriptionID\#', $this.SubscriptionContext.SubscriptionId | Set-Content $filePath -Force
                    (Get-Content $filePath) -replace '\#LAWSId\#', $newLAWSId | Set-Content $filePath -Force
                    (Get-Content $filePath) -replace '\#LAWSSharedKey\#', $newLAWSKey | Set-Content $filePath -Force  
                    
                    # update ca config maps like App Insight key
                    $this.PublishCustomMessage("Upadting App Insight Key...",[MessageType]::Warning)
                    $response = kubectl apply -f $filePath
                    if($null -ne $response){
                        $this.PublishCustomMessage("Successfully updated App Insight Key.",[MessageType]::Update)   
                    }else{
                        $this.PublishCustomMessage("Some error occurred while updating App Insight Key. See logs above for details.",[MessageType]::Error)
                    }
                }else{
                    $this.PublishCustomMessage("Some error occurred while updating App Insight Key/Log Analytics workspace details. See logs above for details.",[MessageType]::Error)
                }

              
                      
            }
            
            if($FixRuntimeAccount -eq $true){
        
                # Download deployment file from server and store it in temp location
                $deploymentFileUrl =  $this.deploymentFileBaseUrl +  $this.runtimeAccountFileName
                $InClusterCATempFolderPath = Join-Path $([Constants]::AzSKTempFolderPath) "InClusterCA";
                if(-not (Test-Path -Path $InClusterCATempFolderPath -PathType Container)){
                    New-Item -ItemType Directory -Force -Path $InClusterCATempFolderPath
                }
                $filePath = Join-Path $($InClusterCATempFolderPath) $this.runtimeAccountFileName
                Invoke-RestMethod  -Method Get -Uri $deploymentFileUrl -OutFile $filePath 
                # update ca scan job runtime account
                $this.PublishCustomMessage("Upadting AzSK Continuous Assurance job runtime account...",[MessageType]::Warning)
                $response = kubectl apply -f $filePath
                if($null -ne $response){
                    $this.PublishCustomMessage("Successfully Upadted AzSK Continuous Assurance job runtime account.",[MessageType]::Update)
                }else{
                    $this.PublishCustomMessage("Some error occurred while updating AzSK Continuous Assurance job runtime account. See logs above for details.",[MessageType]::Error)
                }

            }
            
            if($LogRetentionInDays -gt 0 -or $ScanIntervalInHours -gt 0 -or -not [string]::IsNullOrEmpty($ImageTag)){
                    # Upadte scan interval (if provided, deafult is 24)
                    $Schedule = '"0 #h#/#i# * * *"' 
                    $jobHrs = ((Get-Date).ToUniversalTime().Hour + 1)%24
                    $Schedule = $Schedule -replace '\#h\#', $jobHrs
                    # Update job history limit (if provided , deafult is 7)
                    if($LogRetentionInDays -gt 0){
                        $JobHistoryLimit = $LogRetentionInDays
                    }else{
                        $JobHistoryLimit = 7 
                    }
            
                    if($ScanIntervalInHours -gt 0){
                        $Schedule = $Schedule -replace '\#i\#', $ScanIntervalInHours
                    }else{
                        $Schedule = $Schedule -replace '\#i\#', 24 
                    }
                    
                    $controlSettings = $this.LoadServerConfigFile("ControlSettings.json");
                    $requiredImagePath = $controlSettings.InClusterCA.AKS.RequiredImagePath
                    $reuiredImageTag = $controlSettings.InClusterCA.AKS.RequiredImageTag

                    if([string]::IsNullOrEmpty($ImageTag)){
                        $ImagePath =  $requiredImagePath + ":" + $reuiredImageTag
                    }else{
                        $ImagePath =  $requiredImagePath + ":" + $ImageTag
                    }
            
                    # Download deployment file from server and store it in temp location
                    $deploymentFileUrl =  $this.deploymentFileBaseUrl +  $this.jobSpecificationsFileName
                    $InClusterCATempFolderPath = Join-Path $([Constants]::AzSKTempFolderPath) "InClusterCA";
                    if(-not (Test-Path -Path $InClusterCATempFolderPath -PathType Container)){
                        New-Item -ItemType Directory -Force -Path $InClusterCATempFolderPath
                    }
                    $filePath = Join-Path $($InClusterCATempFolderPath) $this.jobSpecificationsFileName
                    Invoke-RestMethod  -Method Get -Uri $deploymentFileUrl -OutFile $filePath 
                    (Get-Content $filePath) -replace '\#Schedule\#', $Schedule | Set-Content $filePath -Force
                    (Get-Content $filePath) -replace '\#JobHistoryLimit\#', $JobHistoryLimit | Set-Content $filePath -Force
                    (Get-Content $filePath) -replace '\#ImagePath\#', $ImagePath | Set-Content $filePath -Force
                    # update ca job configuration like scan interval, job history limit etc.
                    $this.PublishCustomMessage("Upadting AzSK Continuous Assurance job configuration...",[MessageType]::Warning)
                    $response = kubectl apply -f $filePath
                    if($null -ne $response){
                        $this.PublishCustomMessage("Successfully Upadted AzSK Continuous Assurance job configuration.",[MessageType]::Update)
                    }else{
                        $this.PublishCustomMessage("Some error occurred while updating AzSK Continuous Assurance job configuration. See logs above for details.",[MessageType]::Error)
                    }
                  
            
            }
        }
}