Skip to content

Commit

Permalink
Merge pull request #12 from KelvinTegelaar/master
Browse files Browse the repository at this point in the history
[pull] master from KelvinTegelaar:master
  • Loading branch information
pull[bot] authored Nov 30, 2023
2 parents 3efedcb + 73e032d commit 7c6a102
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 4 deletions.
67 changes: 67 additions & 0 deletions ExecMaintenanceScripts/Scripts/Add-CippUser.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#requires -Version 7.2

[CmdletBinding(DefaultParameterSetName = 'interactive')]
Param(
[Parameter(Mandatory = $true, ParameterSetName = 'noninteractive')]
[ValidateSet('readonly', 'editor', 'admin')]
$Role,
[Parameter(Mandatory = $true, ParameterSetName = 'noninteractive')]
$SelectedUsers,
[Parameter(ParameterSetName = 'noninteractive')]
[Parameter(ParameterSetName = 'interactive')]
$ExpirationHours = 1
)

$ResourceGroup = '##RESOURCEGROUP##'
$Subscription = '##SUBSCRIPTION##'

if (!(Get-Module -ListAvailable Microsoft.PowerShell.ConsoleGuiTools)) {
Install-Module Microsoft.PowerShell.ConsoleGuiTools -Force
}

$Context = Get-AzContext
if (!$Context) {
Write-Host "`n- Connecting to Azure"
$Context = Connect-AzAccount -Subscription $Subscription
}
Write-Host "Connected to $($Context.Account)"

$swa = Get-AzStaticWebApp -ResourceGroupName $ResourceGroup
$Domain = $swa.CustomDomain | Select-Object -First 1
if ($Domain -eq $null) { $Domain = $swa.DefaultHostname }
Write-Host "CIPP SWA - $($swa.name)"

if (!$Role) {
$Role = @('readonly', 'editor', 'admin') | Out-ConsoleGridView -OutputMode Single -Title 'Select CIPP Role'
}

$CurrentUsers = Get-AzStaticWebAppUser -Name $swa.name -ResourceGroupName $ResourceGroup -AuthProvider all | Select-Object DisplayName, Role

$AllUsers = Get-AzADUser -Filter "userType eq 'Member' and accountEnabled eq true" | Select-Object DisplayName, UserPrincipalName


$SelectedUsers = $AllUsers | Where-Object { $CurrentUsers.DisplayName -notcontains $_.UserPrincipalName } | Sort-Object -Property DisplayName | Out-ConsoleGridView -Title "Select users for role '$Role'"
Write-Host "Selected users: $($SelectedUsers.UserPrincipalName -join ', ')"

Write-Host 'Generating invite links...'
$InviteList = foreach ($User in $SelectedUsers) {
$UserInvite = @{
InputObject = $swa
Domain = $Domain
Provider = 'aad'
UserDetail = $User.UserPrincipalName
Role = $Role
NumHoursToExpiration = $ExpirationHours
}
$Invite = New-AzStaticWebAppUserRoleInvitationLink @UserInvite

[PSCustomObject]@{
User = $User.UserPrincipalName
Role = $Role
Link = $Invite.InvitationUrl
Expires = $Invite.ExpiresOn
}
}
$InviteList
$InviteList | Export-Csv -Path '.\cipp-invites.csv' -Append
Write-Host 'Invitations exported to .\cipp-invites.csv'
38 changes: 38 additions & 0 deletions ExecMaintenanceScripts/Scripts/Enable-FunctionAppGitHubActions.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
$ResourceGroup = '##RESOURCEGROUP##'
$Subscription = '##SUBSCRIPTION##'
$FunctionName = '##FUNCTIONAPP##'

$Logo = @'
_____ _____ _____ _____
/ ____|_ _| __ \| __ \
| | | | | |__) | |__) |
| | | | | ___/| ___/
| |____ _| |_| | | |
\_____|_____|_| |_|
'@
Write-Host $Logo

Write-Host '- Connecting to Azure'
Connect-AzAccount -Identity -Subscription $Subscription | Out-Null

Write-Host 'Checking deployment settings'
$DeploymentSettings = & az functionapp deployment source show --resource-group $ResourceGroup --name $FunctionName | ConvertFrom-Json

if (!($DeploymentSettings.isGitHubAction)) {
Write-Host 'Creating GitHub action, follow the prompts to log into GitHub'
$GitHubRepo = ([uri]$DeploymentSettings.repoUrl).LocalPath.TrimStart('/')
az functionapp deployment github-actions add --repo $GitHubRepo --branch $DeploymentSettings.branch --resource-group $ResourceGroup --name $FunctionName --login-with-github
}

