AZSK Azure DevOps NodeJS

Project Log 0: Automating Azure Security Reports With AZSK, NodeJS and PS

In our daily work me and my colleagues @Nixu write plenty of reports. This blog features project log for building full report automation for Microsoft’s excellent AZSK DevOps kit.

The report subject is company called Contoso (In the report ”Contoso Group”) People working with MS technologies might be aware of the company… no pun intended 🙂

https://docs.microsoft.com/en-us/microsoft-365/enterprise/contoso-overview?view=o365-worldwide

Here is short demo of Contoso reports getting automated scan results from my throwaway MSDN environment

Background

AZSK DevOps kit is full security suite by Microsoft which they use also internally. If you are unfamiliar with it, I strongly recommend take a read here, my buddy Sami has made some nice things with it too here

Quick reference From the link: The Secure DevOps Kit for Azure (AzSK) was created by the Core Services Engineering & Operations (CSEO) division at Microsoft, to help accelerate Microsoft IT’s adoption of Azure. We have shared AzSK and its documentation with the community to provide guidance for rapidly scanning, deploying and operationalizing cloud resources, across the different stages of DevOps, while maintaining controls on security and governance.
AzSK is not an official Microsoft product – rather an attempt to share Microsoft CSEO’s best practices with the community..

Creating detailed report files from AZSK

It’s good to understand that AZSK provides plenty of reporting response to begin with (Export to PDF, .LOG and CSV by default) I’d suggest normally starting with these.

Due to extensive use of that output provided by AZSK we require further enrichment for optimal result.

Short rundown is that JSON, CSV, DOCX and .MD files are created with help of NodeJS parser which has templates and helper functions provided. These functions look up into additional definition file which’s existence was discovered by other colleague of mine. This was rather excellent finding 🙂

  • Since the original report is table, I prefer report to be formatted as list as its better fit for anything even barely resembling official report styles
  • Me and my colleagues have done also some grouping on AZSK side, to add all the resources to an array which helps with the list

  • NodeJS generated .MD is also used to copy the report to DOCX
    • This can also be automated by PANDOC, but due to word handily supporting an use of existing styling Copy/Paste actually works better.
    • NodeJS already generates some additional information by fetching the rationales from the definition and creating styling for markdown

Once I am satisfied with the automation I will probably add the full NodeJS source files here, but for now, here are just some excerpts.

Some nice use cases:

  • Adding control references to summaries in reports
2
  • .MD files work out of the box for creating hierarchy on DOCX which can be conveniently browsed from the navigation view
  • NodeJS generated .MD is is useful because its supported by many platforms such as Confluence and Azure Devops out of the box with use of existing widgets
    • There is also existing AZSK solution for Azure DevOps (Maybe another blog, another Time)
Import-Module -Name AZSK
connect-AzAccount

$sub = (Get-AzSubscription | Out-GridView -PassThru).id
[string]$sub

Get-AzSKAzureServicesSecurityStatus -SubscriptionId $sub  

