Skip to content

Commit

Permalink
Feature Gov Assignments (#18)
Browse files Browse the repository at this point in the history
* Add GovAssignment class to manage governance assignments

* Add Set-AzApiCallContext function to manage Azure API call context

* Refactor Get-CsAzGovAssignment function to improve performance and readability

* Refactor Invoke-CsFunction to support CyberShell function invocation across multiple environments

* Refactor GitVersion.yml to use 'main' branch instead of 'master' branch

* Update .gitignore to exclude *nosync* directories

* Refactor GovAssignment class to validate additional properties

* Refactor Get-CsAzGovAssignment function to improve performance and readability

* Update Cs Environment features (#17)

* Update .gitignore to exclude *nosync* directories
* Refactor GitVersion.yml to use 'main' branch instead of 'master' branch
* Refactor Write-OutputPadded function to improve message type handling and add support for blank lines
* Refactor Import-CsEnvironment function to improve JSON path handling and add debug output
* Refactor Write-OutputPadded function to improve message handling and add support for custom config file path in Import-CsEnvironment function
* Refactor Write-OutputPadded function to improve message handling and add support for custom config file path in Import-CsEnvironment function
* Refactor Write-OutputPadded function to improve message type handling and add support for blank lines

* skip: Update next-version in GitVersion.yml to 0.2.1-preview.1

* Skip: Update next-version in GitVersion.yml to 0.2.1

* minor: remove fixed version in GitVersion.yml

* minor: Update GitVersion.yml to use ContinuousDelivery mode

* Revert "minor: Update GitVersion.yml to use ContinuousDelivery mode"

This reverts commit 43ecd36.

* minor: Update GitVersion.yml to use ContinuousDelivery mode

* Bump codecov/codecov-action from 4.1.1 to 4.2.0 (#16)

Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.1.1 to 4.2.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](codecov/codecov-action@v4.1.1...v4.2.0)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: fslef <[email protected]>

* fix pull request template

* Update pull request template

* Update Cs Environment features (#17)

* Update .gitignore to exclude *nosync* directories
* Refactor GitVersion.yml to use 'main' branch instead of 'master' branch
* Refactor Write-OutputPadded function to improve message type handling and add support for blank lines
* Refactor Import-CsEnvironment function to improve JSON path handling and add debug output
* Refactor Write-OutputPadded function to improve message handling and add support for custom config file path in Import-CsEnvironment function
* Refactor Write-OutputPadded function to improve message handling and add support for custom config file path in Import-CsEnvironment function
* Refactor Write-OutputPadded function to improve message type handling and add support for blank lines

* Disable publish_module_to_gallery in build.yaml

* Add Get-CsAzGovAssignment function to retrieve Azure governance assignments

* Refactor GitVersion.yml to use 'master' branch instead of 'main' branch

* Update Get-CsAzGovAssignment.ps1 to add OverdueOnly parameter to filter results

* Update next-version in GitVersion.yml to 0.6.0

* Refactor GovAssignment class to remove unnecessary null or empty checks

* Add OverdueOnly parameter to Get-CsAzGovAssignment.ps1 to filter results

* Update next-version in GitVersion.yml to 0.5.0

---------

Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: fslef <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
fslef and dependabot[bot] authored Apr 10, 2024
1 parent e6552f4 commit 778b207
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- `Get-CsAzGovAssignment` to get azure governance assignments

### Changed
- `Write-OutputPadded`: improve message handling
- `Import-CsEnvironment`: add capability to specify custom path for the config file using the CYBERSHELL_CONFIG environment variable
Expand Down
24 changes: 6 additions & 18 deletions GitVersion.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mode: ContinuousDelivery
major-version-bump-message: '(breaking\schange|breaking|major)\b'
minor-version-bump-message: '(adds?|features?|minor)\b'
next-version: 0.5.0
major-version-bump-message: '\s?(breaking|major|breaking\schange)'
minor-version-bump-message: '\s?(add|feature|minor)'
patch-version-bump-message: '\s?(fix|patch)'
no-bump-message: '\+semver:\s?(none|skip)'
assembly-informational-format: '{NuGetVersionV2}+Sha.{Sha}.Date.{CommitDate}'
Expand All @@ -14,26 +15,13 @@ branches:
tag: useBranchName
increment: Minor
regex: f(eature(s)?)?[\/-]
source-branches: ['main']
source-branches: ['master']
hotfix:
tag: fix
increment: Patch
regex: (hot)?fix(es)?[\/-]
source-branches: ['main']
source-branches: ['master']

ignore:
sha: []
merge-message-formats: {}


# feature:
# tag: useBranchName
# increment: Minor
# regex: f(eature(s)?)?[/-]
# source-branches: ['master']
# hotfix:
# tag: fix
# increment: Patch
# regex: (hot)?fix(es)?[/-]
# source-branches: ['master']

merge-message-formats: {}
2 changes: 1 addition & 1 deletion build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ BuildWorkflow:
#- Merge_CodeCoverage_Files

publish:
- publish_module_to_gallery
#- publish_module_to_gallery
- Publish_Release_To_GitHub


Expand Down
81 changes: 81 additions & 0 deletions src/Classes/GovAssignment.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
class GovAssignment {
# Class properties
[string] $CsEnvironment = ""
[string] $SourceType = ""
[string] $AssessmentName = ""
[string] $AssessmentDisplayName = ""
[string] $AssignedResourceId = ""
[string] $ContainerId = ""
[string] $AssignmentKey = ""
[datetime] $RemediationDueDate
[bool] $IsGracePeriod = $false
[string] $Owner = ""
[bool] $OwnerEmailNotification = $false
[bool] $ManagerEmailNotification = $false
[string] $NotificationDayOfWeek = ""

# Input Validation
static [void] Validate(
[string]$CsEnvironment, [string]$SourceType, [string]$AssessmentName, [string]$AssignedResourceId,
[string]$ContainerId, [string]$AssignmentKey, [datetime]$RemediationDueDate, [bool]$IsGracePeriod,
[string]$Owner, [bool]$OwnerEmailNotification, [bool]$ManagerEmailNotification, [string]$NotificationDayOfWeek) {
$errors = @()
if ([string]::IsNullOrEmpty($CsEnvironment)) { $errors += "CsEnvironment cannot be null or empty" }
if ([string]::IsNullOrEmpty($SourceType)) { $errors += "SourceType cannot be null or empty" }
if ([string]::IsNullOrEmpty($AssessmentName)) { $errors += "AssessmentName cannot be null or empty" }
if ([string]::IsNullOrEmpty($AssignedResourceId)) { $errors += "AssignedResourceId cannot be null or empty" }
if ([string]::IsNullOrEmpty($ContainerId)) { $errors += "ContainerId cannot be null or empty" }
if ([string]::IsNullOrEmpty($AssignmentKey)) { $errors += "AssignmentKey cannot be null or empty" }


if ($errors.Count -gt 0) {
throw ($errors -join "`n")
}
}

# Constructors

# Default constructor
GovAssignment() {
# Logic for initializing the object, if necessary, beyond the default property values.
}

# Convenience constructor from hashtable
GovAssignment([hashtable]$Properties) {
[GovAssignment]::Validate($Properties.CsEnvironment, $Properties.SourceType, $Properties.AssessmentName,
$Properties.AssignedResourceId, $Properties.ContainerId, $Properties.AssignmentKey, $Properties.RemediationDueDate,
$Properties.IsGracePeriod, $Properties.Owner, $Properties.OwnerEmailNotification, $Properties.ManagerEmailNotification,
$Properties.NotificationDayOfWeek)

$this.Init($Properties)
$this.GetAssignmentDetails()
}

# Common constructor for separate properties
GovAssignment([string]$CsEnvironment, [string]$SourceType, [string]$AssessmentName, [string]$AssignedResourceId,
[string]$ContainerId, [string]$AssignmentKey, [datetime]$RemediationDueDate, [bool]$IsGracePeriod, [string]$Owner,
[bool]$OwnerEmailNotification, [bool]$ManagerEmailNotification, [string]$NotificationDayOfWeek) {
[GovAssignment]::Validate($CsEnvironment, $SourceType, $AssignedResourceId, $ContainerId, $AssignmentKey)
$this.CsEnvironment = $CsEnvironment
$this.SourceType = $SourceType
$this.AssignedResourceId = $AssignedResourceId
$this.ContainerId = $ContainerId
$this.AssignmentKey = $AssignmentKey
$this.GetAssignmentDetails()
}

# Methods

[void] Init([hashtable]$Properties) {
foreach ($key in $Properties.Keys) {
if ($this.psobject.properties.Match($key).Count -gt 0) {
$this.$key = $Properties[$key]
}
}
}


[string] ToString() {
return "Assessment: $($this.AssessmentDisplayName) with Key: $($this.AssignmentKey) for Resource: $($this.AssignedResourceId) in Environment: $($this.CsEnvironment)"
}
}
81 changes: 81 additions & 0 deletions src/Private/Set-AzApiCallContext.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
function Set-AzApiCallContext {
<#
.SYNOPSIS
Sets the context for Azure API calls.
.DESCRIPTION
The Set-AzApiCallContext function sets up the context for Azure API calls. It takes in parameters like SubscriptionId, TenantId, targetEndpoint, and others. It then initializes the AzAPICall and creates a bearer token for the specified target endpoint.
.PARAMETER SubscriptionId
The subscription ID for the Azure account.
.PARAMETER TenantId
The tenant ID for the Azure account.
.PARAMETER targetEndpoint
The target endpoint for the Azure API call. It must be one of 'MicrosoftGraph', 'ARM', 'KeyVault', 'LogAnalytics', 'MonitorIngest' and must match the specified pattern.
.PARAMETER DebugAzAPICall
A boolean value indicating whether to debug the Azure API call.
.PARAMETER WriteMethod
The method to write the output.
.PARAMETER DebugWriteMethod
The method to write the debug output.
.PARAMETER SkipAzContextSubscriptionValidation
A boolean value indicating whether to skip Azure context subscription validation.
.EXAMPLE
Set-AzApiCallContext -SubscriptionId "sub-id" -TenantId "tenant-id" -targetEndpoint "https://example.blob.core.windows.net"
This example shows how to set the Azure API call context.
#>

[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory = $true)]
[string]$SubscriptionId,

[Parameter(Mandatory = $true)]
[string]$TenantId,

[Parameter(Mandatory = $true)]
[ValidateSet('MicrosoftGraph', 'ARM', 'KeyVault', 'LogAnalytics', 'MonitorIngest')]
[ValidatePattern('^https://[a-z0-9]+\.blob\.core\.windows\.net$|^https://[a-z0-9]+\.blob\.storage\.azure\.net$')]
[string]$targetEndpoint,

[bool]$DebugAzAPICall = $False,

[string]$WriteMethod = 'Output',

[string]$DebugWriteMethod = 'Warning',

[bool]$SkipAzContextSubscriptionValidation = $true


)

Write-Output "Setting up parameters for AzAPICallModule."

$parameters4AzAPICallModule = @{
SubscriptionId4AzContext = $SubscriptionId
TenantId4AzContext = $TenantId
DebugAzAPICall = $DebugAzAPICall
WriteMethod = $WriteMethod
DebugWriteMethod = $DebugWriteMethod
SkipAzContextSubscriptionValidation = $SkipAzContextSubscriptionValidation
}

if ($PSCmdlet.ShouldProcess("AzAPICall", "Initialize")) {
Write-Output "Initializing AzAPICall."
$azAPICallConf = initAzAPICall @parameters4AzAPICallModule
Write-Information "Creating bearer token..."
createBearerToken -AzAPICallConfiguration $azapicallconf -targetEndPoint $targetEndpoint
Write-Information 'here is the token:' $azAPICallConf['htBearerAccessToken'].$targetEndpoint
}
else {
Write-Output "Initialization of AzAPICall was cancelled by the user."
}
}
142 changes: 142 additions & 0 deletions src/Public/Get-CsAzGovAssignment.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
function Get-CsAzGovAssignment {
<#
.SYNOPSIS
Retrieves Azure Governance Assignments.
.DESCRIPTION
This function retrieves the list of security assessments from Azure and stores the governance assignments.
.PARAMETER azAPICallConf
A hashtable containing the configuration for the Azure API call.
.PARAMETER CsEnvironment
The environment for which the function is being run.
.PARAMETER OverdueOnly
A switch parameter that retrieves only the overdue governance assignments.
.EXAMPLE
Get-CsAzGovAssignment -SubId "your-subscription-id" -azAPICallConf $yourConfig -CsEnvironment "your-environment"
.INPUTS
String, Hashtable, String
.OUTPUTS
ArrayList
Returns an ArrayList of governance assignments.
.NOTES
This function makes use of the AzAPICall function to make the API call to Azure.
#>

param (

[Parameter(Position = 0, Mandatory = $true)]
[System.Collections.Hashtable]$azAPICallConf,

[Parameter(Position = 1, Mandatory = $true)]
[string]$CsEnvironment,

[Parameter()]
[switch]$OverdueOnly
)

Write-OutputPadded "Governance Assignments" -IndentLevel 1 -isTitle -Type "Information"

if ($OverdueOnly) {
Write-OutputPadded "OverdueOnly Parameter set" -IndentLevel 1 -Type "Verbose"
$completionStatus = "'Overdue'"
}
else {
$completionStatus = "'OnTime', 'Overdue', 'Unassigned', 'Completed'"
}

# Get the Governance Assignments list
$query = @"
securityresources
| where type =~ 'microsoft.security/assessments'
| extend assessmentType = tostring(properties.metadata.assessmentType),
assessmentId = tolower(id),
statusCode = tostring(properties.status.code),
source = trim(' ', tolower(tostring(properties.resourceDetails.Source))),
resourceId = trim(' ', tolower(tostring(properties.resourceDetails.Id))),
resourceName = tostring(properties.resourceDetails.ResourceName),
resourceType = tolower(properties.resourceDetails.ResourceType),
displayName = tostring(properties.displayName),
assessmentKey = tostring(split(id, '/')[-1])
| where assessmentType == 'BuiltIn'
| extend environment = case(
source in~ ('azure', 'onpremise'), 'Azure',
source =~ 'aws', 'AWS',
source =~ 'gcp', 'GCP',
source =~ 'github', 'GitHub',
source =~ 'gitlab', 'GitLab',
source =~ 'azuredevops', 'AzureDevOps',
dynamic(null)
)
| where environment in~ ('AWS', 'Azure', 'AzureDevOps', 'GCP', 'GitHub', 'GitLab')
| join kind=leftouter (
securityresources
| where type == 'microsoft.security/assessments/governanceassignments'
| extend dueDate = todatetime(properties.remediationDueDate),
owner = tostring(properties.owner),
disableOwnerEmailNotification = tostring(properties.governanceEmailNotification.disableOwnerEmailNotification),
disableManagerEmailNotification = tostring(properties.governanceEmailNotification.disableManagerEmailNotification),
emailNotificationDayOfWeek = tostring(properties.governanceEmailNotification.emailNotificationDayOfWeek),
governanceStatus = case(
isnull(todatetime(properties.remediationDueDate)), 'NoDueDate',
todatetime(properties.remediationDueDate) >= bin(now(), 1d), 'OnTime',
'Overdue'
),
assessmentId = tolower(tostring(properties.assignedResourceId))
| project dueDate, owner, disableOwnerEmailNotification, disableManagerEmailNotification, emailNotificationDayOfWeek, governanceStatus, assessmentId
) on assessmentId
| extend completionStatus = case(
governanceStatus == 'Overdue', 'Overdue',
governanceStatus == 'OnTime', 'OnTime',
statusCode == 'Unhealthy', 'Unassigned',
'Completed'
)
| where completionStatus in~ ($completionStatus)
| project displayName, resourceId, assessmentKey, resourceType, resourceName, dueDate, owner, completionStatus
| order by completionStatus, displayName
"@

$payLoad = @"
{
"query": "$($query)"
}
"@

Write-OutputPadded "Query Payload:" -Type 'debug' -IndentLevel 1 -BlankLineBefore
Write-OutputPadded "$payLoad" -Type 'data' -IndentLevel 1 -BlankLineBefore

$uri = "$($azapicallconf.azAPIEndpointUrls.ARM)/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01"
$queryResult = AzAPICall -AzAPICallConfiguration $azapiCallConf -uri $uri -body $payLoad -method 'POST' -listenOn Content

$GovAssignments = [System.Collections.ArrayList]::new()

foreach ($assignment in $queryResult) {

$GovAssignmentObj = [GovAssignment]::new()
$GovAssignmentObj.CsEnvironment = $CsEnvironment
$GovAssignmentObj.SourceType = 'Az'
$GovAssignmentObj.AssessmentName = $assignment.assessmentKey
$GovAssignmentObj.AssessmentDisplayName = $assignment.displayName
$GovAssignmentObj.AssignedResourceId = $assignment.resourceId
$GovAssignmentObj.ContainerId = $assignment.resourceId.split("/")[2]
$GovAssignmentObj.AssignmentKey = $assignment.assessmentKey
$GovAssignmentObj.RemediationDueDate = $assignment.dueDate
$GovAssignmentObj.IsGracePeriod = $assignment.isGracePeriod
$GovAssignmentObj.Owner = $assignment.owner
$GovAssignmentObj.OwnerEmailNotification = $assignment.disableOwnerEmailNotification
$GovAssignmentObj.ManagerEmailNotification = $assignment.disableManageremailnotification
$GovAssignmentObj.NotificationDayOfWeek = $assignment.notificationDayOfWeek

# Add the assignment to the list
$GovAssignments.add($GovAssignmentObj)
$assignment | Add-Member -MemberType NoteProperty -Name "Environment" -Value $CsEnvironment
}

return $GovAssignments
}
Loading

0 comments on commit 778b207

Please sign in to comment.