Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Gov Assignments #18

Merged
merged 28 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
25fdfce
Add GovAssignment class to manage governance assignments
fslef Apr 8, 2024
4adc464
Add Set-AzApiCallContext function to manage Azure API call context
fslef Apr 8, 2024
1d959a1
Refactor Get-CsAzGovAssignment function to improve performance and re…
fslef Apr 8, 2024
305383a
Refactor Invoke-CsFunction to support CyberShell function invocation …
fslef Apr 8, 2024
7f272a6
Refactor GitVersion.yml to use 'main' branch instead of 'master' branch
fslef Apr 8, 2024
24c6a7b
Update .gitignore to exclude *nosync* directories
fslef Apr 8, 2024
2f0efce
Refactor GovAssignment class to validate additional properties
fslef Apr 8, 2024
ae8b05d
Refactor Get-CsAzGovAssignment function to improve performance and re…
fslef Apr 8, 2024
396cdf7
Update Cs Environment features (#17)
fslef Apr 10, 2024
8e2a55b
skip: Update next-version in GitVersion.yml to 0.2.1-preview.1
fslef Apr 10, 2024
41e6a16
Skip: Update next-version in GitVersion.yml to 0.2.1
fslef Apr 10, 2024
c2ac446
minor: remove fixed version in GitVersion.yml
fslef Apr 10, 2024
03a74b4
minor: Update GitVersion.yml to use ContinuousDelivery mode
fslef Apr 10, 2024
f273903
Revert "minor: Update GitVersion.yml to use ContinuousDelivery mode"
fslef Apr 10, 2024
11a913f
minor: Update GitVersion.yml to use ContinuousDelivery mode
fslef Apr 10, 2024
ad9b3db
Bump codecov/codecov-action from 4.1.1 to 4.2.0 (#16)
dependabot[bot] Apr 10, 2024
4e5a183
fix pull request template
fslef Apr 10, 2024
3a1a131
Update pull request template
fslef Apr 10, 2024
3885aec
Update Cs Environment features (#17)
fslef Apr 10, 2024
7047197
Disable publish_module_to_gallery in build.yaml
fslef Apr 10, 2024
580c539
Add Get-CsAzGovAssignment function to retrieve Azure governance assig…
fslef Apr 10, 2024
8679d52
Refactor GitVersion.yml to use 'master' branch instead of 'main' branch
fslef Apr 10, 2024
dc381cd
Update Get-CsAzGovAssignment.ps1 to add OverdueOnly parameter to filt…
fslef Apr 10, 2024
01a9ddd
Merge branch 'main' into feat/GovAssignment
fslef Apr 10, 2024
c703510
Update next-version in GitVersion.yml to 0.6.0
fslef Apr 10, 2024
2436556
Refactor GovAssignment class to remove unnecessary null or empty checks
fslef Apr 10, 2024
7cc9119
Add OverdueOnly parameter to Get-CsAzGovAssignment.ps1 to filter results
fslef Apr 10, 2024
96095b4
Update next-version in GitVersion.yml to 0.5.0
fslef Apr 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading