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

Add Governance Assignment cmdlets #21

Merged
merged 11 commits into from
Apr 30, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
- `Get-CsAzGovAssignment` to get azure governance assignments
- `Update-CsAzGovAssignment` to update governance assignments
- Automatic documentation generation

### Changed
Expand Down
File renamed without changes.
49 changes: 14 additions & 35 deletions src/Public/Get-CsAzGovAssignment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ function Get-CsAzGovAssignment {
[switch]$OverdueOnly
)

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

if ($OverdueOnly) {
Write-OutputPadded "OverdueOnly Parameter set" -IndentLevel 1 -Type "Verbose"
Write-OutputPadded "OverdueOnly Parameter set" -IdentLevel 1 -Type "Verbose"
$completionStatus = "'Overdue'"
}
else {
Expand All @@ -57,13 +57,13 @@ function Get-CsAzGovAssignment {
| where type =~ 'microsoft.security/assessments'
| extend assessmentType = tostring(properties.metadata.assessmentType),
assessmentId = tolower(id),
assessmentName = tostring(name),
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])
displayName = tostring(properties.displayName)
| where assessmentType == 'BuiltIn'
| extend environment = case(
source in~ ('azure', 'onpremise'), 'Azure',
Expand All @@ -79,6 +79,7 @@ function Get-CsAzGovAssignment {
securityresources
| where type == 'microsoft.security/assessments/governanceassignments'
| extend dueDate = todatetime(properties.remediationDueDate),
isGracePeriod = tobool(properties.isGracePeriod),
owner = tostring(properties.owner),
disableOwnerEmailNotification = tostring(properties.governanceEmailNotification.disableOwnerEmailNotification),
disableManagerEmailNotification = tostring(properties.governanceEmailNotification.disableManagerEmailNotification),
Expand All @@ -88,8 +89,9 @@ function Get-CsAzGovAssignment {
todatetime(properties.remediationDueDate) >= bin(now(), 1d), 'OnTime',
'Overdue'
),
assessmentId = tolower(tostring(properties.assignedResourceId))
| project dueDate, owner, disableOwnerEmailNotification, disableManagerEmailNotification, emailNotificationDayOfWeek, governanceStatus, assessmentId
assessmentId = tolower(tostring(properties.assignedResourceId)),
assignmentKey = tostring(properties.assignmentKey)
| project dueDate, isGracePeriod, owner, disableOwnerEmailNotification, disableManagerEmailNotification, governanceStatus, assessmentId, assignmentKey, emailNotificationDayOfWeek
) on assessmentId
| extend completionStatus = case(
governanceStatus == 'Overdue', 'Overdue',
Expand All @@ -98,8 +100,9 @@ function Get-CsAzGovAssignment {
'Completed'
)
| where completionStatus in~ ($completionStatus)
| project displayName, resourceId, assessmentKey, resourceType, resourceName, dueDate, owner, completionStatus
| order by completionStatus, displayName
| extend csEnvironment = '$CsEnvironment'
| project csEnvironment, resourceId,assessmentName, assignmentKey,displayName, completionStatus, resourceType, resourceName, owner, dueDate, isGracePeriod, disableOwnerEmailNotification, disableManagerEmailNotification, emailNotificationDayOfWeek
| order by completionStatus, displayName, owner
"@

$payLoad = @"
Expand All @@ -108,35 +111,11 @@ function Get-CsAzGovAssignment {
}
"@

Write-OutputPadded "Query Payload:" -Type 'debug' -IndentLevel 1 -BlankLineBefore
Write-OutputPadded "$payLoad" -Type 'data' -IndentLevel 1 -BlankLineBefore
Write-OutputPadded "Query Payload:" -Type 'debug' -IdentLevel 1 -BlankLineBefore
Write-OutputPadded "$payLoad" -Type 'data' -IdentLevel 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
}
$GovAssignments = AzAPICall -AzAPICallConfiguration $azapiCallConf -uri $uri -body $payLoad -method 'POST' -listenOn Content

return $GovAssignments
}
36 changes: 18 additions & 18 deletions src/Public/Import-CsEnvironment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ function Import-CsEnvironment {
# If the environment variable is not set, default to the CyberShell-Config.jsonc file
# in the .cybershell directory of the user's home directory
if (-not [string]::IsNullOrEmpty($JsonPath)) {
Write-OutputPadded "Config file explicitly specified in Cmdlet" -IndentLevel 1 -Type "Debug" -BlankLineBefore
Write-OutputPadded "Config file explicitly specified in Cmdlet" -IdentLevel 1 -Type "Debug" -BlankLineBefore

}
elseif ($env:CYBERSHELL_CONFIG) {
$JsonPath = $env:CYBERSHELL_CONFIG
Write-OutputPadded "Using JsonPath from CYBERSHELL_CONFIG environment variable" -IndentLevel 1 -Type "Debug"
Write-OutputPadded "Using JsonPath from CYBERSHELL_CONFIG environment variable" -IdentLevel 1 -Type "Debug"
}
else {
$JsonFolder = Join-Path $HOME ".cybershell"
$JsonPath = Join-Path $JsonFolder "CyberShell-Config.jsonc"
Write-OutputPadded "Using default JsonPath: $JsonPath" -IndentLevel 1 -Type "Debug"
Write-OutputPadded "Using default JsonPath: $JsonPath" -IdentLevel 1 -Type "Debug"
}
Write-OutputPadded "$JsonPath" -IndentLevel 1 -Type "Debug"
Write-OutputPadded "$JsonPath" -IdentLevel 1 -Type "Debug"

Write-OutputPadded "Importing configuration from Json" -IndentLevel 1 -Type "Debug" -BlankLineBefore
Write-OutputPadded "Importing configuration from Json" -IdentLevel 1 -Type "Debug" -BlankLineBefore

# Verifying the file exists
if (-Not (Test-Path $JsonPath)) {
Expand All @@ -62,10 +62,10 @@ function Import-CsEnvironment {
try {
# Convert JSON content to a hashtable for structured access
[hashtable]$rawData = $jsonContent | ConvertFrom-Json -AsHashtable
Write-OutputPadded "CyberShell environments and configuration successfully imported." -IndentLevel 1 -Type "Success"
Write-OutputPadded "CyberShell environments and configuration successfully imported." -IdentLevel 1 -Type "Success"
}
catch {
Write-OutputPadded "Failed to import CyberShell environments and configuration from JSON: $_" -IndentLevel 1 -Type "Error"
Write-OutputPadded "Failed to import CyberShell environments and configuration from JSON: $_" -IdentLevel 1 -Type "Error"
}

# Structured global data storage
Expand All @@ -75,39 +75,39 @@ function Import-CsEnvironment {
}

# Debug output for imported data
Write-OutputPadded "Environment loaded:" -IndentLevel 1 -Type "Debug" -BlankLineBefore
Write-OutputPadded "Environment: $($global:CsData.Environments | ConvertTo-Json)" -IndentLevel 1 -Type "Data"
Write-OutputPadded "Environment loaded:" -IdentLevel 1 -Type "Debug" -BlankLineBefore
Write-OutputPadded "Environment: $($global:CsData.Environments | ConvertTo-Json)" -IdentLevel 1 -Type "Data"

# debug output for settings

# if settings exist, output them
if ($global:CsData.Settings) {
Write-OutputPadded "Settings loaded:" -IndentLevel 1 -Type "Debug" -BlankLineBefore
Write-OutputPadded "Settings: $($global:CsData.Settings | ConvertTo-Json) " -IndentLevel 1 -Type "Data"
Write-OutputPadded "Settings loaded:" -IdentLevel 1 -Type "Debug" -BlankLineBefore
Write-OutputPadded "Settings: $($global:CsData.Settings | ConvertTo-Json) " -IdentLevel 1 -Type "Data"
}
else {
Write-OutputPadded "No settings found in the config file." -IndentLevel 1 -Type "Debug"
Write-OutputPadded "No settings found in the config file." -IdentLevel 1 -Type "Debug"
}


# CSData.settings.ExecutionPreference exist then set the script execution preference
if ($global:CsData.Settings.ExecutionPreference) {
Set-ScriptExecutionPreference -ExecutionPreference $global:CsData.Settings.ExecutionPreference
Write-OutputPadded "Script execution preference set to $($global:CsData.Settings.ExecutionPreference)" -IndentLevel 1 -Type "Debug"
Write-OutputPadded "Script execution preference set to $($global:CsData.Settings.ExecutionPreference)" -IdentLevel 1 -Type "Debug"
Write-OutputPadded " " -Type "Debug"
}


If ($global:CsData.Settings.ExecutionPreference -eq "Debug") {
Write-OutputPadded "Debug Informations:" -IndentLevel 1 -Type "Debug"
Write-OutputPadded "Debug Informations:" -IdentLevel 1 -Type "Debug"
}
else {
Write-OutputPadded "Debug and verbose Informations:" -IndentLevel 1 -Type "Debug"
Write-OutputPadded "Debug and verbose Informations:" -IdentLevel 1 -Type "Debug"
}

Write-OutputPadded "JsonPath: $JsonPath" -IndentLevel 2 -Type "Debug"
Write-OutputPadded "JsonPath: $JsonPath" -IdentLevel 2 -Type "Debug"

Write-OutputPadded "Imported CyberShell Data:" -IndentLevel 2 -Type "Debug" -BlankLineBefore
Write-OutputPadded "$(ConvertTo-Json $global:CsData -Depth 20)" -IndentLevel 2 -Type "Data"
Write-OutputPadded "Imported CyberShell Data:" -IdentLevel 2 -Type "Debug" -BlankLineBefore
Write-OutputPadded "$(ConvertTo-Json $global:CsData -Depth 20)" -IdentLevel 2 -Type "Data"

}
159 changes: 159 additions & 0 deletions src/Public/Update-CsAzGovAssignment.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
function Update-CsAzGovAssignment {
<#
.SYNOPSIS
Updates an Azure Governance Assignment.

.DESCRIPTION
The Update-CsAzGovAssignment function updates an Azure Governance Assignment with the provided parameters.

.PARAMETER azAPICallConf
A hashtable containing the configuration for the Azure API call.

.PARAMETER resourceId
The unique identifier of the resource.

.PARAMETER AssessmentName
The name of the assessment.

.PARAMETER assignmentKey
The key of the assignment.

.PARAMETER RemediationDueDate
The due date for remediation. This is optional.

.PARAMETER IsGracePeriod
Indicates whether there is a grace period. This is optional.

.PARAMETER OwnerEmailAddress
The email address of the owner. This is optional.

.PARAMETER OwnerEmailNotification
Indicates whether the owner will receive email notifications. This is optional.

.PARAMETER ManagerEmailNotification
Indicates whether the manager will receive email notifications. This is optional.

.PARAMETER NotificationDayOfWeek
The day of the week when notifications will be sent. This is optional and must be one of 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'.

.EXAMPLE
$azAPICallConf = @{...}
Update-CsAzGovAssignment -azAPICallConf $azAPICallConf -resourceId "resourceId" -AssessmentName "AssessmentName" -assignmentKey "assignmentKey"

This example updates an Azure Governance Assignment with the provided parameters.
#>
[CmdletBinding(SupportsShouldProcess)]
param (

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

[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[string]$resourceId,

[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[string]$AssessmentName,

[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
[string]$assignmentKey,

[Parameter(Mandatory = $false)]
[datetime]$RemediationDueDate,

[Parameter(Mandatory = $false)]
[object]$IsGracePeriod,

[Parameter(Mandatory = $false)]
[string]$OwnerEmailAddress,

[Parameter(Mandatory = $false)]
[bool]$OwnerEmailNotification,

[Parameter(Mandatory = $false)]
[bool]$ManagerEmailNotification,

[Parameter(Mandatory = $false)]
[Validateset('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')]
[string]$NotificationDayOfWeek

)



begin {
# if the input is not from pipeline, then create a custom object with a single entry
Write-OutputPadded "Update Azure Governance Assignment" -Type 'information' -isTitle -BlankLineBefore
}

process {

Write-OutputPadded "Processing Azure Governance Assignment" -Type 'information' -IdentLevel 1 -BlankLineBefore
Write-OutputPadded "Resource Id: $resourceId" -Type 'information' -IdentLevel 1
Write-OutputPadded "Assessment Name: $AssessmentName" -Type 'Verbose' -IdentLevel 1
Write-OutputPadded "Assignment Key: $assignmentKey" -Type 'Verbose' -IdentLevel 1

$uri = "$($azAPICallConf.azAPIEndpointUrls.ARM)$resourceId/providers/microsoft.security/assessments/$AssessmentName/governanceAssignments/$assignmentKey/?api-version=2021-06-01"

# get the existing assignment
$govassessment = AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method 'GET' -listenOn Content


if ($RemediationDueDate) {
# Update the remediationDueDate
$RemediationDueDate = $RemediationDueDate.ToString('yyyy-MM-dd')
Write-OutputPadded "New remediation Due Date: $RemediationDueDate" -Type 'Verbose' -IdentLevel 1
$govassessment.properties.remediationDueDate = $RemediationDueDate
}

if ($IsGracePeriod) {
# Update the isGracePeriod
Write-OutputPadded "New isGracePeriod: $IsGracePeriod" -Type 'Verbose' -IdentLevel 1
$govassessment.properties.isGracePeriod = $IsGracePeriod
}

if ($OwnerEmailAddress) {
# Update the owner
Write-OutputPadded "New Owner Email Address: $OwnerEmailAddress" -Type 'Verbose' -IdentLevel 1
$govassessment.properties.owner = $OwnerEmailAddress
}

if ($OwnerEmailNotification) {
# Update the disableOwnerEmailNotification
Write-OutputPadded "New Owner Email Notification: $OwnerEmailNotification" -Type 'Verbose' -IdentLevel 1
$govassessment.properties.governanceEmailNotification.disableOwnerEmailNotification = $OwnerEmailNotification
}

if ($ManagerEmailNotification) {
# Update the disableManagerEmailNotification
Write-OutputPadded "New Manager Email Notification: $ManagerEmailNotification" -Type 'Verbose' -IdentLevel 1
$govassessment.properties.governanceEmailNotification.disableManagerEmailNotification = $ManagerEmailNotification
}

if ($NotificationDayOfWeek) {
# Update the emailNotificationDayOfWeek
Write-OutputPadded "New Email Notification Day Of Week: $NotificationDayOfWeek" -Type 'Verbose' -IdentLevel 1
$govassessment.properties.governanceEmailNotification.emailNotificationDayOfWeek = $NotificationDayOfWeek
}

# Convert the updated object to JSON
$jsonBody = $govassessment | ConvertTo-Json -Depth 10
Write-OutputPadded "Updated JSON Body:" -Type 'data' -IdentLevel 1
Write-OutputPadded "$jsonBody" -Type 'data' -IdentLevel 1

# Update the assignment
Write-OutputPadded "Updating Azure Governance Assignment" -Type 'debug' -IdentLevel 1


try {
# Check if the operation should proceed using ShouldProcess
if ($PSCmdlet.ShouldProcess("$uri", "Update Azure Governance Assignment")) {
AzAPICall -AzAPICallConfiguration $azAPICallConf -uri $uri -method 'PUT' -body $jsonBody -listenOn Content
Write-OutputPadded "Azure Governance Assignment Updated Successfully" -Type 'success' -IdentLevel 1
}
}
# Catch block to handle any exceptions that occur during the update operation
catch {
Write-OutputPadded "Failed to update Azure Governance Assignment: $_" -Type 'error' -IdentLevel 1
}
}
}
Loading
Loading