Public/cloud-acl.ps1

function Get-CloudACL {
    <#
    .SYNOPSIS
        Gets acls from the Cloud Server.
     
    .DESCRIPTION
        Retrieves a list of acls. Automatically handles token refresh.
     
    .PARAMETER ID
        Optional. Filter by acl ID
     
    .EXAMPLE
        # Get all ACLs
        Get-CloudACL
         
    .EXAMPLE
        # Get an ACL with a specific ID
        Get-CloudACL -ID 5
    #>

    
    [CmdletBinding()]
    param(
       
        [Parameter(Mandatory = $false)]
        [int]$ID
    )
    

    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/acl"
    
    $response = Invoke-CloudApiRequest -Uri $uri -Method Get
    
    $acls = $response.acls
    
    if ($PSBoundParameters.ContainsKey('ID')) {
        $acls = $acls | Where-Object {($_.id -eq $ID) -and ($null -ne $_.id)}
         return $acls
    }
     else {
        return $acls
     }
}

function New-CloudACL {
    <#
    .SYNOPSIS
        Creates a new ACL rule in the Cloud Server.
     
    .DESCRIPTION
        Creates an ACL rule with automatic encoding. Automatically handles token refresh.
     
    .PARAMETER UserID
        Individual user ID
         
    .PARAMETER GroupID
        Group ID (for user selector)
         
    .PARAMETER AllUsers
        Apply to all users
         
    .PARAMETER ResourceType
        Resource type(s)
         
    .PARAMETER ResourceID
        Optional specific resource ID
         
    .PARAMETER ResourceGroupID
        Optional group ID for group-owned resources
         
    .PARAMETER Rights
        Rights to grant (USE, MANAGE, ADMIN, CREATE)
         
    .PARAMETER ZoneID
        Zone ID (default: all zones)
         
    .PARAMETER AllZones
        Apply to all zones
     
    .EXAMPLE
        # Give user #4 read-only access to all HOSTs in all zones
        New-CloudACL -UserID 4 -ResourceType "HOST" -Rights "USE" -AllZones
         
    .EXAMPLE
        # Give group @1 manage access to all VMs, Images, and Templates in zone 0
        New-CloudACL -GroupID 1 -ResourceType "VM","IMAGE","TEMPLATE" -Rights "USE","MANAGE" -ZoneID 0
         
    .EXAMPLE
        # Give user #4 admin access to specific VM #10
        New-CloudACL -UserID 4 -ResourceType "VM" -ResourceID 10 -Rights "USE","MANAGE","ADMIN" -AllZones
    #>

    
    [CmdletBinding(DefaultParameterSetName='UserAll')]
    param(
        [Parameter(Mandatory=$true, ParameterSetName='UserAll')]
        [Parameter(Mandatory=$true, ParameterSetName='UserZone')]
        [int]$UserID,
        
        [Parameter(Mandatory=$true, ParameterSetName='GroupAll')]
        [Parameter(Mandatory=$true, ParameterSetName='GroupZone')]
        [int]$GroupID,
        
        [Parameter(Mandatory=$true, ParameterSetName='AllUsersAll')]
        [Parameter(Mandatory=$true, ParameterSetName='AllUsersZone')]
        [switch]$AllUsers,
        
        [Parameter(Mandatory=$true)]
        [ValidateSet(
            'VM', 'HOST', 'NET', 'IMAGE', 'USER', 'TEMPLATE', 'GROUP', 'DATASTORE', 
            'CLUSTER', 'DOCUMENT', 'ZONE', 'SECGROUP', 'VDC', 'VROUTER', 'MARKETPLACE', 
            'MARKETPLACEAPP', 'VMGROUP', 'VNTEMPLATE', 'BACKUPJOB'
        )]
        [string[]]$ResourceType,
        
        [Parameter(Mandatory=$false)]
        [int]$ResourceID = -1,
        
        [Parameter(Mandatory=$false)]
        [int]$ResourceGroupID = -1,
        
        [Parameter(Mandatory=$true)]
        [ValidateSet('USE', 'MANAGE', 'ADMIN', 'CREATE')]
        [string[]]$Rights,
        
        [Parameter(Mandatory=$true, ParameterSetName='UserZone')]
        [Parameter(Mandatory=$true, ParameterSetName='GroupZone')]
        [Parameter(Mandatory=$true, ParameterSetName='AllUsersZone')]
        [int]$ZoneID,
        
        [Parameter(Mandatory=$true, ParameterSetName='UserAll')]
        [Parameter(Mandatory=$true, ParameterSetName='GroupAll')]
        [Parameter(Mandatory=$true, ParameterSetName='AllUsersAll')]
        [switch]$AllZones
    )
    
    # Encode user/group
    if ($PSBoundParameters.ContainsKey('UserID')) {
        $userEncoded = ConvertTo-ACLUserEncoding -UserID $UserID
    }
    elseif ($PSBoundParameters.ContainsKey('GroupID')) {
        $userEncoded = ConvertTo-ACLUserEncoding -GroupID $GroupID
    }
    else {
        $userEncoded = ConvertTo-ACLUserEncoding -All
    }
    
    # Encode resource
    $resourceParams = @{
        ResourceType = $ResourceType
    }
    if ($ResourceID -ge 0) {
        $resourceParams['ResourceID'] = $ResourceID
    }
    if ($ResourceGroupID -ge 0) {
        $resourceParams['GroupID'] = $ResourceGroupID
    }
    $resourceEncoded = ConvertTo-ACLResourceEncoding @resourceParams
    
    # Encode rights
    $rightsEncoded = ConvertTo-ACLRightsEncoding -Rights $Rights
    
    # Encode zone
    if ($AllZones) {
        $zoneEncoded = ConvertTo-ACLZoneEncoding -All
    }
    else {
        $zoneEncoded = ConvertTo-ACLZoneEncoding -ZoneID $ZoneID
    }
    
    # Build the ACL data
    $aclData = @{
        user     = [long]$userEncoded
        resource = [long]$resourceEncoded
        rights   = $rightsEncoded
        zone     = [long]$zoneEncoded
    }
    
    $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/acl"
    
    Write-Verbose "Creating ACL rule:"
    Write-Verbose " User: $userEncoded"
    Write-Verbose " Resource: $resourceEncoded"
    Write-Verbose " Rights: $rightsEncoded"
    Write-Verbose " Zone: $zoneEncoded"
    
    try {
        $response = Invoke-CloudApiRequest -Uri $uri -Method Post -Body $aclData
        Write-Host "ACL rule created successfully." -ForegroundColor Green
        return $response
    }
    catch {
        # Check if the error is due to rule already existing
        if ($_.Exception.Message -match "already exists") {
            Write-Host "Note: ACL rule already exists." -ForegroundColor Yellow
            # Return a success-like object so scripts can continue
            return [PSCustomObject]@{
                Status = "AlreadyExists"
                Message = "ACL rule already exists"
            }
        }
        else {
            # Re-throw other errors
            throw
        }
    }
}

