AzSDK.ArmTemplateEvaluator.psm1

Set-StrictMode -Version Latest

function Get-AzSDKArmTemplateSecurityStatus
{
    <#
    .SYNOPSIS
    This command would help in evaluating the ARM Templates for security issues
    .DESCRIPTION
    This command would help in evaluating the ARM Templates for security issues
     
    .PARAMETER ArmTemplatePath
        Path to ARM Template file
    .PARAMETER ArmTemplateParameterPath
        Path to ARM Template parameter file
             
    .LINK
    https://aka.ms/azsdkossdocs
 
    #>

    Param(
        [Parameter(Mandatory = $true, HelpMessage = "Path to ARM Template file")]
        [string]        
        $ArmTemplatePath,

        [Parameter(Mandatory = $false, HelpMessage = "Path to ARM Template parameter file")]
        [string]
        $ArmTemplateParameterPath
    )
    Write-Host "`nPREVIEW WARNING: AzSDK ARM Template Evaluator is currently in preview and supports controls from only AppService(7/11), Storage(4/5) and CosmosDb(5/5). Support for additional controls and services will be added in future releases.`n" -ForegroundColor Cyan
    $controlsPath = Join-Path $PSScriptRoot "controls.gen.json"
    $controlsContent = Get-Content $controlsPath -Raw
    $armTemplateContent = Get-Content $ArmTemplatePath -Raw
    $libResults = $null
    if([string]::IsNullOrWhiteSpace($ArmTemplateParameterPath)) {
        $libResults = Get-AzSDKArmTemplateSecurityStatusObj `
            -EvaluatorRulesContent $controlsContent `
            -ArmTemplateContent $armTemplateContent
    } else {
        $armTemplateParameterContent = Get-Content $ArmTemplateParameterPath -Raw
        $libResults = Get-AzSDKArmTemplateSecurityStatusInternal `
            -EvaluatorRulesContent $controlsContent `
            -ArmTemplateContent $armTemplateContent `
            -ArmTemplateParameterContent $armTemplateParameterContent
    }
    $results = $libResults | Where-Object {$_.VerificationResult -ne "NotSupported"} | Select-Object -ExcludeProperty "IsEnabled"
    $timeMarker = [datetime]::Now.ToString("yyyyMMdd_HHmmss")
    $resultsFolder = [System.IO.Path]::Combine($env:LOCALAPPDATA, "Microsoft", "AzSDKLogs", "ARMTemplateEvaluator", $timeMarker)
    $csvFileName = "ATE_Results_" + $timeMarker + ".csv"
    $csvFilePath = [System.IO.Path]::Combine($resultsFolder, $csvFileName)
    $csvResults = [System.Collections.ArrayList]::new()
    foreach($result in $results) {
        $csvResultItem = "" | Select-Object "ResourceType", "ControlId", "Description", "VerificationResult", `
                                "LineNumber", "JsonPath", "Value", "Recommendation", "IsPropertyFound", "IsValueValid", `
                                 "ResourceLineNumber", "ResourceJsonPath"
        $csvResultItem.ResourceType = $result.ResourceType
        $csvResultItem.ControlId = $result.ControlId
        $csvResultItem.Description = $result.Description
        $csvResultItem.VerificationResult = $result.VerificationResult
        $csvResultItem.IsPropertyFound = !$result.IsTokenNotFound
        $csvResultItem.IsValueValid = !$result.IsTokenNotValid
        if($result.ResultDataMarkers.Count -gt 0) {
            $csvResultItem.LineNumber = $result.ResultDataMarkers[0].LineNumber
            $csvResultItem.JsonPath = $result.ResultDataMarkers[0].JsonPath
            $data = $result.ResultDataMarkers[0].DataMarker
            if($data -ieq "true" -or $data -ieq "false") {
                $csvResultItem.Value = $data.ToLower()
            } else{
                $csvResultItem.Value = $data
            }
        } else {
            $csvResultItem.LineNumber = -1
            $csvResultItem.JsonPath = "Not found"
            $csvResultItem.Value = ""
        }
        $csvResultItem.Recommendation = $result.Recommendation
        $csvResultItem.ResourceLineNumber = $result.ResourceDataMarker.LineNumber
        $csvResultItem.ResourceJsonPath = $result.ResourceDataMarker.JsonPath
        $csvResults.Add($csvResultItem) | Out-Null
        if($result.VerificationResult -eq "Passed") { 
            Write-Result -result $result -color Green
        } elseif ($result.VerificationResult -eq "Verify") {
            WWrite-Result -result $result -color Yellow
        } elseif ($result.VerificationResult -eq "Failed") {
            Write-Result -result $result -color Red
        } else {
            Write-Result -result $result -color White
        }
    }
    [System.IO.Directory]::CreateDirectory($resultsFolder) | Out-Null
    $csvResults | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Out-File $csvFilePath -Force
    $detailedOutputFileName = "ATE_Detailed_" + $timeMarker + ".json"
    $detailedOutputFilePath = [System.IO.Path]::Combine($resultsFolder, $detailedOutputFileName)

    $serializerSettings = [Newtonsoft.Json.JsonSerializerSettings]::new();
    $serializerSettings.Converters.Add([Newtonsoft.Json.Converters.StringEnumConverter]::new());
    $serializerSettings.Formatting = [Newtonsoft.Json.Formatting]::Indented;
    $serializerSettings.NullValueHandling = [Newtonsoft.Json.NullValueHandling]::Ignore;
    $serializerSettings.ReferenceLoopHandling = [Newtonsoft.Json.ReferenceLoopHandling]::Ignore;

    $detailedOutput = [Newtonsoft.Json.JsonConvert]::SerializeObject($libResults, $serializerSettings)
    $detailedOutput | Out-File $detailedOutputFilePath -Force
    Copy-Item $ArmTemplatePath -Destination $resultsFolder -Force
    if(![string]::IsNullOrWhiteSpace($ArmTemplateParameterPath)) {
        Copy-Item $ArmTemplateParameterPath -Destination $resultsFolder -Force
    }
    Start-Process $resultsFolder
    Write-Host "`nResults are exported to $resultsFolder`n`n"
    return $resultsFolder
}

function Write-Result($result, $color) {
    Write-Host $result.ResourceType "-" $result.ControlId -ForegroundColor $color
    if($result.ResultDataMarkers.Count -gt 0) {
        Write-Host "`t" "LineNumber:" $result.ResultDataMarkers[0].LineNumber -ForegroundColor $color
        Write-Host "`t" "JsonPath:" $result.ResultDataMarkers[0].JsonPath -ForegroundColor $color
        Write-Host "`t" "Value:" $result.ResultDataMarkers[0].DataMarker -ForegroundColor $color
    } else {
        Write-Host "`t" "LineNumber:" "-1" -ForegroundColor $color
        Write-Host "`t" "JsonPath:" "Not found" -ForegroundColor $color
        Write-Host "`t" "Value:" "" -ForegroundColor $color
    }
    Write-Host "`t" "Result:" $result.VerificationResult "`n" -ForegroundColor $color
}