$DeploymentSettings = & az functionapp deployment source show --resource-group $ResourceGroup --name $FunctionName | ConvertFrom-Json
if ($DeploymentSettings.isGitHubAction) {
$cipp = Get-AzFunctionApp -ResourceGroupName $ResourceGroup
$cipp.ApplicationSettings['WEBSITE_RUN_FROM_PACKAGE'] = 1
$cipp | Update-AzFunctionAppSetting -AppSetting $cipp.ApplicationSettings

Write-Host "GitHub action created and project set to run from package, navigate to $($DeploymentSettings.repoUrl)/actions and run the 'Build and deploy Powershell project to Azure Function App'"
}
else {
Write-Host 'GitHub action not set up for deployment, try running the script again.'
}
122 changes: 122 additions & 0 deletions ExecMaintenanceScripts/Scripts/Grant-CippConditionalAccess.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
if (!(Get-Module -ListAvailable Microsoft.Graph)) {
Install-Module Microsoft.Graph -Confirm:$false -Force -AllowPrerelease
}

$ResourceGroup = '##RESOURCEGROUP##'
$Subscription = '##SUBSCRIPTION##'
$FunctionName = '##FUNCTIONAPP##'
$TokenIP = '##TOKENIP##'

$Logo = @'
_____ _____ _____ _____
/ ____|_ _| __ \| __ \
| | | | | |__) | |__) |
| | | | | ___/| ___/
| |____ _| |_| | | |
\_____|_____|_| |_|
'@
Write-Host $Logo

Write-Host '=== Conditional Access Management ==='
if (Test-Path -Path '.\cipp-function-namedLocation.json') {
$UseCache = Read-Host -Prompt 'Used cached Named Location for CIPP? (Y/n)'
if ($UseCache -ne 'n') {
$ipNamedLocation = Get-Content -Path '.\cipp-function-namedLocation.json' | ConvertFrom-Json -AsHashtable
}
}

if (!($ipNamedLocation)) {
Write-Host "`n- Connecting to Azure"
Connect-AzAccount -Identity -Subscription $Subscription | Out-Null
$Function = Get-AzFunctionApp -ResourceGroupName $ResourceGroup -Name $FunctionName

Write-Host 'Getting Function App IP addresses'
# Get possible IPs from function app
$PossibleIpAddresses = (($Function | Select-Object -ExpandProperty PossibleOutboundIpAddress) + ',' + $TokenIP) -split ','

# Convert possible IP addresses to ipv4CidrRange list
$ipRanges = foreach ($Ip in $PossibleIpAddresses) {
$Cidr = '{0}/32' -f $Ip
@{
'@odata.type' = '#microsoft.graph.iPv4CidrRange'
'cidrAddress' = $Cidr
}
}

# Return ipNamedLocation object
$ipNamedLocation = @{
'@odata.type' = '#microsoft.graph.ipNamedLocation'
displayName = ('CyberDrain Improved Partner Portal - {0}' -f $FunctionName)
isTrusted = $true
ipRanges = $ipRanges
}

$ipNamedLocation | ConvertTo-Json -Depth 10 | Out-File -Path '.\cipp-function-namedLocation.json'
Write-Host 'Named location policy created and saved to .\cipp-function-namedLocation.json'
}

Write-Host "`n- Connecting to Customer Graph API, ensure you log in from a system that is allowed through the Conditional Access policy"
Select-MgProfile -Name 'beta'
$GraphOptions = @{
Scopes = @('Policy.Read.All', 'Policy.ReadWrite.ConditionalAccess', 'Application.Read.All')
UseDeviceAuthentication = $true
}

do {
Connect-MgGraph @GraphOptions
$Context = Get-MgContext
if ($Context) {
Write-Host "Connected as $($Context.Account) ($($Context.TenantId))"
$Switch = Read-Host -Prompt 'Switch Accounts? (y/N)'
if ($Switch -eq 'y') {
Disconnect-MgGraph | Out-Null
}
}
}
while (!(Get-MgContext))

Write-Host "`n- Getting existing policies"
$Policies = Get-MgIdentityConditionalAccessPolicy
Write-Host($Policies.displayName -join "`n")

