Compare commits

...

7 Commits

7 changed files with 398 additions and 7 deletions

View File

@ -0,0 +1,234 @@
#region Private Helpers
<#
.SYNOPSIS
Tests if a PnP Group exists.
.DESCRIPTION
This function tests if a PnP Group exists in the current SharePoint site.
.PARAMETER Identity
The identity of the group to test.
.EXAMPLE
Test-PnPGroup -Identity "MyGroup"
#>
function Test-PnPGroup {
param(
[string]$Identity
)
try {
Get-PnPGroup -Identity $Identity -ErrorAction Stop | Out-Null
return $true
} catch {
return $false
}
}
<#
.SYNOPSIS
Tests if an Entra ID Group exists and PnP can resolve it.
.DESCRIPTION
This function tests if a Entra ID Group exists and can be resolved by PnP.
.PARAMETER Identity
The identity of the group to test.
.EXAMPLE
Test-EntraIdGroup -Identity "MyGroup"
#>
function Test-EntraIdGroup {
param(
[string]$Identity
)
try {
Get-PnPEntraIdGroup -Identity $Identity -ErrorAction Stop | Out-Null
return $true
} catch {
return $false
}
}
#endregion
#region Public Functions
<#
.SYNOPSIS
Sets permissions on a folder in a SharePoint document library.
.DESCRIPTION
This script breaks permission inheritance on a specified folder in a SharePoint document library and assigns permissions to a specified owner group and additional groups defined in the ACL parameter.
.PARAMETER Name
The name of the folder to set permissions on.
.PARAMETER List
The name of the document library containing the folder. Default is 'Shared Documents'.
.PARAMETER Owner
The name of the SharePoint group to assign as the owner of the folder with 'Full Control' permissions.
.PARAMETER Acl
An array of objects defining additional groups and their permissions to assign to the folder. Each object should have a 'DisplayName' property for the group name and a 'Role' property for the permission level (e.g., 'Read', 'Edit').
.EXAMPLE
$Acl = @(
@{ DisplayName = "SG-ADMIN-AdvocateFloats-Dynamic"; Role = "Edit" },
@{ DisplayName = "SG-ADMIN-AdvocateManagers-Dynamic"; Role = "Edit" }
)
.\Set-PnPFolderAcl.ps1 -Name "ProjectX" -List "Shared Documents" -Owner "Project Owners" -Acl $Acl
#>
function Set-PnPFolderAcl {
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact = 'High'
)]
param(
[Parameter(
Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName
)]
[string]$FolderName,
[Parameter()]
[string]$List = 'Shared Documents',
[Parameter(Mandatory)]
[string]$Owner,
[Parameter(Mandatory)]
[pscustomobject[]]$Acl
)
begin {
if (-not (Get-PnPContext)) {
Throw "Not connected to a SharePoint site. Run Connect-PnPOnline first."
}
if (-not (Get-PnPList -Identity $List -ErrorAction SilentlyContinue)) {
Throw "The specified list '$List' does not exist."
}
if (-not (Test-PnPGroup -Identity $Owner)) {
Throw "The specified owner group '$Owner' does not exist."
}
$ValidRoles = Get-PnPRoleDefinition | Select-Object -ExpandProperty Name
if (-not $ValidRoles) {
throw "Unable to retrieve SharePoint role definitions."
}
foreach ($entry in $Acl) {
if (-not ($entry.PSObject.Properties.Name -contains 'Group' -and
$entry.PSObject.Properties.Name -contains 'Role')) {
Throw "Each ACL entry must contain 'Group' and 'Role' properties."
}
if (-not (Test-PnPGroup -Identity $entry.Group) -and
-not (Test-EntraIdGroup -Identity $entry.Group)) {
Throw "The specified group '$($entry.Group)' does not exist as a PnP Group or Entra ID Group."
}
if ($entry.Role -notin $ValidRoles) {
Throw "Invalid role '$($entry.Role)' specified for group '$($entry.Group)'. Valid roles are: $($ValidRoles -join ', ')."
}
}
}
process {
$FolderPath = "$List/$FolderName"
# Retrieve folder and current permissions
$FolderItem = Get-PnPFolder -Url $FolderPath -ErrorAction Stop
$ListItem = Get-PnPListItem -List $List -Id $FolderItem.ListItemAllFields.Id
# Break inheritance ONLY IF the folder doesn't already have unique permissions
if (-not $ListItem.HasUniqueRoleAssignments) {
if ($PSCmdlet.ShouldProcess(
$FolderPath,
"Break inheritance and grant Full Control to '$Owner' on '$FolderPath'."
)) {
Write-Verbose "Breaking inheritance for '$FolderPath'."
Write-Verbose "Granting 'Full Control' to '$Owner' on '$FolderPath'."
Set-PnPFolderPermission `
-List $List `
-Identity $FolderPath `
-Group $Owner `
-AddRole 'Full Control' `
-ClearExisting
}
}
# Get existing role assignments
$RoleAssignments = Get-PnPProperty -ClientObject $ListItem -Property RoleAssignments
$CurrentAcl = foreach ($ra in $RoleAssignments) {
$principal = Get-PnPProperty -ClientObject $ra -Property Member
$bindings = Get-PnPProperty -ClientObject $ra -Property RoleDefinitionBindings
foreach ($binding in $bindings) {
[pscustomobject]@{
Principal = $principal.Title
Role = $binding.Name
}
}
}
# foreach ($entry in $CurrentAcl) {
# if ($entry.Principal -eq $Owner) {
# continue
# }
# if
# }
foreach ($entry in $Acl) {
$GroupName = $entry.Group
$Role = $entry.Role
$AlreadyAssigned = $ResolvedRoles | Where-Object {
$_.Principal -eq $GroupName -and
$_.Role -eq $Role
}
if ($AlreadyAssigned) {
Write-Verbose "Permission '$Role' already assigned to '$GroupName' on '$FolderPath'. Skipping."
continue
}
if (Test-PnPGroup $GroupName) {
if ($PSCmdlet.ShouldProcess(
$FolderPath,
"Grant '$Role' to SharePoint group '$GroupName' on '$FolderPath'."
)) {
Write-Verbose "Granting '$Role' to SharePoint group '$GroupName' on '$FolderPath'."
Set-PnPFolderPermission `
-List $List `
-Identity $FolderPath `
-Group $GroupName `
-AddRole $Role
}
} elseif (Test-EntraIdGroup $GroupName) {
if ($PSCmdlet.ShouldProcess(
$FolderPath,
"Grant '$Role' to Entra ID group '$GroupName' on '$FolderPath'."
)) {
Write-Verbose "Granting '$Role' to Entra ID group '$GroupName' on '$FolderPath'."
Set-PnPFolderPermission `
-List $List `
-Identity $FolderPath `
-Group $GroupName `
-AddRole $Role
}
}
}
}
}
#endregion
Export-ModuleMember -Function Set-PnPFolderAcl

View File

@ -0,0 +1,33 @@
param (
[Parameter(Mandatory=$true)]
[string]
$UserId
)
$VerbosePreference = 'Continue'
Connect-MgGraph -NoWelcome -Scopes User.ReadWrite.All,Organization.Read.All
$User = Get-MgUser -UserId $UserId -Property DisplayName,UserPrincipalName,Id,OnPremisesImmutableId | Select-Object -Property DisplayName,UserPrincipalName,Id,OnPremisesImmutableId
Write-Host "User identified to remove is '$($User.DisplayName) ($($User.UserPrincipalName))'"
do {
$Decision = Read-Host "Continue? (y/n)"
if ($Decision -cmatch '[Nn](?:o)?') { exit }
} while ($Decision -cnotmatch '[Yy](?:es)?')
Write-Host "Removing OnPremisesImmutableId..."
Invoke-MgGraphRequest -Method PATCH -Uri "https://graph.microsoft.com/v1.0/Users/$($User.Id)" -Body @{OnPremisesImmutableId = $null}
Write-Verbose "OnPremisesImmutableId: '$($User.OnPremisesImmutableId)' -> '$((Get-MgUser -UserId $UserId -Property OnPremisesImmutableId).OnPremisesImmutableId)'"
Write-Host "Starting ADSync Delta sync... " -NoNewline
try {
if ($Configuration.RemoteAdSync) {
Invoke-Command -ComputerName $Configuration.RemoteAdSyncComputerName -ScriptBlock { (Start-AdSyncSyncCycle -PolicyType Delta).Result }
} else {
(Start-AdSyncSyncCycle -PolicyType Delta).Result
}
} catch {
Write-Host -ForegroundColor Red "Failed: $_"
break
}

107
m365/Set-License.psm1 Normal file
View File

@ -0,0 +1,107 @@
<#
.SYNOPSIS
Replaces a license for Microsoft 365 users.
.DESCRIPTION
This script replaces a specified license (SKU) with another license for Microsoft 365 users.
.PARAMETER InputObject
The Microsoft Graph user objects to process.
.PARAMETER RemoveSkuPartNumber
The SKU Part Number of the license to be removed.
.PARAMETER RemoveSkuId
The SKU Id of the license to be removed.
.PARAMETER AddSkuPartNumber
The SKU Part Number of the license to be added.
.PARAMETER AddSkuId
The SKU Id of the license to be added.
.PARAMETER All
If specified, all users with the license to be removed will be processed.
.PARAMETER WhatIf
If specified, the actions will be displayed without making any changes.
#>
function Set-License {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline=$true)]
[array]
$InputObject,
[Parameter(Mandatory=$true,
ParameterSetName='SkuPartNumber')]
[string]
$RemoveSkuPartNumber,
[Parameter(Mandatory=$true,
ParameterSetName='SkuId')]
[guid]
$RemoveSkuId,
[Parameter(Mandatory=$true,
ParameterSetName='SkuPartNumber')]
[string]
$AddSkuPartNumber,
[Parameter(Mandatory=$true,
ParameterSetName='SkuId')]
[guid]
$AddSkuId,
[Parameter()]
[switch]
$All,
[Parameter()]
[switch]
$WhatIf
)
begin {
if ($null -eq (Get-MgContext)) {
throw 'You are not connected to Microsoft Graph. Please connect using Connect-MgGraph.'
}
if ($PSCmdlet.ParameterSetName -eq 'SkuPartNumber') {
# Validate that the Remove SKU exists and get its Id
$RemoveSku = (Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq $RemoveSkuPartNumber })
if ($null -eq $RemoveSku) {
throw "The SKU Part Number '$RemoveSkuPartNumber' was not found."
}
$RemoveSkuId = $RemoveSku.SkuId
# Validate that the Add SKU exists and get its Id
$AddSku = (Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq $AddSkuPartNumber })
if ($null -eq $AddSku) {
throw "The SKU Part Number '$AddSkuPartNumber' was not found."
}
$AddSkuId = $AddSku.SkuId
} elseif ($PSCmdlet.ParameterSetName -eq 'SkuId') {
# Validate that the Remove SKU exists
$RemoveSku = (Get-MgSubscribedSku -All | Where-Object { $_.SkuId -eq $RemoveSkuId })
if ($null -eq $RemoveSku) {
throw "The SKU Id '$RemoveSkuId' was not found."
}
# Validate that the Add SKU exists
$AddSku = (Get-MgSubscribedSku -All | Where-Object { $_.SkuId -eq $AddSkuId })
if ($null -eq $AddSku) {
throw "The SKU Id '$AddSkuId' was not found."
}
}
}
process {
$Users = @()
if ($All) {
# Get all users with the old SKU assigned
$Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $RemoveSkuId)" -All
} else {
# Get users from the pipeline with the old SKU assigned
$Users += (Get-MgUser -UserId $InputObject.Id -Property Id,DisplayName,UserPrincipalName,AssignedLicenses) | Where-Object { $RemoveSkuId -in $_.AssignedLicenses.SkuId }
}
foreach ($User in $Users) {
if ($WhatIf) {
Write-Host "WhatIf: Replacing license for user '$($User.UserPrincipalName)' - Removing SKU Id '$RemoveSkuId', Adding SKU Id '$AddSkuId'"
} else {
# Replace the license
Set-MgUserLicense -UserId $User.Id -RemoveLicenses @($RemoveSkuId) -AddLicenses @{}
Set-MgUserLicense -UserId $User.Id -RemoveLicenses @() -AddLicenses @{ SkuId = $AddSkuId }
}
}
}
}
Export-ModuleMember -Function Set-License

View File

@ -99,4 +99,4 @@ foreach ($User in $Users) {
}
# $Results | sort OfficeLocation,DisplayName | select Id,DisplayName,License,JobTitle,@{Label='BusinessPhone';Expression={$_.BusinessPhones[0]}},MobilePhone,FaxNumber,CompanyName,OfficeLocation | Out-GridView
$Results | Sort-Object OfficeLocation,DisplayName | Select-Object Id,DisplayName,License,JobTitle,@{Label='BusinessPhone';Expression={$_.BusinessPhones[0]}},MobilePhone,FaxNumber,CompanyName,OfficeLocation | Export-Csv -Path results.csv -NoTypeInformation
$Results | Sort-Object OfficeLocation,DisplayName | Select-Object Id,DisplayName,License,JobTitle,OfficeLocation,CompanyName,Department,@{Label='BusinessPhone';Expression={$_.BusinessPhones[0]}},MobilePhone,FaxNumber | Export-Csv -Path results.csv -NoTypeInformation

View File

@ -0,0 +1,16 @@
$Username = ((Get-CimInstance win32_computersystem | ForEach-Object username) -split '\\')[1]
Write-Host "Signed in user is '$Username'"
# Map PSDrive for HKU
New-PSDrive -Name HKU -PSProvider Registry -Root hkey_users | Out-Null
# Find matching Volatile Env
try {
$Environment = ((Get-ItemProperty 'HKU:\*\Volatile Environment' | Where-Object Username -eq $Username).PSPath -split '\\')[2]
$UserRegistryPath = Join-Path -Path 'HKU:' -ChildPath $Environment
} catch [System.Security.SecurityException] {
Write-Error "Permission Denied"
throw System.Security.SecurityException
}
Write-Host "User registry path is '$UserRegistryPath'"

View File

@ -6,8 +6,8 @@ $TenantId = ''
$CompanyId = ''
$UserSecret = ''
$AgentInstallUrl = (Invoke-RestMethod -Method "Get" -URI "https://configuration.myconnectsecure.com/api/v4/configuration/agentlink?ostype=windows&msi_required=true")
$AgentInstallFile = 'cybercnsagent.msi'
$AgentInstallUrl = (Invoke-RestMethod -Method "Get" -URI "https://configuration.myconnectsecure.com/api/v4/configuration/agentlink?ostype=windows")
$AgentInstallFile = 'cybercnsagent.exe'
$AgentInstallPath = Join-Path -Path $PackagesPath -ChildPath $AgentInstallFile
# Prep
@ -24,8 +24,9 @@ Write-Host "Downloading agent installer..."
# Install
Write-Host "Starting installation..."
Start-Process msiexec -ArgumentList "/i `"$AgentInstallPath`" /q WRAPPED_ARGUMENTS=`"-c $CompanyId -e $TenantId -j $UserSecret -i`" /lv `"$($AgentInstallPath).log`"" -Wait
Write-Host "Log saved at $($AgentInstallPath).log"
Start-Process -FilePath $AgentInstallPath -ArgumentList "-c $($CompanyId) -e $TenantId -j $UserSecret -i" -Wait
#Start-Process msiexec -ArgumentList "/i `"$AgentInstallPath`" /q WRAPPED_ARGUMENTS=`"-c $CompanyId -e $TenantId -j $UserSecret -i`" /lv `"$($AgentInstallPath).log`"" -Wait
#Write-Host "Log saved at $($AgentInstallPath).log"
# Clean up

View File

@ -3,9 +3,9 @@
$PackagesPath = 'C:\Packages'
$DestinationPath = 'C:\Program Files\nssm'
$InstallerUrl = 'https://nssm.cc/ci/nssm-2.24-103-gdee49fc.zip'
$InstallerUrl = 'https://nssm.cc/release/nssm-2.24.zip'
$ArchivePath = Join-Path -Path $PackagesPath -ChildPath ($InstallerUrl | Select-String -Pattern "(nssm-(?:\d\.?)+-\d.*\.zip)").Matches.Value
$ArchivePath = Join-Path -Path $PackagesPath -ChildPath ($InstallerUrl | Select-String -Pattern "(nssm-(?:\d\.?)+\.zip)").Matches.Value
# Exit if the application is already installed
if (Test-Path $DestinationPath) { Write-Host 'NSSM is already installed.'; exit }