Framework/Configurations/ContinuousAssurance/Continuous_Assurance_Runbook.ps1
function Set-Modules { param( [System.Collections.IDictionary] $ModuleList, [string[]] $SyncModuleList ) $ModuleList.Keys | ForEach-Object{ $ModuleName = $_ $ModuleVersion = $ModuleList.Item($_) $Module = Get-AzureRmAutomationModule ` -ResourceGroupName $AutomationAccountRG ` -AutomationAccountName $AutomationAccountName ` -Name $ModuleName -ErrorAction SilentlyContinue if(($Module | Measure-Object).Count -eq 0) { #Download module if it is not available DownloadModule -ModuleName $ModuleName -ModuleVersion $ModuleVersion -Sync ($SyncModuleList.Contains($ModuleName)) } else { #module is in extraction state if($Module.ProvisioningState -ne "Failed" -and $Module.ProvisioningState -ne "Succeeded" -and $Module.ProvisioningState -ne "Created") { "Current provisioning state for module $ModuleName is $($Module.ProvisioningState)" } #Check if module with specified version already exists elseif(CheckModuleVersion -ModuleName $ModuleName -ModuleVersion $ModuleVersion) { $ModuleName + " is updated in assets" return } else { #Download required version DownloadModule -ModuleName $ModuleName -ModuleVersion $ModuleVersion -Sync ($SyncModuleList.Contains($ModuleName)) } } } } function DownloadModule { param( [string]$ModuleName, [string]$ModuleVersion, [bool] $Sync ) $SearchResult = SearchModule -ModuleName $ModuleName -ModuleVersion $ModuleVersion if($SearchResult) { $ModuleName = $SearchResult.title.'#text' # get correct casing for the Module name $PackageDetails = Invoke-RestMethod -Method Get -UseBasicParsing -Uri $SearchResult.id $ModuleVersion = $PackageDetails.entry.properties.version #Build the content URL for the nuget package $ModuleContentUrl = "$PowershellGalleryUrl/api/v2/package/$ModuleName/$ModuleVersion" # Find the actual blob storage location of the Module do { $ActualUrl = $ModuleContentUrl $ModuleContentUrl = (Invoke-WebRequest -Uri $ModuleContentUrl -MaximumRedirection 0 -UseBasicParsing -ErrorAction Ignore).Headers.Location } while($null -ne $ModuleContentUrl) $AutomationModule = New-AzureRmAutomationModule ` -ResourceGroupName $AutomationAccountRG ` -AutomationAccountName $AutomationAccountName ` -Name $ModuleName ` -ContentLink $ActualUrl "Downloading "+ $ModuleName + " , version:-" + $ModuleVersion if($Sync) { while( $AutomationModule.ProvisioningState -ne "Created" -and $AutomationModule.ProvisioningState -ne "Succeeded" -and $AutomationModule.ProvisioningState -ne "Failed" ) { #Module is in extracting state Start-Sleep -Seconds 120 $AutomationModule = $AutomationModule | Get-AzureRmAutomationModule } if($AutomationModule.ProvisioningState -eq "Failed") { Write-Error "Importing $AutomationModule Module to Automation failed." } } } } function CheckModuleVersion { param( [string] $ModuleName, [string] $ModuleVersion ) $SearchResult = SearchModule -ModuleName $ModuleName -ModuleVersion $ModuleVersion $Module = Get-AzureRmAutomationModule ` -ResourceGroupName $AutomationAccountRG ` -AutomationAccountName $AutomationAccountName ` -Name $ModuleName -ErrorAction SilentlyContinue if(($Module | Measure-Object).Count -eq 0) { #Module is not available return $false } else { #added condition to return false if module is not succcessfully extracted return ((($Module.ProvisioningState -eq "Succeeded") -or ($Module.ProvisioningState -eq "Created")) -and ($SearchResult.properties.Version -eq $Module.Version)) } } function SearchModule { param( [string] $ModuleName, [string] $ModuleVersion ) $url ="" if([string]::IsNullOrWhiteSpace($ModuleVersion)) { $queryString = "`$filter=IsLatestVersion&searchTerm=%27$ModuleName%27&includePrerelease=false&`$skip=0&`$top=40" $url = "$PowershellGalleryUrl/api/v2/Search()?$queryString" } else { $queryString = "id=%27$ModuleName%27&`$skip=0&`$top=40" $url = "$PowershellGalleryUrl/api/v2/FindPackagesById()?$queryString" } $SearchResult = Invoke-RestMethod -Method Get -Uri $url -UseBasicParsing if(!$SearchResult) { Write-Error "Could not find Module '$ModuleName'" return $null } else { $SearchResult = $SearchResult | Where-Object -FilterScript { return $_.title.'#text' -eq $ModuleName } #filter for module version if(![string]::IsNullOrWhiteSpace($ModuleVersion)) { $SearchResult = $SearchResult | Where-Object -FilterScript { return $_.properties.version -eq $ModuleVersion } } return $SearchResult } } function AddDependentModules { param( $InputModuleList ) $InputModuleList.Keys | ForEach-Object{ $moduleName = $_ $moduleVersion = $InputModuleList.Item($_) $searchResult = SearchModule -ModuleName $moduleName -ModuleVersion $moduleVersion if($searchResult) { $packageDetails = Invoke-RestMethod -Method Get -UseBasicParsing -Uri $searchResult.id $dependencies = $packageDetails.entry.properties.dependencies if($dependencies) { $dependencies = $dependencies.Split("|") # parse depencencies, which are in the format: Module1name:[Module1version]:|Module2name:[Module2version] for($index=$dependencies.count-1;($index -ge 0) -and (![string]::IsNullOrWhiteSpace($dependencies[$index]));$index--) { $dependencyModuleDetail = $dependencies[$index].Split(":") $dependencyModuleName = $dependencyModuleDetail[0] $dependencyModuleVersion = $dependencyModuleDetail[1].Replace('[','').Replace(']','') #Add dependent module to the result list if(!$ResultModuleList.Contains($dependencyModuleName)) { $tempList = [ordered]@{$dependencyModuleName=$dependencyModuleVersion} $tempList+= $ResultModuleList $ResultModuleList.Clear() $tempList.Keys|ForEach-Object{$ResultModuleList.Add($_,$tempList.Item($_))} AddDependentModules -InputModuleList @{$dependencyModuleName=$dependencyModuleVersion} | Out-Null } } } if(!$ResultModuleList.Contains($moduleName)) { if([string]::IsNullOrWhiteSpace($moduleVersion)) { $moduleVersion = $searchResult.properties.Version } $ResultModuleList.Add($moduleName,$moduleVersion) } } } return $ResultModuleList } function ReLinkAllSchedules() { $allRunbookSchedules = @(); $allRunbookSchedules += Get-AzureRmAutomationScheduledRunbook -AutomationAccountName $AutomationAccountName -ResourceGroupName $AutomationAccountRG -RunbookName $RunbookName -ErrorAction SilentlyContinue if($allRunbookSchedules.Count -ne 0) { # Filter schedules of AzSDK runbook $allRunbookSchedules | Where-Object { $_.ScheduleName -ne $NextRunScheduleName -and $_.RunbookName -eq $RunbookName} | ForEach-Object { Unregister-AzureRmAutomationScheduledRunbook -RunbookName $_.RunbookName -ScheduleName $_.ScheduleName ` -ResourceGroupName $_.ResourceGroupName ` -AutomationAccountName $_.AutomationAccountName -ErrorAction Stop -Force | Out-Null Register-AzureRmAutomationScheduledRunbook -RunbookName $_.RunbookName -ScheduleName $_.ScheduleName ` -ResourceGroupName $_.ResourceGroupName ` -AutomationAccountName $_.AutomationAccountName -ErrorAction Stop| Out-Null }; "Relinked runbook" } else { # Create new Next run schedule CreateNextRunSchedule } } function CreateNextRunSchedule() { #create next run schedule Get-AzureRmAutomationSchedule -AutomationAccountName $AutomationAccountName ` -ResourceGroupName $AutomationAccountRG -Name $NextRunScheduleName -ErrorAction SilentlyContinue | Remove-AzureRmAutomationSchedule -Force New-AzureRmAutomationSchedule -AutomationAccountName $AutomationAccountName -Name $NextRunScheduleName ` -ResourceGroupName $AutomationAccountRG -StartTime $(get-date).AddMinutes(10) ` -OneTime -ErrorAction Stop | Out-Null Register-AzureRmAutomationScheduledRunbook -RunbookName $RunbookName -ScheduleName $NextRunScheduleName ` -ResourceGroupName $AutomationAccountRG ` -AutomationAccountName $AutomationAccountName -ErrorAction Stop | Out-Null "Scheduled next run" } function ConvertStringToBoolean($strToConvert) { switch($strToConvert) { "true" {return $true} "false" {return $false} } return $false #adding this to prevent error all path doesn't return value" } function GetCCStatus() { Set-AzSDKOMSSettings -OMSWorkspaceID $OMSWorkspaceId -OMSSharedKey $OMSWorkspaceSharedKey -Source "CC" #set values in azsdksettings json $UseOnlinePolicyStore = ConvertStringToBoolean($UseOnlinePolicyStore) $EnableAADAuthForOnlinePolicyStore = ConvertStringToBoolean($EnableAADAuthForOnlinePolicyStore) if($UseOnlinePolicyStore) { if($EnableAADAuthForOnlinePolicyStore) { Set-AzSDKPolicySettings -OnlinePolicyStoreUrl $OnlinePolicyStoreUrl -EnableAADAuthForOnlinePolicyStore } else { Set-AzSDKPolicySettings -OnlinePolicyStoreUrl $OnlinePolicyStoreUrl } } else { Set-AzSDKPolicySettings -DisableOnlinePolicy } "Running command 'Get-AzSDKAzureServicesSecurityStatus'" $SVTResultPath = "" if($ResourceGroupNames.Trim() -eq "*") { $SVTResultPath = Get-AzSDKAzureServicesSecurityStatus -SubscriptionId $SubscriptionID -ExcludeTags "OwnerAccess" } else { $SVTResultPath = Get-AzSDKAzureServicesSecurityStatus -SubscriptionId $SubscriptionID -ResourceGroupNames $ResourceGroupNames -ExcludeTags "OwnerAccess" } "Running command 'Get-AzSDKSubscriptionSecurityStatus'" $SubCoreResultPath = Get-AzSDKSubscriptionSecurityStatus -SubscriptionId $SubscriptionID -ExcludeTags "OwnerAccess" $ReportFolderPath = (Get-Item $SVTResultPath).parent.FullName "commands execution completed" #Check if storage account exists try { Get-AzureRmStorageAccount -ResourceGroupName $StorageAccountRG -Name $StorageAccountName -ErrorAction stop | Out-Null } catch { Write-Error -Message $_.Exception throw $_.Exception } if($ReportFolderPath) { #Create output files in storage $containerName = "azsdkexecutionlogs" $archiveFilePath = "$ReportFolderPath\AutomationLogs_" + $(Get-Date -format "yyyyMMdd_HHmmss") + ".zip" $keys = Get-AzureRmStorageAccountKey -ResourceGroupName $StorageAccountRG -Name $StorageAccountName $currentContext = New-AzureStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $keys[0].Value -Protocol Https try { Get-AzureStorageContainer -Name $containerName -Context $currentContext -ErrorAction Stop | Out-Null } Catch { New-AzureStorageContainer -Name $containerName -Context $currentContext | Out-Null } Compress-Archive -Path $ReportFolderPath -CompressionLevel Optimal -DestinationPath $archiveFilePath Set-AzureStorageBlobContent -File $archiveFilePath -Container $containerName -Context $currentContext | Out-Null "Exported reports to storage" #cleanup Remove-Item -Path $ReportFolderPath -Recurse } else { "No output reports generated" } } #config start $ResourceGroupNames = Get-AutomationVariable -Name "AppResourceGroupNames" $OMSWorkspaceId = Get-AutomationVariable -Name "OMSWorkspaceId" $OMSWorkspaceSharedKey = Get-AutomationVariable -Name "OMSSharedKey" $StorageAccountName = Get-AutomationVariable -Name "ReportsStorageAccountName" $StorageAccountRG = "[#AutomationAccountRG#]" $AutomationAccountRG = "[#AutomationAccountRG#]" $AutomationAccountName="[#AutomationAccountName#]" $RunbookName = "[#RunbookName#]" $NextRunScheduleName = "Next_Run_Schedule" $UseOnlinePolicyStore = "[#UseOnlinePolicyStore#]" $OnlinePolicyStoreUrl = "[#OnlinePolicyStoreUrl#]" $EnableAADAuthForOnlinePolicyStore = "[#EnableAADAuthForOnlinePolicyStore#]" #config end #global variables $PowershellGalleryUrl = "https://www.powershellgallery.com" $AzSDKModuleName = "[#ModuleName#]" $ResultModuleList = [ordered]@{} #Login try { $RunAsConnection = Get-AutomationConnection -Name "AzureRunAsConnection" "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 { if(!$RunAsConnection) { throw "Connection AzureRunAsConnection not found. Please create one" } else { throw $_.Exception } } #Set subscription id $SubscriptionID = $RunAsConnection.SubscriptionID #check if AzureRM is available (for scenario where AzSDK is available but AzureRM extraction might have failed) $azureRMmodule = Get-AzureRmAutomationModule -ResourceGroupName $AutomationAccountRG ` -AutomationAccountName $AutomationAccountName ` -Name AzureRM -ErrorAction SilentlyContinue $isAzureRMAvailable = $null -ne $azureRMmodule -and ` ($azureRMmodule.ProvisioningState -eq "Succeeded" -or $azureRMmodule.ProvisioningState -eq "Created") #check if AzSDK module is latest and AzureRM is available if(!(CheckModuleVersion -ModuleName $AzSDKModuleName) -or !$isAzureRMAvailable) { #Update all modules #Module list is in hashtable format : key = modulename , value = version (This is useful to fetch version of specific module by name) $finalModuleList = [ordered]@{} #Get dependencies of azsdk module AddDependentModules -InputModuleList @{$AzSDKModuleName=""} | Out-Null #Azure modules to be downloaded first should be added first in finalModuleList $baseModuleList = [ordered]@{} $baseModuleList.Add("AzureRM.Profile",$ResultModuleList.Item("AzureRM.Profile")) $baseModuleList.Add("AzureRM.Automation",$ResultModuleList.Item("AzureRM.Automation")) $ResultModuleList.Remove("AzureRM.Profile") $ResultModuleList.Remove("AzureRM.Automation") $finalModuleList += $baseModuleList $finalModuleList += $ResultModuleList $syncModules = @("AzureRM.Profile", "AzureRM.Automation"); Set-Modules -ModuleList $finalModuleList -SyncModuleList $syncModules #Create next run CreateNextRunSchedule } #check if AzSDK command is accessible elseif((Get-Command -Name "Get-AzSDKAzureServicesSecurityStatus" -ErrorAction SilentlyContinue|Measure-Object).Count -eq 0) { CreateNextRunSchedule } else { GetCCStatus } #relink all schedules to use latest modules ReLinkAllSchedules |