function Remove-CloudACL {
    <#
    .SYNOPSIS
        Removes an ACL rule from the Cloud Server.
     
    .DESCRIPTION
        Deletes an ACL rule by ID. Automatically handles token refresh.
     
    .PARAMETER ID
        ACL rule ID to remove
     
    .EXAMPLE
        # Remove an ACL with a specific ID
        Remove-CloudACL -ID 5
         
    .EXAMPLE
        # Remove an ACL with a specific ID and bypass the confirmation prompt
        Remove-CloudACL -ID 5 -Confirm:$false
         
    .EXAMPLE
        # Remove multiple ACLs
        19,20,21 | ForEach-Object { Remove-CloudACL -ID $_ }
         
    .EXAMPLE
        # Remove ACLs matching a pattern
        Get-CloudACL | Where-Object { $_.string -like "*@102*" } | ForEach-Object { Remove-CloudACL -ID $_.id }
    #>

    
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact='High')]
    param(
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [int]$ID
    )
    
    process {
        # Get the ACL details for confirmation message
        $acl = Get-CloudACL -ID $ID
        
        if (-not $acl) {
            Write-Warning "ACL with ID $ID not found."
            return
        }
        
        $aclDescription = $acl.string
        
        # Confirm before deletion
        if ($PSCmdlet.ShouldProcess("ACL ID $ID ($aclDescription)", "Remove ACL rule")) {
            
            $uri = "$($script:CloudConnection.BaseUri)/manifold-api/v2/cloud/acl/$ID"
            
            Write-Verbose "Removing ACL rule ID ${ID}: $aclDescription"
            
            try {
                $response = Invoke-CloudApiRequest -Uri $uri -Method Delete
                Write-Host "ACL rule $ID removed successfully." -ForegroundColor Green
                return $response
            }
            catch {
                # Check for specific error messages
                if ($_.Exception.Message -match "not found|does not exist") {
                    Write-Warning "ACL rule $ID not found or already deleted."
                    return
                }
                else {
                    # Re-throw other errors
                    throw
                }
            }
        }
    }
}