$ReportFile = (Get-ChildItem -Path "$env:USERPROFILE\AppData\Local\Microsoft\AzSKLogs\" -Recurse | where {$_.FullName -match "CSV" -and $_.FullName -match "GRS" -and $_.FullName -notmatch "unsupported" -and $_.FullName -notmatch "Consolidated"}| Sort-Object -Property LastWriteTime -Descending | select -first 1).FullName

if (!$ReportFile) { Write-Host "no report file" -ForegroundColor red;Exit 0}
Write-Host "`nReport file path:`n$ReportFile" -ForegroundColor Green
$ReportCsv = Import-Csv $ReportFile
$GroupedList = $ReportCsv | Group-Object ControlID,Status,FeatureName,ControlSeverity,Description,Recommendation



# Consolidate

$Report = @()

ForEach ($Result in $GroupedList)
{
    $PSObject = New-Object PSObject -Property @{
        ControlID = $Result.Group[0].ControlId
        Status = $Result.Group[0].Status
        FeatureName = $Result.Group[0].FeatureName
        ResourceName = $Result.Group.ResourceName
        ControlSeverity = $Result.Group[0].ControlSeverity
        Description = $Result.Group[0].Description
        Recommendation = $Result.Group[0].Recommendation
    
    }

    $Report += $PSObject
}

$items = $Report.featurename | select -Unique
    foreach ($service in $items) {

    $sd = $report  | where {$_.FeatureName -match $service }| sort-object -Property ControlSeverity | select controlid,*name*,reco*,*ControlSeverity*,status, *description* | ConvertTo-Json -Depth 2

    $export = 
    'var data =' + $sd + 
    '
    module.exports={data}'
    mkdir .\md\ -force -confirm:$false
    
    $export | Out-File .\md\export.js -Encoding utf8

        node.exe .\azsk.js; start-sleep -Milliseconds 300
        
}
#

const fs = require('fs')
var chalk = require('chalk')
var {render} = require('mustache')

var {data} = require(__dirname + '/md/export')
var {getRationale} = require(__dirname + '/helpers')
var {template,topHeaders} = require('./template')

async function genRep () {
     
//
                for (let index = 0; index < data.length; index++) {
                     
                     const scannedItem = data[index]    
                     let {FeatureName,ControlID} = scannedItem
              
                     
                     if (index == 0) {
                            var createHeaders = render(topHeaders,scannedItem)
                            var addToexisting = true

                            try {fs.readFileSync(`${__dirname}\\md\\`+`00-results.md`) } catch {
                                   console.log('creating new report file')
                                   fs.writeFileSync(`${__dirname}\\md\\`+`00-results.md`,createHeaders)   
                                   addToexisting = false
                                   console.log(addToexisting)
                            } 
                            
                            if (addToexisting == true) {
                                   console.log('appending to existing report')
                                   fs.appendFileSync(`${__dirname}\\md\\`+`00-results.md`,createHeaders)}

                            fs.writeFileSync(`${__dirname}\\md\\`+`00-results-${FeatureName}.md`,createHeaders)

                            }
                     var rationale =  await getRationale (FeatureName,ControlID)

                     scannedItem.rationale = rationale
                     //console.log(data[index].FeatureName)

                     //console.log(scannedItem)
                     var output = render(template,scannedItem)
                     console.log(chalk.bold.yellowBright.bgGrey.bold(`creating report for${FeatureName}`,chalk.green.bgBlack(` ControlID: ${scannedItem.ControlID}`)))

                     fs.writeFileSync(`${__dirname}\\md\\` + `${scannedItem.ControlID}.md`, output)
                     fs.appendFileSync(`${__dirname}\\md\\`+`00-results-${FeatureName}.md`,output)
                     fs.appendFileSync(`${__dirname}\\md\\`+`00-results.md`,output)
                
                }

}

genRep() 








function stylizeContent (content,color,font,tags) {
    if (tags) { 
    return `${tags.split(',')[0]}<span style= "font-family: ${font}; color: ${color}">${content}</span> ${tags.split(',')[1]}
    `
    } else {
        return `<span style= "font-family: ${font}; color: ${color}">${content}</span> <br>
       `
    }
}

//let rationale = stylizeContent(' **Rationale**: {{rationale}}','black', 'Arial')
let ControlID = stylizeContent('{{ControlID}}','#0089cf', 'Arial',"<H4>,</H4>")
//console.log(rationale)
let Status = stylizeContent('**Status** {{Status}}','black', 'Arial',)
let Severity = stylizeContent('**Severity** {{ControlSeverity}}','black', 'Arial',)
let Recommendation = stylizeContent('**Recommendation** {{Recommendation}}','black', 'Arial',)
let Rationale = stylizeContent('**Rationale:** {{rationale}}','black', 'Arial',)
let Resources = stylizeContent(`**Resources**:{{#ResourceName}} <br> {{.}} {{/ResourceName}}`,'black', 'Arial',)
let Description = stylizeContent('**Description** {{Description}}','black', 'Arial',)

let template = `
${ControlID}
${Status}
${Severity}
${Recommendation}
${Rationale}
${Description}
${Resources}

`

let FeatureName = stylizeContent('{{FeatureName }}','#0089cf', 'Arial',"<H2>,</H2>")
let FeatureDescription = stylizeContent('This section describes the top recommendations and detailed audit findings on {{FeatureName}} resources.','black', 'Arial')
let TopRecommendations = stylizeContent('Top Recommendations','#0089cf', 'Arial',"<H3>,</H3>")
let TopControls = stylizeContent('Controls','#0089cf', 'Arial',"<H3>,</H3>")

let topHeaders = `
${FeatureName}
${FeatureDescription}
${TopRecommendations}
${TopControls}
`

module.exports={template,topHeaders}

To Be Continued 🙂

Br Joosua

2 comments on “Project Log 0: Automating Azure Security Reports With AZSK, NodeJS and PS

  1. Paluuviite: Project Log Part 3: Automating Azure Security Reports – Combining Subscription and resource security results – SecureCloudBlog

  2. Paluuviite: Security Posture Management with Azure Policy and Microsoft Defender for Cloud – SecureCloudBlog

Vastaa

Täytä tietosi alle tai klikkaa kuvaketta kirjautuaksesi sisään:

WordPress.com-logo

Olet kommentoimassa WordPress.com -tilin nimissä. Log Out /  Muuta )

Facebook-kuva

Olet kommentoimassa Facebook -tilin nimissä. Log Out /  Muuta )

Muodostetaan yhteyttä palveluun %s

%d bloggaajaa tykkää tästä: