Functions/New-AHPolicyExemption.ps1



Function New-AHPolicyExemption{
    [CmdletBinding()]
    param()

    Begin{
        If ('System.Management.Automation.ServerRemoteDebugger' -eq [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace.Debugger.GetType().FullName) {
            throw "This cmdlet can only be used on a local host and cannot be used from a remote session."
            return
        }
        elseif ((get-item env:/).Name -contains 'AZURE_HTTP_USER_AGENT') {
            throw "This cmdlet can only be used on a local host and cannot be used from Azure Cloud Shell."
            return
        }
#Region FancyDatePicker
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$dateForm = New-Object Windows.Forms.Form -Property @{
    StartPosition = [Windows.Forms.FormStartPosition]::CenterScreen
    Size          = New-Object Drawing.Size 243, 230
    Text          = 'Select a Date'
    Topmost       = $true
}

$calendar = New-Object Windows.Forms.MonthCalendar -Property @{
    ShowTodayCircle   = $false
    MaxSelectionCount = 1
}
$dateForm.Controls.Add($calendar)

$okButton = New-Object Windows.Forms.Button -Property @{
    Location     = New-Object Drawing.Point 38, 165
    Size         = New-Object Drawing.Size 75, 23
    Text         = 'OK'
    DialogResult = [Windows.Forms.DialogResult]::OK
}
$dateForm.AcceptButton = $okButton
$dateForm.Controls.Add($okButton)

$cancelButton = New-Object Windows.Forms.Button -Property @{
    Location     = New-Object Drawing.Point 113, 165
    Size         = New-Object Drawing.Size 75, 23
    Text         = 'Cancel'
    DialogResult = [Windows.Forms.DialogResult]::Cancel
}
$dateForm.CancelButton = $cancelButton
$dateForm.Controls.Add($cancelButton)
#EndRegion FancyDatePicker
#Region FancyDescriptionInput
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$descriptionForm = New-Object System.Windows.Forms.Form
$descriptionForm.Text = 'Data Entry Form'
$descriptionForm.Size = New-Object System.Drawing.Size(300,200)
$descriptionForm.StartPosition = 'CenterScreen'

$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Point(75,120)
$okButton.Size = New-Object System.Drawing.Size(75,23)
$okButton.Text = 'OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$descriptionForm.AcceptButton = $okButton
$descriptionForm.Controls.Add($okButton)

$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Point(150,120)
$cancelButton.Size = New-Object System.Drawing.Size(75,23)
$cancelButton.Text = 'Cancel'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$descriptionForm.CancelButton = $cancelButton
$descriptionForm.Controls.Add($cancelButton)

$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = 'Please enter the information in the space below:'
$descriptionForm.Controls.Add($label)

$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10,40)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$descriptionForm.Controls.Add($textBox)

$descriptionForm.Topmost = $true
#EndRegion FancyDescriptionInput

    }
    Process {
#Steps:
### - Get management group of policies - maybe hard code
    $ManagementGroup = Get-AzManagementGroup | Where{$_.DisplayName -eq 'Enterprise Policy'}
    If($Null -eq $ManagementGroup){
        $ManagementGroup = Get-AzManagementGroup | ogv -PassThru -Title 'Select which Management Group has the policy applied to it'
    }
### - Get Policy Initiative ID - only display policy defintions for policies that are already assigned
    
    $PolicySetDefinitionIds = (Get-AzPolicyAssignment -Scope $ManagementGroup.Id ).Properties.PolicyDefinitionId #all policy definitions for assigned policies
    #some items are policy definitions, others are policy set definitions, get a readable version of each
    #$PolicyChoices = $PolicySetDefinitionIds | Where{$_ -like "*/policyDefinitions/*"}
    $PolicySetChoices = $PolicySetDefinitionIds | Where{$_ -like "*/policySetDefinitions/*"}
    #$PolicyChoices = $PolicyChoices | %{Get-AzPolicyDefinition -Id $_ | select @{n='DisplayName';E={$_.Properties.DisplayName}}, @{n='Description';E={$_.Properties.Description}},ResourceId}
    $PolicySetChoices = $PolicySetChoices | %{Get-AzPolicySetDefinition -Id $_ | select @{n='DisplayName';E={$_.Properties.DisplayName}}, @{n='Description';E={$_.Properties.Description}},ResourceId}
    $DefinitionChoices = $PolicySetChoices #+ $PolicyChoices
    $PolicySetToAddExemptionTo = $DefinitionChoices | ogv -passthru -Title 'Select which policy to add an exemption to'
    If(($PolicySetToAddExemptionTo.gettype()).BaseType.Name -eq 'Array'){
        throw "One and only one policy set may be selected"
        return
    }
    $PolicyAssignment = (Get-AzPolicyAssignment -Scope $ManagementGroup.Id -PolicyDefinitionId $PolicySetToAddExemptionTo.ResourceId)
### - Get Policy definition within the initiative
    If($PolicySetToAddExemptionTo.ResourceId -like "*/policyDefinitions/*"){
        $PolicyDefinitionToExclude = $PolicySetToAddExemptionTo.ResourceId
        #$policyToAddExemptionTo = (Get-AzPolicyAssignment -Scope $ManagementGroup.Id -PolicyDefinitionId $PolicySetToAddExemptionTo.ResourceId)
    }Else{ #it is a policy set so we need to know which policies within the set to exempt
        #$policyToAddExemptionTo = (get-azpolicysetass)
        $PolicyDefinitionToExclude = (Get-AzPolicySetDefinition -ResourceId $PolicySetToAddExemptionTo.ResourceId).Properties.PolicyDefinitions | ogv -passthru -Title 'Select which policy/policies to add exemptions to within the initiative'
    }
### - Get Resource(s) to exclude
    $resourceTypeToExclude = (Get-AzResource | group ResourceType).Name | ogv -passthru -Title 'Select which resource type to exclude'
    $resourcesToExclude = $resourceTypeToExclude | %{get-azresource -ResourceType $_} | ogv -passthru -Title 'Select which specific resources to exclude'
    #$resourcesToExclude = Get-AzResource -ResourceType $resourceTypeToExclude
### - Define naming
# $DisplayName = "$($ResourceName) - $($PolicyAssignment.Properties.DisplayName) - $($nistControlId) - $($PolicyName)"
# $ExemptionName = $DisplayName.Substring(0,64)

#get category
$ExemptionCategory = @('Waiver','Mitigated') | ogv -passthru -Title 'Select the Exemption Category'

#get expiration date
$result = $dateForm.ShowDialog()
If($result -eq [Windows.Forms.DialogResult]::OK){
    $ExpirationDate = $calendar.SelectionStart.ToString('yyyy-MM-dd')
}Else{
    throw 'A date must be selected'
    return
}
If($ExpirationDate -gt [datetime]::now.AddDays(366)){
    write-warning "This exemption will be processed however expiration dates over 1 year in the future are discouraged."
}

#get description
$descriptionForm.Add_Shown({$textBox.Select()})
$result = $descriptionForm.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
    $description = $textBox.Text
}Else{
    throw "a description must be entered"
    return
}

### - Create splats

    $MySplats = ForEach($definitionToExclude in $PolicyDefinitionToExclude){
        ForEach($resource in $resourcesToExclude){
            $Policy = Get-AzPolicyDefinition -Id $definitionToExclude.PolicyDefinitionId
            $PolicyName =  $Policy.Properties.DisplayName

            $DisplayName = "$($resource.Name) - $($PolicyAssignment.Properties.DisplayName) - $($Policy.Properties.DisplayName)"
            $ExemptionName = $DisplayName.Substring(0,64)

            @{
                Name = $ExemptionName
                PolicyAssignment = $PolicyAssignment
                Scope = $resource.ResourceId
                ExemptionCategory = $ExemptionCategory
                DisplayName = $DisplayName
                PolicyDefinitionReferenceId = $definitionToExclude.policyDefinitionReferenceId
                ExpiresOn = $ExpirationDate
                description = $description
            }
        }

    }

### - Create exclusion

ForEach($splat in $MySplats){New-AzPolicyExemption @splat}

    }
    end{}

}