Framework/Configurations/ContinuousAssurance/Continuous_Assurance_Runbook.ps1
#Telemetry functions -- start here function SetCommonProperties([psobject] $EventObj) { $notAvailable = "NA" $eventObj.data.baseData.properties.Add("JobId",$PsPrivateMetaData.JobId.Guid) $eventObj.data.baseData.properties.Add("SubscriptionId",$RunAsConnection.SubscriptionID) } function GetEventBaseObject([string] $EventName) { $eventObj = "" | Select-Object data, iKey, name, tags, time $eventObj.iKey = $telemetryKey $eventObj.name = "Microsoft.ApplicationInsights." + $telemetryKey.Replace("-", "") + ".Event" $eventObj.time = [datetime]::UtcNow.ToString("o") $eventObj.tags = "" | Select-Object ai.internal.sdkVersion $eventObj.tags.'ai.internal.sdkVersion' = "dotnet: 2.1.0.26048" $eventObj.data = "" | Select-Object baseData, baseType $eventObj.data.baseType = "EventData" $eventObj.data.baseData = "" | Select-Object ver, name, measurements, properties $eventObj.data.baseData.ver = 2 $eventObj.data.baseData.name = $EventName $eventObj.data.baseData.measurements = New-Object 'system.collections.generic.dictionary[string,double]' $eventObj.data.baseData.properties = New-Object 'system.collections.generic.dictionary[string,string]' return $eventObj; } function PublishEvent([string] $EventName, [hashtable] $Properties, [hashtable] $Metrics) { try { #return if telemetry key is empty if ([string]::IsNullOrWhiteSpace($telemetryKey)) { return; }; $eventObj = GetEventBaseObject -EventName $EventName SetCommonProperties -EventObj $eventObj if ($Properties -ne $null) { $Properties.Keys | ForEach-Object { try { if (!$eventObj.data.baseData.properties.ContainsKey($_)) { $eventObj.data.baseData.properties.Add($_ , $Properties[$_].ToString()) } } catch {#left blank intentionally } } } if ($Metrics -ne $null) { $Metrics.Keys | ForEach-Object { try { $metric = $Metrics[$_] -as [double] if (!$eventObj.data.baseData.measurements.ContainsKey($_) -and $metric -ne $null) { $eventObj.data.baseData.measurements.Add($_ , $Metrics[$_]) } } catch {#left blank intentionally } } } $eventJson = ConvertTo-Json $eventObj -Depth 100 -Compress Invoke-WebRequest -Uri "https://dc.services.visualstudio.com/v2/track" ` -Method Post ` -ContentType "application/x-json-stream" ` -Body $eventJson ` -UseBasicParsing | Out-Null } catch {#left blank intentionally } } #Telemetry functions -- end here #function to create one time temporary helper schedule function CreateHelperSchedule($nextRetryIntervalInMinutes) { #create next run schedule Get-AzureRmAutomationSchedule -AutomationAccountName $AutomationAccountName ` -ResourceGroupName $AutomationAccountRG -Name $CAHelperScheduleName -ErrorAction SilentlyContinue | Remove-AzureRmAutomationSchedule -Force New-AzureRmAutomationSchedule -AutomationAccountName $AutomationAccountName -Name $CAHelperScheduleName ` -ResourceGroupName $AutomationAccountRG -StartTime $(get-date).AddMinutes($nextRetryIntervalInMinutes) ` -OneTime -ErrorAction Stop | Out-Null Register-AzureRmAutomationScheduledRunbook -RunbookName $RunbookName -ScheduleName $CAHelperScheduleName ` -ResourceGroupName $AutomationAccountRG ` -AutomationAccountName $AutomationAccountName -ErrorAction Stop | Out-Null PublishEvent -EventName "CA Job Rescheduled" -Properties @{"IntervalInMinutes" = $nextRetryIntervalInMinutes} } #function to invoke script from server function InvokeScript($accessToken, $policyStoreURL,$fileName, $version) { [System.Uri] $validatedURI = $null; $URI = $global:ExecutionContext.InvokeCommand.ExpandString($policyStoreURL) $result = "Write-Host 'Error downloading the script'" if([System.Uri]::TryCreate($URI, [System.UriKind]::Absolute, [ref] $validatedURI)) { if($accessToken) { $retry = 3 while($retry -gt 0) { $retry = $retry - 1 $result = iwr $validatedUri -Headers @{"Authorization" = "Bearer $accessToken"} -UseBasicParsing if ($null -ne $result -and $result.StatusCode -ge 200 -and $result.StatusCode -le 399) { $retry = -1; } } } else { $retry = 3 while($retry -gt 0) { $retry = $retry - 1 $result = iwr $validatedUri -UseBasicParsing if ($null -ne $result -and $result.StatusCode -ge 200 -and $result.StatusCode -le 399) { $retry = -1; } } } iex $result; } } try { #start job timer $jobTimer = [System.Diagnostics.Stopwatch]::StartNew(); #----------------------------------Config start------------------------------------------------------------------ $automationAccountRG = "[#automationAccountRG#]" $automationAccountName="[#automationAccountName#]" $telemetryKey ="[#telemetryKey#]" $onlinePolicyStoreUrl = "[#onlinePolicyStoreUrl#]" $OSSPolicyStoreUrl = "[#OSSPolicyStoreUrl#]" $enableAADAuthForOnlinePolicyStore = "[#enableAADAuthForOnlinePolicyStore#]" $runbookCoreSetupScript = "RunbookCoreSetup.ps1" $runbookScanAgentScript = "RunbookScanAgent.ps1" $RunbookName = "Continuous_Assurance_Runbook" $CAHelperScheduleName = "CA_Helper_Schedule" #setting default AzSDK version $moduleVersion = "2.5.0" $azureRmResourceURI = "https://management.core.windows.net/" $RunAsConnection = Get-AutomationConnection -Name "AzureRunAsConnection" #-----------------------------------Config end------------------------------------------------------------------------- #-----------------------------------Telemetry script------------------------------------------------------------------- PublishEvent -EventName "CA Job Started" -Properties @{ "OnlinePolicyStoreUrl"=$OnlinePolicyStoreUrl; ` "AzureADAppId"=$RunAsConnection.ApplicationId } #------------------------------------Execute RunbookCoreSetup.ps1 to download required modules------------------------- #Login if(!$RunAsConnection) { throw "Connection AzureRunAsConnection not found." } try { "Logging in to Azure..." Add-AzureRmAccount ` -ServicePrincipal ` -TenantId $RunAsConnection.TenantId ` -ApplicationId $RunAsConnection.ApplicationId ` -CertificateThumbprint $RunAsConnection.CertificateThumbprint | Out-Null Set-AzureRmContext -SubscriptionId $RunAsConnection.SubscriptionID | Out-Null } catch { throw $_.Exception } #create helper schedule to run job again after 30 minutes in case online policy URL is down "Validating installed AzSDK version..." #Step 1: Get module version from installed AzSDK module $module = Get-AzureRmAutomationModule -ResourceGroupName $AutomationAccountRG ` -AutomationAccountName $AutomationAccountName | ` Where-Object { $_.Name -like "azsdk*"} if($null -ne $module -and $null -ne $module.Version) { $moduleVersion = $module.Version } else { $moduleVersion = (Get-AzureRmAutomationAccount -Name $automationAccountName -ResourceGroupName $automationAccountRG).Tags.AzSDKVersion } Write-Output ("Found AzSDK module version: " + $moduleVersion) PublishEvent -EventName "CA Job Invoke Setup Started" InvokeScript -policyStoreURL $OSSPolicyStoreUrl -fileName $runbookCoreSetupScript -version $moduleVersion PublishEvent -EventName "CA Job Invoke Setup Completed" #------------------------------------Execute RunbookScanAgent.ps1 to scan subscription and resources------------------- if((Get-Command -Name "Get-AzSDKAccessToken" -ErrorAction SilentlyContinue|Measure-Object).Count -gt 0) { if($enableAADAuthForOnlinePolicyStore -eq "true") { $accessToken = Get-AzSDKAccessToken -ResourceAppIdURI $azureRmResourceURI } PublishEvent -EventName "CA Job Invoke Scan Started" InvokeScript -accessToken $accessToken -policyStoreURL $onlinePolicyStoreUrl -fileName $runbookScanAgentScript -version $moduleVersion PublishEvent -EventName "CA Job Invoke Scan Completed" } PublishEvent -EventName "CA Job Completed" -Metrics @{ "TimeTakenInMs" = $jobTimer.ElapsedMilliseconds; ` "SuccessCount" = 1 } } catch { PublishEvent -EventName "CA Job Error" -Properties @{ "ErrorRecord" = ($_ | Out-String) } -Metrics @{"TimeTakenInMs" =$jobTimer.ElapsedMilliseconds; "SuccessCount" = 0} } #----------------------------------Runbook end------------------------------------------------------------------------- |