In this blog post, I am going to show how to run ScoutSuite in an Azure DevOps Pipeline and display reports within an Azure Storage Account Static Website
What is Scout Suite?
https://github.com/nccgroup/ScoutSuite
“Scout Suite is an open source multi-cloud security-auditing tool, which enables security posture assessment of cloud environments. Using the APIs exposed by cloud providers, Scout Suite gathers configuration data for manual inspection and highlights risk areas. Rather than going through dozens of pages on the web consoles, Scout Suite presents a clear view of the attack surface automatically.”
Scout Suite security-auditing tool that supports multiple cloud provides including Microsoft Azure!
Installation of Scout Suite is documented here in their Wiki , they also offer a Docker container – which in this blog I will be running!
Configuring Scout Suite for Microsoft Azure
The Azure related setup requirements are here
In this blog example I am going to be using a service principal
Summary of Azure DevOps Pipeline
I am going to be running Scout Suite security-auditing tool and outputting its report to an Azure Storage Account static website; their is some third-party html extensions for Azure DevOps but due to the report being a dynamic html page it doesn’t display correctly.
Prior to running pipeline
Azure Key Vault
With the service principal I created during my configuration setup within Microsoft Azure I am going to store the details of the service principal in Azure Key Vault
Secret names:-
CLIENT-ID :- Client ID of Service Principal
CLIENT-SECRET :- Service Principal Secret
TENANT-ID :- Azure AD Tenant ID of Service Principal
Configure an access policy for the service principal to allow it to retrieve these secrets from within the pipeline
Azure Storage Account Static Website
Create & configure an Azure Storage Account Static Website, I blogged how to create this previously
Additionally, due to it being a potential sensitive report, configure the storage account firewall also 🙂
Prior to running pipeline work now completed, lets look at the pipeline!
Azure DevOps Pipeline
task: AzureKeyVault@1:- Retrieves the relevant secrets using the managed identity
- task: AzureKeyVault@1
displayName: "Retrieve Key Vault Secrets"
inputs:
azureSubscription: 'tamopstf2'
keyVaultName: 'tamopsscoutkv'
secretsFilter: '*'
runAsPreJob: false
task: AzureCCLI@2:- Adds Azure DevOps agent IP to Storage Account
- task: AzureCLI@2
displayName: "Add Azure DevOps IP to Storage Account"
inputs:
azureSubscription: 'tamopstf2'
scriptType: 'bash'
scriptLocation: inlineScript
inlineScript: |
IP=($(curl -s http://ipinfo.io/json | jq '.ip' | sed -e 's/^"//' -e 's/"$//'))
az storage account network-rule add -g tamops-scoutsuite --account-name tamopsscoutsuite --ip-address $IP
task: Bash@3:- Run Scout Suite from Docker image
- Referencing Scout Suite to run using the Service Principal credentials created earlier
- Outputs report to /share that is mounted using -v, in this example the directory is $(DATE) that is a variable passed into Azure DevOps pipeline
- task: Bash@3
displayName: "Run Scout Suite"
inputs:
targetType: 'inline'
script: |
mkdir $(DATE)
cd $(DATE)
docker run --rm -v $(pwd):/share rossja/ncc-scoutsuite:0.6.0 /root/scoutsuite/bin/scout azure --client-id $CLIENT_ID --client-secret $CLIENT_SECRET --tenant $TENANT_ID --service-principal --all-subscriptions --no-browser --report-dir /share --report-name index
echo "test"
env:
CLIENT_ID: $(CLIENT-ID)
CLIENT_SECRET: $(CLIENT-SECRET)
TENANT_ID: $(TENANT-ID)
task: AzureCCLI@2:- Copies Scout Suite Report created in above task to blob of Azure Storage Account Static Website
- task: AzureCLI@2
displayName: "Copy Scout Suite Report"
inputs:
azureSubscription: 'tamopstf2'
scriptType: 'bash'
scriptLocation: inlineScript
inlineScript: |
az storage copy -s $(DATE) --destination-account-name tamopsscoutsuite --destination-container '$web' --recursive
task: AzureCCLI@2:- Remove Azure DevOps IP from Storage Account
- task: AzureCLI@2
displayName: "Remove Azure DevOps IP from Storage Account"
inputs:
azureSubscription: 'tamopstf2'
scriptType: 'bash'
scriptLocation: inlineScript
inlineScript: |
# az --version
# az account list
IP=($(curl -s http://ipinfo.io/json | jq '.ip' | sed -e 's/^"//' -e 's/"$//'))
az storage account network-rule remove -g tamops-scoutsuite --account-name tamopsscoutsuite --ip-address $IP
Full pipeline
name: $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)
trigger:
batch: true
branches:
include:
- main
variables:
DATE: '05042021'
pr: none
stages :
- stage: scoutsuite
jobs:
- job: scoutsuite
continueOnError: false
steps:
- task: AzureKeyVault@1
displayName: "Retrieve Key Vault Secrets"
inputs:
azureSubscription: 'tamopstf2'
keyVaultName: 'tamopsscoutkv'
secretsFilter: '*'
runAsPreJob: false
- task: AzureCLI@2
displayName: "Add Azure DevOps IP to Storage Account"
inputs:
azureSubscription: 'tamopstf2'
scriptType: 'bash'
scriptLocation: inlineScript
inlineScript: |
IP=($(curl -s http://ipinfo.io/json | jq '.ip' | sed -e 's/^"//' -e 's/"$//'))
az storage account network-rule add -g tamops-scoutsuite --account-name tamopsscoutsuite --ip-address $IP
- task: Bash@3
displayName: "Run Scout Suite"
inputs:
targetType: 'inline'
script: |
mkdir $(DATE)
cd $(DATE)
docker run --rm -v $(pwd):/share rossja/ncc-scoutsuite:0.6.0 /root/scoutsuite/bin/scout azure --client-id $CLIENT_ID --client-secret $CLIENT_SECRET --tenant $TENANT_ID --service-principal --all-subscriptions --no-browser --report-dir /share --report-name index
echo "test"
env:
CLIENT_ID: $(CLIENT-ID)
CLIENT_SECRET: $(CLIENT-SECRET)
TENANT_ID: $(TENANT-ID)
- task: AzureCLI@2
displayName: "Copy Scout Suite Report"
inputs:
azureSubscription: 'tamopstf2'
scriptType: 'bash'
scriptLocation: inlineScript
inlineScript: |
az storage copy -s $(DATE) --destination-account-name tamopsscoutsuite --destination-container '$web' --recursive
- task: AzureCLI@2
displayName: "Remove Azure DevOps IP from Storage Account"
inputs:
azureSubscription: 'tamopstf2'
scriptType: 'bash'
scriptLocation: inlineScript
inlineScript: |
# az --version
# az account list
IP=($(curl -s http://ipinfo.io/json | jq '.ip' | sed -e 's/^"//' -e 's/"$//'))
az storage account network-rule remove -g tamops-scoutsuite --account-name tamopsscoutsuite --ip-address $IP
Running in Azure Devops, we can see each task referenced


Finally, view the report!