Write-Host "`n- Named Location Check"
$NamedLocations = Get-MgIdentityConditionalAccessNamedLocation
if ($NamedLocations.displayName -notcontains $ipNamedLocation.displayName) {
Write-Host "Creating Named Location: '$($ipNamedLocation.displayName)'"
$NamedLocation = New-MgIdentityConditionalAccessNamedLocation -BodyParameter $ipNamedLocation
}
else {
$NamedLocation = $NamedLocations | Where-Object { $_.displayName -eq $ipNamedLocation.displayName }
Write-Host "Named Location exists: '$($NamedLocation.displayName)'"
Update-MgIdentityConditionalAccessNamedLocation -NamedLocationId $NamedLocation.Id -BodyParameter $ipNamedLocation
}

Write-Host "`n- Conditional access policy check"
$ConfigPolicy = Read-Host -Prompt 'Exclude CIPP from existing CA policies? (Y/n)'
if ($ConfigPolicy -ne 'n') {
foreach ($Policy in $Policies) {
Write-Host "- Policy: $($Policy.displayName)"
$Conditions = $Policy.Conditions
$ExcludeLocations = $Conditions.Locations.ExcludeLocations
$IncludeLocations = $Conditions.Locations.IncludeLocations
if ($ExcludeLocations -eq 'AllTrusted' -or $ExcludeLocations -contains $NamedLocation.Id) {
Write-Host 'Named location already excluded'
}
elseif ($IncludeLocations -eq 'AllTrusted' -or $IncludeLocations -contains $NamedLocation.Id) {
Write-Host 'Named location is already included'
}
else {
Write-Host 'Adding exclusion for named location'
$Locations = [system.collections.generic.list[string]]::new()
foreach ($Location in $ExcludeLocations) {
$Locations.Add($Location) | Out-Null
}
$Locations.Add($NamedLocation.Id) | Out-Null
$Conditions.Locations.ExcludeLocations = [string[]]$Locations
if (!($Conditions.Locations.IncludeLocations)) { $Conditions.Locations.IncludeLocations = 'All' }
Update-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $Policy.Id -Conditions $Conditions
}
}
Write-Host "`nDone."
}
10 changes: 10 additions & 0 deletions ExecScheduledCommand/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"bindings": [
{
"name": "QueueItem",
"type": "queueTrigger",
"direction": "in",
"queueName": "scheduledcommandprocessor"
}
]
}
86 changes: 86 additions & 0 deletions ExecScheduledCommand/run.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Input bindings are passed in via param block.
param($QueueItem, $TriggerMetadata)

$Table = Get-CippTable -tablename 'ScheduledTasks'
$task = $QueueItem.TaskInfo
$commandParameters = $QueueItem.Parameters

$tenant = $QueueItem.Parameters['TenantFilter']
Write-Host 'started task'
try {
try {
$results = & $QueueItem.command @commandParameters
}
catch {
$results = "Task Failed: $($_.Exception.Message)"

}

Write-Host 'ran the command'
if ($results -is [String]) {
$results = @{ Results = $results }
}
if ($results -is [array]) {
$results = $results | Where-Object { $_ -is [string] }
$results = $results | ForEach-Object { @{ Results = $_ } }
}

$results = $results | Select-Object * -ExcludeProperty RowKey, PartitionKey

$StoredResults = $results | ConvertTo-Json -Compress -Depth 20 | Out-String
if ($StoredResults.Length -gt 64000 -or $task.Tenant -eq 'AllTenants') {
$StoredResults = @{ Results = 'The results for this query are too long to store in this table, or the query was meant for All Tenants. Please use the options to send the results to another target to be able to view the results. ' } | ConvertTo-Json -Compress
}
}
catch {
$errorMessage = $_.Exception.Message
if ($task.Recurrence -gt 0) { $State = 'Failed - Planned' } else { $State = 'Failed' }
Update-AzDataTableEntity @Table -Entity @{
PartitionKey = $task.PartitionKey
RowKey = $task.RowKey
Results = "$errorMessage"
TaskState = $State
}
Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Failed to execute task $($task.Name): $errorMessage" -sev Error
}


$TableDesign = '<style>table.blueTable{border:1px solid #1C6EA4;background-color:#EEE;width:100%;text-align:left;border-collapse:collapse}table.blueTable td,table.blueTable th{border:1px solid #AAA;padding:3px 2px}table.blueTable tbody td{font-size:13px}table.blueTable tr:nth-child(even){background:#D0E4F5}table.blueTable thead{background:#1C6EA4;background:-moz-linear-gradient(top,#5592bb 0,#327cad 66%,#1C6EA4 100%);background:-webkit-linear-gradient(top,#5592bb 0,#327cad 66%,#1C6EA4 100%);background:linear-gradient(to bottom,#5592bb 0,#327cad 66%,#1C6EA4 100%);border-bottom:2px solid #444}table.blueTable thead th{font-size:15px;font-weight:700;color:#FFF;border-left:2px solid #D0E4F5}table.blueTable thead th:first-child{border-left:none}table.blueTable tfoot{font-size:14px;font-weight:700;color:#FFF;background:#D0E4F5;background:-moz-linear-gradient(top,#dcebf7 0,#d4e6f6 66%,#D0E4F5 100%);background:-webkit-linear-gradient(top,#dcebf7 0,#d4e6f6 66%,#D0E4F5 100%);background:linear-gradient(to bottom,#dcebf7 0,#d4e6f6 66%,#D0E4F5 100%);border-top:2px solid #444}table.blueTable tfoot td{font-size:14px}table.blueTable tfoot .links{text-align:right}table.blueTable tfoot .links a{display:inline-block;background:#1C6EA4;color:#FFF;padding:2px 8px;border-radius:5px}</style>'
$HTML = ($results | Select-Object * -ExcludeProperty RowKey, PartitionKey | ConvertTo-Html -Fragment) -replace '<table>', "$TableDesign<table class=blueTable>" | Out-String
$title = "Scheduled Task $($task.Name) - $($task.ExpectedRunTime)"
Write-Host $title
switch -wildcard ($task.PostExecution) {
'*psa*' { Send-CIPPAlert -Type 'psa' -Title $title -HTMLContent $HTML }
'*email*' { Send-CIPPAlert -Type 'email' -Title $title -HTMLContent $HTML }
'*webhook*' {
$Webhook = [PSCustomObject]@{
'Tenant' = $tenant
'TaskInfo' = $QueueItem.TaskInfo
'Results' = $Results
}
Send-CIPPAlert -Type 'webhook' -Title $title -JSONContent $($Webhook | ConvertTo-Json -Depth 20)
}
}

Write-Host 'ran the command'

if ($task.Recurrence -le '0' -or $task.Recurrence -eq $null) {
Update-AzDataTableEntity @Table -Entity @{
PartitionKey = $task.PartitionKey
RowKey = $task.RowKey
Results = "$StoredResults"
TaskState = 'Completed'
}
}
else {
$nextRun = (Get-Date).AddDays($task.Recurrence)
$nextRunUnixTime = [int64]($nextRun - (Get-Date '1/1/1970')).TotalSeconds
Update-AzDataTableEntity @Table -Entity @{
PartitionKey = $task.PartitionKey
RowKey = $task.RowKey
Results = "$StoredResults"
TaskState = 'Planned'
ScheduledTime = "$nextRunUnixTime"
}
}
Write-LogMessage -API 'Scheduler_UserTasks' -tenant $tenant -message "Successfully executed task: $($task.name)" -sev Info
1 change: 0 additions & 1 deletion Modules/CIPPCore/Public/GraphHelper/Convert-SKUName.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
function Convert-SKUname($skuname, $skuID) {
Set-Location (Get-Item $PSScriptRoot).FullName
$ConvertTable = Import-Csv Conversiontable.csv
if ($skuname) { $ReturnedName = ($ConvertTable | Where-Object { $_.String_Id -eq $skuname } | Select-Object -Last 1).'Product_Display_Name' }
if ($skuID) { $ReturnedName = ($ConvertTable | Where-Object { $_.guid -eq $skuid } | Select-Object -Last 1).'Product_Display_Name' }
Expand Down
1 change: 0 additions & 1 deletion Modules/CIPPCore/Public/GraphHelper/New-passwordString.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ function New-passwordString {
param (
[int]$count = 12
)
Set-Location (Get-Item $PSScriptRoot).FullName
$SettingsTable = Get-CippTable -tablename 'Settings'
$PasswordType = (Get-CIPPAzDataTableEntity @SettingsTable).passwordType
if ($PasswordType -eq 'Correct-Battery-Horse') {
Expand Down
2 changes: 1 addition & 1 deletion Modules/CippEntrypoints/CippEntrypoints.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function Receive-CippHttpTrigger {
function Receive-CippQueueTrigger {
Param($QueueItem, $TriggerMetadata)
$APIName = $TriggerMetadata.FunctionName

Set-Location (Get-Item $PSScriptRoot).Parent.Parent.FullName
$FunctionName = 'Push-{0}' -f $APIName
$QueueTrigger = @{
QueueItem = $QueueItem
Expand Down
2 changes: 1 addition & 1 deletion version_latest.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.7.3
4.7.4

0 comments on commit 7c6a102

Please sign in to comment.