ChocoCCM.psm1
function Add-CCMGroup { <# .SYNOPSIS Adds a group to Central Management .DESCRIPTION Deployments in Central Management revolve around Groups. Before you can execute a deployment you must define a target group of computers the Deployment will execute on. Use this function to create new groups in your Central Management system .PARAMETER Name The name you wish to give the group .PARAMETER Description A short description of the group .PARAMETER Group The group(s) to include as members .PARAMETER Computer The computer(s) to include as members .EXAMPLE Add-CCMGroup -Name PowerShell -Description "I created this via the ChocoCCM module" -Computer pc1,pc2 .EXAMPLE Add-CCMGroup -Name PowerShell -Description "I created this via the ChocoCCM module" -Group Webservers #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/addccmgroup")] param( [Parameter(Mandatory)] [string] $Name, [Parameter()] [string] $Description, [Parameter()] [string[]] $Group, [Parameter()] [string[]] $Computer ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $computers = Get-CCMComputer $groups = Get-CCMGroup $ComputerCollection = foreach ($item in $Computer) { if ($item -in $current.computers.computerName) { Write-Warning "Skipping $item, already exists" } else { $Cresult = $computers | Where-Object Name -EQ $item | Select-Object -ExpandProperty Id # Drop object into $computerCollection [pscustomobject]@{ computerId = $Cresult } } } $GroupCollection = foreach ($item in $Group) { if ($item -in $current.groups.subGroupName) { Write-Warning "Skipping $item, already exists" } else { $Gresult = $groups | Where-Object Name -EQ $item | Select-Object -ExpandProperty Id # Drop object into $computerCollection [pscustomobject]@{subGroupId = $Gresult } } } $processedComputers = $ComputerCollection $processedGroups = $GroupCollection } process { $body = @{ Name = $Name Description = $Description Groups = if (-not $processedGroups) { @() } else { @(, $processedGroups) } Computers = if (-not $processedComputers) { @() } else { @(, $processedComputers) } } | ConvertTo-Json $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Groups/CreateOrEdit" Method = "POST" ContentType = "application/json" Body = $body WebSession = $Session } Write-Verbose $body try { $response = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } [pscustomobject]@{ name = $Name description = $Description groups = $Group computers = $Computer } } } function Add-CCMGroupMember { <# .SYNOPSIS Adds a member to an existing Group in Central Management .DESCRIPTION Add new computers and groups to existing Central Management Groups .PARAMETER Name The group to edit .PARAMETER Computer The computer(s) to add .PARAMETER Group The group(s) to add .EXAMPLE Add-CCMGroupMember -Group 'Newly Imaged' -Computer Lab1,Lab2,Lab3 #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/addccmgroupmember")] param( [Parameter(Mandatory)] [Parameter(ParameterSetName = "Computer")] [Parameter(ParameterSetName = "Group")] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMGroup).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Name, [Parameter(Mandatory, ParameterSetName = "Computer")] [Parameter(ParameterSetName = 'Group')] [string[]] $Computer, [Parameter(Mandatory, ParameterSetName = "Group")] [string[]] $Group ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $computers = Get-CCMComputer $groups = Get-CCMGroup $ComputerCollection = [System.Collections.Generic.List[psobject]]::new() $GroupCollection = [System.Collections.Generic.List[psobject]]::new() $id = Get-CCMGroup -Group $name | Select-Object -ExpandProperty Id $current = Get-CCMGroup -Id $id | Select-Object * $current.computers | ForEach-Object { $ComputerCollection.Add([pscustomobject]@{computerId = "$($_.computerId)" }) } } process { switch ($PSCmdlet.ParameterSetName) { { $Computer } { foreach ($c in $Computer) { if ($c -in $current.computers.computerName) { Write-Warning "Skipping $c, already exists" } else { $Cresult = $computers | Where-Object { $_.Name -eq "$c" } | Select-Object Id $ComputerCollection.Add([pscustomobject]@{ computerId = "$($Cresult.Id)" }) } } $processedComputers = $ComputerCollection } 'Group' { foreach ($g in $Group) { if ($g -in $current.groups.subGroupName) { Write-Warning "Skipping $g, already exists" } else { $Gresult = $groups | Where-Object { $_.Name -eq $g } | Select-Object Id $GroupCollection.Add([pscustomobject]@{ subGroupId = "$($Gresult.Id)" }) } } $processedGroups = $GroupCollection } } $body = @{ Name = $Name Id = ($groups | Where-Object { $_.name -eq "$Name" } | Select-Object -ExpandProperty Id) Description = ($groups | Where-Object { $_.name -eq "$Name" } | Select-Object -ExpandProperty Description) Groups = if (-not $processedGroups) { @() } else { @(, $processedGroups) } Computers = if (-not $processedComputers) { @() } else { @(, $processedComputers) } } | ConvertTo-Json -Depth 3 Write-Verbose $body $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Groups/CreateOrEdit" Method = "POST" ContentType = "application/json" Body = $body WebSession = $Session } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop $successGroup = Get-CCMGroupMember -Group $Name [pscustomobject]@{ Name = $Name Description = $successGroup.Description Groups = $successGroup.Groups.subGroupName Computers = $successGroup.Computers.computerName } } catch { throw $_.Exception.Message } } } function Connect-CCMServer { <# .SYNOPSIS Creates a session to a central management instance .DESCRIPTION Creates a web session cookie used for other functions in the ChocoCCM module .PARAMETER Hostname The hostname and port number of your Central Management installation .PARAMETER Credential The credentials for your Central Management installation. You'll be prompted if left blank .EXAMPLE Connect-CCMServer -Hostname localhost:8090 .EXAMPLE $cred = Get-Credential ; Connect-CCMServer -Hostname localhost:8090 -Credential $cred #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/connectccmserver")] param( [Parameter(Mandatory, Position = 0)] [String] $Hostname, [Parameter(Mandatory)] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [switch] $UseSSL ) begin { $script:Hostname = $Hostname $protocol = 'http' } process { if ($UseSSL) { $protocol = 'https' } $body = @{ usernameOrEmailAddress = "$($Credential.UserName)" password = "$($Credential.GetNetworkCredential().Password)" } $Result = Invoke-WebRequest -Uri "$($protocol)://$Hostname/Account/Login" -Method POST -ContentType 'application/x-www-form-urlencoded' -Body $body -SessionVariable Session -ErrorAction Stop $script:Session = $Session $script:Protocol = $protocol } } function Disable-CCMDeployment { <# .SYNOPSIS Archive a CCM Deployment. This will move a Deployment to the Archived Deployments section in the Central Management Web UI. .DESCRIPTION Moves a deployment in Central Management to the archive. This Deployment will no longer be available for use. .PARAMETER Deployment The deployment to archive .EXAMPLE Disable-CCMDeployment -Deployment 'Upgrade VLC' .EXAMPLE Archive-CCMDeployment -Deployment 'Upgrade VLC' #> [Alias('Archive-CCMDeployment')] [CmdletBinding(ConfirmImpact = "High", SupportsShouldProcess, HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/disableccmdeployment")] param( [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Deployment ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $deployId = Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id } process { if ($PSCmdlet.ShouldProcess("$Deployment", "ARCHIVE")) { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentPlans/Archive" Method = "POST" ContentType = "application/json" Body = @{ id = "$deployId" } | ConvertTo-Json Websession = $Session } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } } function Export-CCMDeployment { <# .SYNOPSIS Exports a Deployment to a CliXML file .DESCRIPTION Adds ability to export a deployment as cli-xml. Useful for backup/source control of deployments .PARAMETER Deployment The CCM Deployment to Export .PARAMETER DeploymentStepsOnly Only export a deployment's steps .PARAMETER OutFile The xml file to save the deployment as .PARAMETER AllowClobber Allow a file to be overwritten if it already exists .EXAMPLE Export-CCMDeployment -Deployment TestDeployment -OutFile C:\temp\testdeployment.xml .EXAMPLE Export-CCMDeployment -Deployment UpgradeChrome -OutFile C:\temp\upgradechrome_ccmdeployment.xml -AllowClobber #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/exportccmdeployment")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Deployment, [Parameter()] [Switch] $DeploymentStepsOnly, [Parameter(Mandatory)] [String] $OutFile, [Parameter()] [Switch] $AllowClobber ) process { $exportParams = if ($AllowClobber) { @{ Force = $true } } else { @{} } $DeploymentObject = Get-CCMDeployment -Name $Deployment if ($DeploymentStepsOnly) { $DeploymentObject.deploymentSteps | Export-Clixml -Depth 10 -Path $OutFile -Force @exportParams } else { $DeploymentObject | Export-Clixml -Depth 10 -Path $OutFile @exportParams } } } function Export-CCMDeploymentReport { <# .SYNOPSIS Downloads a deployment report from Central Management. This will be saved in the path you specify for OutputFolder .DESCRIPTION Downloads a deployment report from Central Management in PDF or Excel format. The file is saved to the OutputFolder .PARAMETER Deployment The deployment from which to generate and download a report .PARAMETER Type The type of report, either PDF or Excel .PARAMETER OutputFolder The path to save the report too .EXAMPLE Export-CCMDeploymentReport -Deployment 'Complex' -Type PDF -OutputFolder C:\temp\ .EXAMPLE Export-CCMDeploymentReport -Deployment 'Complex -Type Excel -OutputFolder C:\CCMReports #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/exportccmdeploymentreport")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Deployment, [Parameter(Mandatory)] [ValidateSet('PDF', 'Excel')] [string] $Type, [Parameter(Mandatory)] [ValidateScript( { Test-Path $_ })] [string] $OutputFolder ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $deployId = Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id } process { $irmParams = @{ Method = "GET" ContentType = "application/json" WebSession = $Session } switch ($Type) { 'PDF' { $url = "$($protocol)://$hostname/api/services/app/DeploymentPlans/GetDeploymentPlanDetailsToPdf?deploymentPlanId=$deployId" } 'Excel' { $url = "$($protocol)://$hostname/api/services/app/DeploymentPlans/GetDeploymentPlanDetailsToExcel?deploymentPlanId=$deployId" } } $irmParams.Add('Uri', "$url") try { $record = Invoke-RestMethod @irmParams -ErrorAction Stop $fileName = $record.result.fileName $fileType = $record.result.fileType $fileToken = $record.result.fileToken } catch { throw $_.Exception } $downloadParams = @{ Uri = "$($protocol)://$hostname/File/DownloadTempFile?fileType=$fileType&fileToken=$fileToken&fileName=$fileName" OutFile = "$OutputFolder\$fileName" WebSession = $Session Method = "GET" ContentType = $fileType } try { $dl = Invoke-RestMethod @downloadParams -ErrorAction Stop } catch { $_.ErrorDetails } } } function Export-CCMOutdatedSoftwareReport { <# .SYNOPSIS Download an outdated Software report from Central Management. This file will be saved to the OutputFolder specified .DESCRIPTION Download either a PDF or Excel format report of outdated software from Central Management to the OutputFolder specified .PARAMETER Report The report to download .PARAMETER Type Specify either PDF or Excel .PARAMETER OutputFolder The path to save the file .EXAMPLE Export-CCMOutdatedSoftwareReport -Report '7/4/2020 6:44:40 PM' -Type PDF -OutputFolder C:\CCMReports #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/exportccmoutdatedsoftwarereport")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMOutdatedSoftwareReport).creationTime of ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Report, [Parameter(Mandatory)] [ValidateSet('PDF', 'Excel')] [string] $Type, [Parameter(Mandatory)] [ValidateScript( { Test-Path $_ })] [string] $OutputFolder ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $reportId = Get-CCMOutdatedSoftwareReport | Where-Object { $_.creationTime -eq "$Report" } | Select-Object -ExpandProperty id $irmParams = @{ Method = "GET" ContentType = "application/json" WebSession = $Session } switch ($Type) { 'PDF' { $url = "$($protocol)://$hostname/api/services/app/OutdatedReports/GetOutdatedSoftwareToPdf?reportId=$reportId" } 'Excel' { $url = "$($protocol)://$hostname/api/services/app/OutdatedReports/GetOutdatedSoftwareToExcel?reportId=$reportId" } } $irmParams.Add('Uri', "$url") try { $record = Invoke-RestMethod @irmParams -ErrorAction Stop $fileName = $record.result.fileName $fileType = $record.result.fileType $fileToken = $record.result.fileToken } catch { throw $_.Exception } $downloadParams = @{ Uri = "$($protocol)://$hostname/File/DownloadTempFile?fileType=$fileType&fileToken=$fileToken&fileName=$fileName" OutFile = "$($OutputFolder)\$($fileName)" WebSession = $Session Method = "GET" ContentType = $fileType } try { $dl = Invoke-RestMethod @downloadParams -ErrorAction Stop } catch { $_.ErrorDetails } } } function Get-CCMComputer { <# .SYNOPSIS Returns information about computers in CCM .DESCRIPTION Query for all, or by computer name/id to retrieve information about the system as reported in Central Management .PARAMETER Computer Returns the specified computer(s) .PARAMETER Id Returns the information for the computer with the specified id .EXAMPLE Get-CCMComputer .EXAMPLE Get-CCMComputer -Computer web1 .EXAMPLE Get-CCMComputer -Id 13 .NOTES #> [CmdletBinding(DefaultParameterSetName = "All", HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmcomputer")] param( [Parameter(Mandatory, ParameterSetName = "Computer")] [string[]] $Computer, [Parameter(Mandatory, ParameterSetName = "Id")] [int] $Id ) begin { if (-not $Session) { throw "Unauthenticated! Please run Connect-CCMServer first" } } process { if (-not $Id) { $records = Invoke-RestMethod -Uri "$($protocol)://$Hostname/api/services/app/Computers/GetAll" -WebSession $Session } switch ($PSCmdlet.ParameterSetName) { "Computer" { foreach ($c in $computer) { [pscustomobject]$records.result | Where-Object { $_.name -match "$c" } } } "Id" { $records = Invoke-RestMethod -Uri "$($protocol)://$Hostname/api/services/app/Computers/GetComputerForEdit?Id=$Id" -WebSession $Session $records } default { $records.result } } } } function Get-CCMDeployment { <# .SYNOPSIS Return information about a CCM Deployment. .DESCRIPTION Returns detailed information about Central Management Deployment Plans. .PARAMETER Name Returns the named Deployment Plan. .PARAMETER Id Returns the Deployment Plan with the given Id. .PARAMETER IncludeStepResults If set, additionally retrieves the results for each step of the deployment. .EXAMPLE Get-CCMDeployment .EXAMPLE Get-CCMDeployment -Name Bob .EXAMPLE Get-CCMDeployment -Id 583 -IncludeStepResults #> [CmdletBinding(DefaultParameterSetname = "default", HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmdeployment")] param( [Parameter(ParameterSetName = "Name", Mandatory)] [string] $Name, [Parameter(ParameterSetName = "Id", Mandatory)] [string] $Id, [Parameter(ParameterSetName = "Name")] [Parameter(ParameterSetName = "Id")] [switch] $IncludeStepResults ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { if (-not $Id) { $records = Invoke-RestMethod -Uri "$($protocol)://$Hostname/api/services/app/DeploymentPlans/GetAll" -WebSession $Session } switch ($PSCmdlet.ParameterSetName) { 'Name' { $queryId = $records.result | Where-Object { $_.Name -eq "$Name" } | Select-Object -ExpandProperty Id $records = Invoke-RestMethod -Uri "$($protocol)://$Hostname/api/services/app/DeploymentPlans/GetDeploymentPlanForEdit?Id=$queryId" -WebSession $Session if ($IncludeStepResults) { $result = $records.result.deploymentPlan $result.deploymentSteps = $result.deploymentSteps | Get-CCMDeploymentStep -IncludeResults $result } else { $records.result.deploymentPlan } } 'Id' { $records = Invoke-RestMethod -Uri "$($protocol)://$Hostname/api/services/app/DeploymentPlans/GetDeploymentPlanForEdit?Id=$id" -WebSession $Session if ($IncludeStepResults) { $result = $records.result.deploymentPlan $result.deploymentSteps = $result.deploymentSteps | Get-CCMDeploymentStep -IncludeResults $result } else { $records.result.deploymentPlan } } default { $records.result } } } } function Get-CCMDeploymentStep { <# .SYNOPSIS Return information about a CCM Deployment step. .DESCRIPTION Returns detailed information about Central Management Deployment Steps. .PARAMETER InputObject Retrieves additional details for the given step. .PARAMETER Id Returns the Deployment Step with the given Id. .PARAMETER IncludeResults If set, additionally retrieves the results for the targeted step. .EXAMPLE Get-CCMDeploymentStep -Id 583 -IncludeResults .EXAMPLE Get-CCMDeploymentStep -InputObject $step -IncludeResults #> [CmdletBinding(DefaultParameterSetName = 'IdOnly', HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmdeploymentstep")] param( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'StepObject')] [Alias('Step')] [psobject] $InputObject, [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = 'IdOnly')] [Alias('DeploymentStepId')] [long] $Id, [Parameter()] [switch] $IncludeResults ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { if (-not $Id) { $Id = $InputObject.Id } $params = @{ Uri = "${script:Protocol}://${script:Hostname}/api/services/app/DeploymentSteps/GetDeploymentStepForEdit" WebSession = $script:Session Body = @{ Id = $Id } } $result = Invoke-RestMethod @params if ($IncludeResults) { $result | Select-Object -ExcludeProperty 'deploymentStepComputers' -Property @( '*' @{ Name = 'deploymentStepComputers' Expression = { $params = @{ Uri = "${script:Protocol}://${script:Hostname}/api/services/app/DeploymentStepComputers/GetAllByDeploymentStepId" WebSession = $script:Session Body = @{ deploymentStepId = $_.id } } (Invoke-RestMethod @params).result } } ) } else { $result } } } function Get-CCMGroup { <# .SYNOPSIS Returns group information for your CCM installation .DESCRIPTION Returns information about the groups created in your CCM Installation .PARAMETER Group Returns group with the provided name .PARAMETER Id Returns group withe the provided id .EXAMPLE Get-CCMGroup .EXAMPLE Get-CCMGroup -Id 1 .EXAMPLE Get-CCMGroup -Group 'Web Servers' #> [CmdletBinding(DefaultParameterSetName = "default", HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmgroup")] param( [Parameter(Mandatory, ParameterSetName = "Group")] [string[]] $Group, [Parameter(Mandatory, ParameterSetName = "Id")] [String[]] $Id ) begin { if (-not $Session) { throw "Unauthenticated! Please run Connect-CCMServer first" } } process { if (-not $Id) { $records = Invoke-RestMethod -Method Get -Uri "$($protocol)://$hostname/api/services/app/Groups/GetAll" -WebSession $Session #$records = Invoke-We -Uri http://$Hostname/api/services/app/Groups/GetAll -WebSession $Session -UseBasicParsing } switch ($PSCmdlet.ParameterSetName) { "Group" { $records.result | Where-Object { $_.name -in $Group } } "Id" { $records = Invoke-RestMethod -Uri "$($protocol)://$Hostname/api/services/app/Groups/GetGroupForEdit?Id=$Id" -WebSession $Session $records.result } default { $records.result } } } } function Get-CCMGroupMember { <# .SYNOPSIS Returns information about a CCM group's members .DESCRIPTION Return detailed group information from Chocolatey Central Management .PARAMETER Group The Group to query .EXAMPLE Get-CCMGroupMember -Group "WebServers" #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmgroupmember")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMGroup -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Group ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $Id = (Get-CCMGroup -Group $Group).Id $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Groups/GetGroupForEdit?id=$Id" Method = "GET" ContentType = "application/json" WebSession = $Session } try { $record = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } $cCollection = [System.Collections.Generic.List[psobject]]::new() $gCollection = [System.Collections.Generic.List[psobject]]::new() $record.result.computers | ForEach-Object { $cCollection.Add($_) } $record.result.groups | ForEach-Object { $gCollection.Add($_) } [pscustomobject]@{ Name = $record.result.Name Description = $record.result.Description Groups = @($gCollection) Computers = @($cCollection) CanDeploy = $record.result.isEligibleForDeployments } } } function Get-CCMOutdatedSoftware { <# .SYNOPSIS Returns all outdated software reported in CCM .DESCRIPTION Returns all outdated software reported in CCM .EXAMPLE Get-CCMOutdatedSoftware #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmoutdatedsoftware")] param() process { $r = Get-CCMSoftware | Where-Object { $_.isOutdated -eq $true } $r } } function Get-CCMOutdatedSoftwareMember { <# .SYNOPSIS Returns computers with the requested outdated software. To see outdated software information use Get-CCMOutdatedSoftware .DESCRIPTION Returns the computers with the requested outdated software. To see outdated software information use Get-CCMOutdatedSoftware .PARAMETER Software The software to query. Software here refers to what would show up in Programs and Features on a machine. Example: If you have VLC installed, this shows as 'VLC Media Player' in Programs and Features. .PARAMETER Package This is the Chocolatey package name to search for. .EXAMPLE Get-CCMOutdatedSoftwareMember -Software 'VLC Media Player' .EXAMPLE Get-CCMOutdatedSoftwareMember -Package vlc #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmoutdatedsoftwaremember")] param( [Parameter()] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMSoftware -All | Where-Object { $_.isOutdated -eq $true }).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Software, [Parameter()] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMSoftware -All | Where-Object { $_.isOutdated -eq $true }).packageId if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Package ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { if ($Software) { $id = Get-CCMSoftware -Software $Software | Select-Object -ExpandProperty softwareId } if ($Package) { $id = Get-CCMSoftware -Package $Package | Select-Object -ExpandProperty softwareId } $id | ForEach-Object { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/ComputerSoftware/GetAllPagedBySoftwareId?filter=&softwareId=$($_)&skipCount=0&maxResultCount=100" Method = "GET" ContentType = "application/json" WebSession = $Session } try { $record = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { $_.Exception.Message } $record.result.items | ForEach-Object { [pscustomobject]@{ softwareId = $_.softwareId software = $_.software.name packageName = $_.software.packageId packageVersion = $_.software.packageVersion name = $_.computer.name friendlyName = $_.computer.friendlyName ipaddress = $_.computer.ipaddress fqdn = $_.computer.fqdn computerid = $_.computer.id } } } } } function Get-CCMOutdatedSoftwareReport { <# .SYNOPSIS List all Outdated Software Reports generated in Central Management .DESCRIPTION List all Outdated Software Reports generated in Central Management .EXAMPLE Get-CCMOutdatedSoftwareReport #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmoutdatedsoftwarereport")] param() begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Reports/GetAllPaged?reportTypeFilter=1" Method = "GET" ContentType = "application/json" WebSession = $Session } try { $response = Invoke-RestMethod @irmParams } catch { throw $_.Exception.Message } $response.result.items | ForEach-Object { [pscustomobject]@{ reportType = $_.report.reportType -as [String] creationTime = $_.report.creationTime -as [String] id = $_.report.id -as [string] } } } } function Get-CCMOutdatedSoftwareReportDetail { <# .SYNOPSIS View detailed information about an Outdated Software Report .DESCRIPTION Return report details from an Outdated Software Report in Central Management .PARAMETER Report The report to query .EXAMPLE Get-CCMOutdatedSoftwareReportDetail -Report '7/4/2020 6:44:40 PM' #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmoutdatedsoftwarereportdetail")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMOutdatedSoftwareReport).creationTime if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Report ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $reportId = Get-CCMOutdatedSoftwareReport | Where-Object { $_.creationTime -eq "$Report" } | Select-Object -ExpandProperty id $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/OutdatedReports/GetAllByReportId?reportId=$reportId&sorting=outdatedReport.packageDisplayText%20asc&skipCount=0&maxResultCount=200" Method = "GET" ContentType = "application/json" WebSession = $Session } try { $response = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } $response.result.items.outdatedReport } } function Get-CCMRole { <# .SYNOPSIS Get roles available in Chococlatey Central Management .DESCRIPTION Return information about roles available in Chocolatey Central Management .PARAMETER Name The name of a role to query .EXAMPLE Get-CCMRole .EXAMPLE Get-CCMRole -Name CCMAdmin #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmrole")] param( [Parameter(ParameterSetName = "Name")] [string] $Name ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Role/GetRoles?permission=" Method = "GET" ContentType = "application/json" WebSession = $Session } try { $response = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } switch ($PSCmdlet.ParameterSetName) { 'Name' { $response.result.items | Where-Object { $_.name -eq $Name } } default { $response.result.items } } } } function Get-CCMSoftware { <# .SYNOPSIS Returns information about software tracked inside of CCM .DESCRIPTION Return information about each piece of software managed across all of your estate inside Central Management .PARAMETER Software Return information about a specific piece of software by friendly name .PARAMETER Package Return information about a specific package .PARAMETER Id Return information about a specific piece of software by id .EXAMPLE Get-CCMSoftware .EXAMPLE Get-CCMSoftware -Software 'VLC Media Player' .EXAMPLE Get-CCMSoftware -Package vlc .EXAMPLE Get-CCMSoftware -Id 37 .NOTES #> [CmdletBinding(DefaultParameterSetname = "All", HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getccmsoftware")] param( [Parameter(Mandatory, ParameterSetName = "Software")] [string] $Software, [Parameter(Mandatory, ParameterSetName = "Package")] [string] $Package, [Parameter(Mandatory, ParameterSetName = "Id")] [int] $Id ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { if (-not $Id) { $records = Invoke-RestMethod -Uri "$($protocol)://$Hostname/api/services/app/Software/GetAll" -WebSession $Session -UseBasicParsing } switch ($PSCmdlet.ParameterSetName) { "Software" { $softwareId = $records.result.items | Where-Object { $_.name -eq "$Software" } | Select-Object -ExpandProperty Id $softwareId | ForEach-Object { $irmParams = @{ WebSession = $Session Uri = "$($protocol)://$Hostname/api/services/app/ComputerSoftware/GetAllPagedBySoftwareId?filter=&softwareId=$($_)&skipCount=0&maxResultCount=500" } $records = Invoke-RestMethod @irmParams $records.result } } "Package" { $packageId = $records.result.items | Where-Object { $_.packageId -eq "$Package" } | Select-Object -ExpandProperty id $packageId | ForEach-Object { $irmParams = @{ WebSession = $Session Uri = "$($protocol)://$Hostname/api/services/app/ComputerSoftware/GetAllPagedBySoftwareId?filter=&softwareId=$($_)&skipCount=0&maxResultCount=500" } $records = Invoke-RestMethod @irmParams $records.result } } "Id" { $irmParams = @{ WebSession = $Session Uri = "$($protocol)://$Hostname/api/services/app/ComputerSoftware/GetAllPagedBySoftwareId?filter=&softwareId=$Id&skipCount=0&maxResultCount=500" } $records = Invoke-RestMethod @irmParams $records.result.items } default { $records.result.items } } } } function Get-DeploymentResult { <# .SYNOPSIS Return the result of a Central Management Deployment .DESCRIPTION Return the result of a Central Management Deployment .PARAMETER Deployment The Deployment for which to return information .EXAMPLE Get-CCMDeploymentResult -Name 'Google Chrome Upgrade' #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/getdeploymentresult")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Deployment ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $deployId = Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id } process { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentSteps/GetAllPagedByDeploymentPlanId?resultFilter=Success%2CFailed%2CUnreachable%2CInconclusive%2CReady%2CActive%2CCancelled%2CUnknown%2CDraft&deploymentPlanId=$deployId&sorting=planOrder%20asc&skipCount=0&maxResultCount=10" Method = "GET" ContentType = "application/json" WebSession = $Session } try { $records = Invoke-RestMethod @irmParams -ErrorAction Stop $records.result.items } catch { throw $_.Exception.Message } } } function Import-PDQDeployPackage { <# .SYNOPSIS Imports a PDQ Deploy package as a Central Management Deployment .DESCRIPTION Imports a PDQ Deploy package as a Central Management Deployment .PARAMETER PdqXml The pdq xml file to import .EXAMPLE Import-PDQDeployPackage .NOTES General notes #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/importpdqdeploypackage")] param( [Parameter(Mandatory)] [ValidateScript( { Test-Path $_ })] [String] $PdqXml ) process { [xml]$xmlData = Get-Content $PdqXml $deploymentName = $xmlData.SelectNodes("//*[Name]").Name $deploymentName = "PDQ Import: $deploymentName" Write-Verbose "Adding deployment with name: $deploymentName" New-CCMDeployment -Name $deploymentName $deploymentSteps = $xmldata.SelectNodes("//*[Steps]").Steps $deploymentSteps = $deploymentSteps | ForEach-Object { if ($_.InstallStep) { $_.InstallStep | ForEach-Object { [pscustomobject]@{ SuccessCodes = @($_.SuccessCodes) FailureAction = $_.ErrorMode Type = switch ($_.Typename) { 'Install' { "Basic" } } Package = $deploymentName Title = if ($_.title) { $_.title } else { "Install $deploymentName" } } } } } $ccmSteps = @{ Type = $deploymentSteps.Type ValidExitCodes = $deploymentSteps.SuccessCodes } if ($deploymentSteps.FailureAction -eq 'StopdeploymentFail') { $ccmSteps.Add('FailOnError', $true) } else { $ccmSteps.Add('FailOnError', $false) } if ($deploymentSteps.Type -eq 'Basic') { $ccmSteps.Add('ChocoCommand', 'upgrade') $ccmSteps.add('Package', '7-zip') } Write-Verbose "Adding steps from imported package to Deployment" New-CCMDeploymentStep -Deployment $deploymentName -Name $deploymentSteps.Title @ccmSteps } end { Write-Warning "No targets will be defined for this deployment" } } function Move-CCMDeploymentToReady { <# .SYNOPSIS Moves a deployment to Ready state .DESCRIPTION Moves a Deployment to the Ready state so it can start .PARAMETER Deployment The deployment to move .EXAMPLE Move-CCMDeploymentToReady -Deployment 'Upgrade Outdated VLC' .EXAMPLE Move-CCMDeploymenttoReady -Deployment 'Complex Deployment' #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/moveccmdeploymenttoready")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = Get-CCMDeployment -All if ($WordToComplete) { $r.name.Where{ $_ -match "^$WordToComplete" } } else { $r.name } } )] [string] $Deployment ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $id = (Get-CCMDeployment -Name $Deployment).id } process { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentPlans/MoveToReady" Method = "POST" ContentType = "application/json" Body = @{ id = "$id" } | ConvertTo-Json WebSession = $Session } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } function New-CCMDeployment { <# .SYNOPSIS Create a new CCM Deployment Plan .DESCRIPTION Creates a new CCM Deployment. This is just a shell. You'll need to add steps with New-CCMDeploymentStep. .PARAMETER Name The name for the deployment .EXAMPLE New-CCMDeployment -Name 'This is awesome' #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/newccmdeployment")] param( [Parameter(Mandatory)] [string] $Name ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentPlans/CreateOrEdit" Method = "POST" ContentType = "application/json" Body = @{ Name = "$Name" } | ConvertTo-Json WebSession = $Session } try { $record = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } [pscustomobject]@{ name = $Name id = $record.result.id } } } function New-CCMDeploymentStep { <# .SYNOPSIS Adds a Deployment Step to a Deployment Plan .DESCRIPTION Adds both Basic and Advanced steps to a Deployment Plan .PARAMETER Deployment The Deployment where the step will be added .PARAMETER Name The Name of the step .PARAMETER TargetGroup The group(s) the step will target .PARAMETER ExecutionTimeoutSeconds How long to wait for the step to timeout. Defaults to 14400 (4 hours) .PARAMETER FailOnError Fail the step if there is an error. Defaults to True .PARAMETER RequireSuccessOnAllComputers Ensure all computers are successful before moving to the next step. .PARAMETER ValidExitCodes Valid exit codes your script can emit. Default values are: '0','1605','1614','1641','3010' .PARAMETER Type Either a Basic or Advanced Step .PARAMETER ChocoCommand Select from Install,Upgrade, or Uninstall. Used with a Simple step type. .PARAMETER PackageName The chocolatey package to use with a simple step. .PARAMETER Script A scriptblock your Advanced step will use .EXAMPLE New-CCMDeploymentStep -Deployment PowerShell -Name 'From ChocoCCM' -TargetGroup WebServers -Type Basic -ChocoCommand upgrade -PackageName firefox .EXAMPLE New-CCMDeploymentStep -Deployment PowerShell -Name 'From ChocoCCM' -TargetGroup All,PowerShell -Type Advanced -Script { $process = Get-Process >> >> Foreach($p in $process){ >> Write-Host $p.PID >> } >> >> Write-Host "end" >> >> } .EXAMPLE New-CCMDeploymentStep -Deployment PowerShell -Name 'From ChocoCCM' -TargetGroup All,PowerShell -Type Advanced -Script {(Get-Content C:\script.txt)} #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/newccmdeploymentstep")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Deployment, [Parameter(Mandatory)] [string] $Name, [Parameter()] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMGroup).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string[]] $TargetGroup = @(), [Parameter()] [string] $ExecutionTimeoutSeconds = '14400', [Parameter()] [switch] $FailOnError = $true, [Parameter()] [switch] $RequireSuccessOnAllComputers = $false, [Parameter()] [string[]] $ValidExitCodes = @('0', '1605', '1614', '1641', '3010'), [Parameter(Mandatory, ParameterSetName = "StepType")] [Parameter(Mandatory, ParameterSetName = "Basic")] [Parameter(Mandatory, ParameterSetName = "Advanced")] [ValidateSet('Basic', 'Advanced')] [string] $Type, [Parameter(Mandatory, ParameterSetName = "Basic")] [ValidateSet('Install', 'Upgrade', 'Uninstall')] [string] $ChocoCommand, [Parameter(Mandatory, ParameterSetName = "Basic")] [string] $PackageName, [Parameter(Mandatory, ParameterSetName = "Advanced")] [scriptblock] $Script ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { switch ($PSCmdlet.ParameterSetName) { 'Basic' { $Body = @{ Name = "$Name" DeploymentPlanId = "$(Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id)" DeploymentStepGroups = @( Get-CCMGroup -Group $TargetGroup | Select-Object Name, Id | ForEach-Object { [pscustomobject]@{ groupId = $_.id; groupName = $_.name } } ) ExecutionTimeoutInSeconds = "$ExecutionTimeoutSeconds" RequireSuccessOnAllComputers = "$RequireSuccessOnAllComputers" failOnError = "$FailOnError" validExitCodes = "$($validExitCodes -join ',')" script = "$($ChocoCommand.ToLower())|$($PackageName)" } | ConvertTo-Json -Depth 3 $Uri = "$($protocol)://$hostname/api/services/app/DeploymentSteps/CreateOrEdit" } 'Advanced' { $Body = @{ Name = "$Name" DeploymentPlanId = "$(Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id)" DeploymentStepGroups = @( Get-CCMGroup -Group $TargetGroup | Select-Object Name, Id | ForEach-Object { [pscustomobject]@{ groupId = $_.id; groupName = $_.name } } ) ExecutionTimeoutInSeconds = "$ExecutionTimeoutSeconds" RequireSuccessOnAllComputers = "$RequireSuccessOnAllComputers" failOnError = "$FailOnError" validExitCodes = "$($validExitCodes -join ',')" script = "$($Script.ToString())" } | ConvertTo-Json -Depth 3 $Uri = "$($protocol)://$hostname/api/services/app/DeploymentSteps/CreateOrEditPrivileged" } } $irmParams = @{ Uri = "$($Uri)" Method = "POST" ContentType = "application/json" WebSession = $Session Body = $Body } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } function New-CCMOutdatedSoftwareReport { <# .SYNOPSIS Create a new Outdated Software Report in Central Management .DESCRIPTION Create a new Outdated Software Report in Central Management .EXAMPLE New-CCMOutdatedSoftwareReport .NOTES Creates a new report named with a creation date timestamp in UTC format #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/newccmoutdatedsoftwarereport")] param() begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/OutdatedReports/Create" Method = "POST" ContentType = 'application/json' WebSession = $Session } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } function Remove-CCMDeployment { <# .SYNOPSIS Removes a deployment plan .DESCRIPTION Removes the Deployment Plan selected from a Central Management installation .PARAMETER Deployment The Deployment to delete .EXAMPLE Remove-CCMDeployment -Name 'Super Complex Deployment' .EXAMPLE Remove-CCMDeployment -Name 'Deployment Alpha' -Confirm:$false #> [CmdletBinding(ConfirmImpact = "High", SupportsShouldProcess, HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/removeccmdeployment")] param( [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string[]] $Deployment ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $deployId = [System.Collections.Generic.List[string]]::new() $Deployment | ForEach-Object { $deployId.Add($(Get-CCMDeployment -Name $_ | Select-Object -ExpandProperty Id)) } } process { $deployId | ForEach-Object { if ($PSCmdlet.ShouldProcess("$Deployment", "DELETE")) { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentPlans/Delete?Id=$($_)" Method = "DELETE" ContentType = "application/json" } Invoke-RestMethod @irmParams } } } } function Remove-CCMDeploymentStep { <# .SYNOPSIS Removes a deployment plan .DESCRIPTION Removes the Deployment Plan selected from a Central Management installation .PARAMETER Deployment The Deployment to remove a step from .PARAMETER Step The Step to remove .EXAMPLE Remove-CCMDeploymentStep -Name 'Super Complex Deployment' -Step 'Kill web services' .EXAMPLE Remove-CCMDeploymentStep -Name 'Deployment Alpha' -Step 'Copy Files' -Confirm:$false #> [CmdletBinding(ConfirmImpact = "High", SupportsShouldProcess, HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/removeccmdeploymentstep")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment) if ($WordToComplete) { $r.Name.Where{ $_ -match "^$WordToComplete" } } else { $r.Name } } )] [string] $Deployment, [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $d = (Get-CCMDeployment -Name $($FakeBoundParams.Deployment)).id $idSteps = (Get-CCMDeployment -Id $d).deploymentSteps.Name if ($WordToComplete) { $idSteps.Where{ $_ -match "^$WordToComplete" } } else { $idSteps } } )] [string] $Step ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $deployId = Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id $deploymentSteps = Get-CCMDeployment -Id $deployId | Select-Object deploymentSteps $stepId = $deploymentSteps.deploymentSteps | Where-Object { $_.Name -eq "$Step" } | Select-Object -ExpandProperty id } process { if ($PSCmdlet.ShouldProcess("$Step", "DELETE")) { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentSteps/Delete?Id=$stepId" Method = "DELETE" ContentType = "application/json" WebSession = $Session } $null = Invoke-RestMethod @irmParams } } } function Remove-CCMGroup { <# .SYNOPSIS Removes a CCM group .DESCRIPTION Removes a group from Chocolatey Central Management .PARAMETER Group The group(s) to delete .EXAMPLE Remove-CCMGroup -Group WebServers .EXAMPLE Remove-CCMGroup -Group WebServer,TestAppDeployment .EXAMPLE Remove-CCMGroup -Group PilotPool -Confirm:$false #> [CmdletBinding(ConfirmImpact = "High", SupportsShouldProcess, HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/removeccmdeploymentstep")] param( [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMGroup -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string[]] $Group ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $Group | ForEach-Object { $id = Get-CCMGroup -Group $_ | Select-Object -ExpandProperty Id $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Groups/Delete?id=$id" Method = "DELETE" ContentType = "application/json" WebSession = $Session } if ($PSCmdlet.ShouldProcess($Group, "DELETE")) { Write-Verbose -Message "Removing group: $($_) with Id: $($id)" try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } } } function Remove-CCMGroupMember { <# .SYNOPSIS Remove a member from a Central Management Group .DESCRIPTION Remove a member from a Central Management Group .PARAMETER Group The group you want to remove a member from .PARAMETER GroupMember The group(s) to remove from the group .PARAMETER ComputerMember The computer(s) to remove from the group .EXAMPLE Remove-CCMGroupMember -Group TestLab -ComputerMember TestPC1 .EXAMPLE Remove-CCMGroupMember -Group TestLab -ComputerMember Test1,Test2 -GroupMember SecondLab #> [CmdletBinding(ConfirmImpact = "High", SupportsShouldProcess, HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/removeccmgroupmember")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMGroup -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Group, [Parameter()] [string[]] $GroupMember, [Parameter()] [string[]] $ComputerMember ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { $currentMembers = Get-CCMGroupMember -Group $Group $G = Get-CCMGroup -Group $Group $currentMembers | Add-Member -MemberType NoteProperty -Name Id -Value $G.Id foreach ($c in $ComputerMember) { $currentMembers.Computers = @($($currentMembers.Computers | Where-Object { $_.ComputerName -ne $c })) } foreach ($g in $GroupMember) { $currentMembers.Groups = @($($currentMembers.Groups | Where-Object { $_.subGroupName -ne $g })) } if (-not $currentMembers.Groups) { $currentMembers.Groups = @() } if (-not $currentMembers.Computers) { $currentMembers.Computers = @() } $body = $currentMembers | ConvertTo-Json -Depth 3 $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Groups/CreateOrEdit" Method = "POST" ContentType = "application/json" Body = $body WebSession = $Session } Write-Verbose $body try { $result = Invoke-RestMethod @irmParams -ErrorAction Stop [pscustomobject]@{ Status = $result.success Group = $Group AffectedComputers = $ComputerMember AffectedGroups = $GroupMember } } catch { throw $_.Exception.Message } } } function Remove-CCMStaleDeployment { <# .SYNOPSIS Removes stale CCM Deployments .DESCRIPTION Remove stale deployments from CCM based on their age and run status. .PARAMETER Age The age in days to prune .EXAMPLE Remove-StaleCCMDeployment -Age 30 #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High", HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/removeccmstaledeployment")] param( [Parameter(Mandatory)] [string] $Age ) process { <# Bad States: Unknown = 0 Pending = 2 Failed = 8 #> $badStates = @(0, 2, 8) if ($PSCmdlet.ShouldProcess("$Deployment", "DELETE")) { Get-CCMDeployment -All | Where-Object { $_.CreationDate -ge (Get-Date).AddDays(-$Age) -and $null -eq $_.StartDateTimeUtc -and $_.Result -in $badStates } | Remove-CCMDeployment } } } function Set-CCMDeploymentStep { <# .SYNOPSIS Modify a Deployment Step of a Central Management Deployment .DESCRIPTION Modify a Deployment Step of a Central Management Deployment .PARAMETER Deployment The Deployment to modify .PARAMETER Step The step to modify .PARAMETER TargetGroup Set the target group of the deployment .PARAMETER ExecutionTimeoutSeconds Modify the execution timeout of the deployment in seconds .PARAMETER FailOnError Set the FailOnError flag for the deployment step .PARAMETER RequireSuccessOnAllComputers Set the RequreSuccessOnAllComputers for the deployment step .PARAMETER ValidExitCodes Set valid exit codes for the deployment .PARAMETER ChocoCommand For a basic step, set the choco command to execute. Install, Upgrade, or Uninstall .PARAMETER PackageName For a basic step, the choco package to use in the deployment .PARAMETER Script For an advanced step, this is a script block of PowerShell code to execute in the step .EXAMPLE Set-CCMDeploymentStep -Deployment 'Google Chrome Upgrade' -Step 'Upgrade' -TargetGroup LabPCs -ExecutionTimeoutSeconds 14400 -ChocoCommand Upgrade -PackageName googlechrome .EXAMPLE $stepParams = @{ Deployment = 'OS Version' Step = 'Gather Info' TargetGroup = 'US-East servers' Script = { $data = Get-WMIObject win32_OperatingSystem [pscustomobject]@{ Name = $data.caption Version = $data.version } } } Set-CCMDeploymentStep @stepParams #> [CmdletBinding(DefaultParameterSetName = "Dumby", HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/setccmdeploymentstep")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Deployment, [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $d = (Get-CCMDeployment -Name $($FakeBoundParams.Deployment)).id $idSteps = (Get-CCMDeployment -Id $d).deploymentSteps.Name if ($WordToComplete) { $idSteps.Where{ $_ -match "^$WordToComplete" } } else { $idSteps } } )] [string] $Step, [Parameter()] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMGroup -All).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string[]] $TargetGroup, [Parameter()] [string] $ExecutionTimeoutSeconds, [Parameter()] [switch] $FailOnError, [Parameter()] [switch] $RequireSuccessOnAllComputers, [Parameter()] [string[]] $ValidExitCodes, [Parameter(Mandatory, ParameterSetName = "Basic")] [ValidateSet('Install', 'Upgrade', 'Uninstall')] [string] $ChocoCommand, [Parameter(Mandatory, ParameterSetName = "Basic")] [string] $PackageName, [Parameter(Mandatory, ParameterSetName = "Advanced")] [scriptblock] $Script ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $deployId = Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id $deploymentSteps = Get-CCMDeployment -Id $deployId | Select-Object deploymentSteps $stepId = $deploymentSteps.deploymentSteps | Where-Object { $_.Name -eq "$Step" } | Select-Object -ExpandProperty id $existingstepsParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentSteps/GetDeploymentStepForView?Id=$stepId" Method = "GET" ContentType = "application/json" WebSession = $Session } try { $existingsteps = Invoke-RestMethod @existingstepsParams -ErrorAction Stop } catch { throw $_.Exception.Message } $existingsteps = $existingsteps.result.deploymentStep $existingsteps } process { #So many if statements, so little time foreach ($param in $PSBoundParameters) { $param.Name $param.Value #$existingsteps.$($param.Key) = $param.Value } #$existingsteps } } function Set-CCMGroup { <# .SYNOPSIS Change information about a group in Chocolatey Central Management .DESCRIPTION Change the name or description of a Group in Chocolatey Central Management .PARAMETER Group The Group to edit .PARAMETER NewName The new name of the group .PARAMETER NewDescription The new description of the group .EXAMPLE Set-CCMGroup -Group Finance -Description 'Computers in the finance division' .EXAMPLE Set-CCMGroup -Group IT -NewName TheBestComputers .EXAMPLE Set-CCMGroup -Group Test -NewName NewMachineImaged -Description 'Group for freshly imaged machines needing a baseline package pushed to them' #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/setccmgroup")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMGroup).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Group, [Parameter()] [string] $NewName, [Parameter()] [string] $NewDescription ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $existing = Get-CCMGroup -Group $Group } process { if ($NewName) { $Name = $NewName } else { $Name = $existing.name } if ($NewDescription) { $Description = $NewDescription } else { $Description = $existing.description } $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Groups/CreateOrEdit" Method = "POST" ContentType = "application/json" Body = @{ Id = $($existing.id) Name = $Name Description = $Description Groups = @() Computers = @() } | ConvertTo-Json WebSession = $Session } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } function Set-CCMNotificationStatus { <# .SYNOPSIS Turn notifications on or off in CCM .DESCRIPTION Manage your notification settings in Central Management. Currently only supports On, or Off .PARAMETER Enable Enables notifications .PARAMETER Disable Disables notifications .EXAMPLE Set-CCMNotificationStatus -Enable .EXAMPLE Set-CCMNotificationStatus -Disable #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/setccmnotificationstatus")] param( [Parameter(Mandatory, ParameterSetName = "Enabled")] [switch] $Enable, [Parameter(Mandatory, ParameterSetName = "Disabled")] [switch] $Disable ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } } process { switch ($PSCmdlet.ParameterSetName) { 'Enabled' { $status = $true } 'Disabled' { $status = $false } } $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/Notification/UpdateNotificationSettings" Method = "PUT" ContentType = "application/json" WebSession = $Session Body = @{ receiveNotifications = $status notifications = @( @{ name = "App.NewUserRegistered" isSubscribed = $true } ) } | ConvertTo-Json } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } function Start-CCMDeployment { <# .SYNOPSIS Starts a deployment .DESCRIPTION Starts the specified deployment in Central Management .PARAMETER Deployment The deployment to start .EXAMPLE Start-CCMDeployment -Deployment 'Upgrade Outdated VLC' .EXAMPLE Start-CCMDeployment -Deployment 'Complex Deployment' #> [CmdletBinding(HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/startccmdeployment")] param( [Parameter(Mandatory)] [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = Get-CCMDeployment if ($WordToComplete) { $r.name.Where{ $_ -match "^$WordToComplete" } } else { $r.name } } )] [string] $Deployment ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $id = (Get-CCMDeployment -Name $Deployment).id } process { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentPlans/Start" Method = "POST" ContentType = "application/json" Body = @{ id = "$id" } | ConvertTo-Json WebSession = $Session } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } function Stop-CCMDeployment { <# .SYNOPSIS Stops a running CCM Deployment .DESCRIPTION Stops a deployment current running in Central Management .PARAMETER Deployment The deployment to Stop .EXAMPLE Stop-CCMDeployment -Deployment 'Upgrade VLC' #> [cmdletBinding(ConfirmImpact = "high", SupportsShouldProcess, HelpUri = "https://docs.chocolatey.org/en-us/central-management/chococcm/functions/stopccmdeployment")] param( [ArgumentCompleter( { param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams) $r = (Get-CCMDeployment).Name if ($WordToComplete) { $r.Where{ $_ -match "^$WordToComplete" } } else { $r } } )] [string] $Deployment ) begin { if (-not $Session) { throw "Not authenticated! Please run Connect-CCMServer first!" } $deployId = Get-CCMDeployment -Name $Deployment | Select-Object -ExpandProperty Id } process { if ($PSCmdlet.ShouldProcess("$Deployment", "CANCEL")) { $irmParams = @{ Uri = "$($protocol)://$hostname/api/services/app/DeploymentPlans/Cancel" Method = "POST" ContentType = "application/json" Body = @{ id = "$deployId" } | ConvertTo-Json Websession = $Session } try { $null = Invoke-RestMethod @irmParams -ErrorAction Stop } catch { throw $_.Exception.Message } } } } # SIG # Begin signature block # MIIjhQYJKoZIhvcNAQcCoIIjdjCCI3ICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC/2XhxyIaW3gi7 # Tegz0e1JWkyFTMQWuQ7Yri/jT/rix6CCHX4wggUwMIIEGKADAgECAhAECRgbX9W7 # ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa # Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD # ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l # qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT # eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH # CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+ # bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo # LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB # yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK # BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v # Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow # eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl # ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA # AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK # BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j # BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s # DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS # dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6 # r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo # +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz # sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq # aGxEMrJmoecYpJpkUe8wggU5MIIEIaADAgECAhAKudMQ+yEr6IyBs9LC6M5RMA0G # CSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ # bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0 # IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcNMjEwNDI3MDAwMDAw # WhcNMjQwNDMwMjM1OTU5WjB3MQswCQYDVQQGEwJVUzEPMA0GA1UECBMGS2Fuc2Fz # MQ8wDQYDVQQHEwZUb3Bla2ExIjAgBgNVBAoTGUNob2NvbGF0ZXkgU29mdHdhcmUs # IEluYy4xIjAgBgNVBAMTGUNob2NvbGF0ZXkgU29mdHdhcmUsIEluYy4wggEiMA0G # CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChcaeNqeO3O3hzbDYYMcxvv/QNSPE4 # fpI+NGECR+FYdDO2utX9/SPxRCzWBrsgntPs/7IPk/uFZk/yTIiNoXO+cqJE45L9 # 2Ldfn6gAcwjGna/j2f/bbSFSeXW9z9lM3DJecFwXQleWR/8OKCnD+d1ZmHB0BA5v # 0bQCfU8ZT7S0u9+KAKqyqgZrJyQiPfBVqXes9RSua7+0SVXmaBrJf9njHAf5KNFY # /TEgm1r1zYwxfcsuE5eYdr2/suytUJpN18m9DmAdYm72va0KMxoKIBGuQy9DnaDI # +nMiegsdhkL9sIysIin7Pcwjkwx9lRmtIqJA27Hfgb1MaL0OnkpwRY+VAgMBAAGj # ggHEMIIBwDAfBgNVHSMEGDAWgBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4E # FgQUTvMFGF2V6ylQalFt+afRXjSaBIMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM # MAoGCCsGAQUFBwMDMHcGA1UdHwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2lj # ZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8vY3Js # NC5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDBLBgNVHSAERDBC # MDYGCWCGSAGG/WwDATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0 # LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGG # GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2Nh # Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWdu # aW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAKFxncHA # zDFesUJXaM21qMRk5+nIZcDuISfGgJcDjMHsRLw7na5Yn7IhiNY+OsKnPVkfPhL/ # MNXSHG6on+IpxiB2/Bry9thqKvpQdPBe8mFN0ctJDgrSceyRC5SA9EiO22J3YNe0 # yVEKAG+Yk2A/WhKBzCCpRskMlRr7KeLm6DvAgvDsMfkKtePMl2PraON+tFNpc2b1 # LTKT4okiU5uAWpjYAt9sYBsKTeZb5NJt0ZQ3akEEIAQs63/mSDAZlzMOJMWNK/yv # 4NU5CiPVcohJ0WjUJUIrAMmAVlZ2h8NhCXJOv28cHWEgPks/zqdDdIhJfDF+ALd1 # 0JTBrwCNcYQG68AwggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqG # SIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFz # c3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTla # MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT # EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9v # dCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8 # MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauy # efLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34Lz # B4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+x # embud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhA # kHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1Lyu # GwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2 # PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37A # lLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD7 # 6GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ # ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXA # j6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTAD # AQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF # 66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEE # bTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYB # BQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3Vy # ZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAI # MAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979X # B72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4k # vFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU # 53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pc # VIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5v # Iy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQwggau # MIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJ # BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k # aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAe # Fw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcw # FQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3Rl # ZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3 # DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9Ge # TKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0 # hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZl # jZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAsh # aG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVY # TXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1 # biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCir # c0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+ # DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA # +bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42Pg # puE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzS # M7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU # uhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6 # mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsG # AQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t # MEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl # cnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3Js # My5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAE # GTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1Z # jsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8d # B+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVp # P0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp8 # 76i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2 # nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3 # ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQ # txMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc # 4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+Y # AN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZ # vAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQr # H4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGxjCCBK6gAwIBAgIQCnpK # iJ7JmUKQBmM4TYaXnTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUG # A1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQg # RzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIyMDMyOTAwMDAw # MFoXDTMzMDMxNDIzNTk1OVowTDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD # ZXJ0LCBJbmMuMSQwIgYDVQQDExtEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMiAtIDIw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC5KpYjply8X9ZJ8BWCGPQz # 7sxcbOPgJS7SMeQ8QK77q8TjeF1+XDbq9SWNQ6OB6zhj+TyIad480jBRDTEHukZu # 6aNLSOiJQX8Nstb5hPGYPgu/CoQScWyhYiYB087DbP2sO37cKhypvTDGFtjavOuy # 8YPRn80JxblBakVCI0Fa+GDTZSw+fl69lqfw/LH09CjPQnkfO8eTB2ho5UQ0Ul8P # UN7UWSxEdMAyRxlb4pguj9DKP//GZ888k5VOhOl2GJiZERTFKwygM9tNJIXogpTh # LwPuf4UCyYbh1RgUtwRF8+A4vaK9enGY7BXn/S7s0psAiqwdjTuAaP7QWZgmzuDt # rn8oLsKe4AtLyAjRMruD+iM82f/SjLv3QyPf58NaBWJ+cCzlK7I9Y+rIroEga0OJ # yH5fsBrdGb2fdEEKr7mOCdN0oS+wVHbBkE+U7IZh/9sRL5IDMM4wt4sPXUSzQx0j # UM2R1y+d+/zNscGnxA7E70A+GToC1DGpaaBJ+XXhm+ho5GoMj+vksSF7hmdYfn8f # 6CvkFLIW1oGhytowkGvub3XAsDYmsgg7/72+f2wTGN/GbaR5Sa2Lf2GHBWj31HDj # QpXonrubS7LitkE956+nGijJrWGwoEEYGU7tR5thle0+C2Fa6j56mJJRzT/JROeA # iylCcvd5st2E6ifu/n16awIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeAMAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkwFzAI # BgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaaL3WM # aiCPnshvMB0GA1UdDgQWBBSNZLeJIf5WWESEYafqbxw2j92vDTBaBgNVHR8EUzBR # ME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVk # RzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcBAQSB # gzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgGCCsG # AQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3DQEB # CwUAA4ICAQANLSN0ptH1+OpLmT8B5PYM5K8WndmzjJeCKZxDbwEtqzi1cBG/hBmL # P13lhk++kzreKjlaOU7YhFmlvBuYquhs79FIaRk4W8+JOR1wcNlO3yMibNXf9lnL # ocLqTHbKodyhK5a4m1WpGmt90fUCCU+C1qVziMSYgN/uSZW3s8zFp+4O4e8eOIqf # 7xHJMUpYtt84fMv6XPfkU79uCnx+196Y1SlliQ+inMBl9AEiZcfqXnSmWzWSUHz0 # F6aHZE8+RokWYyBry/J70DXjSnBIqbbnHWC9BCIVJXAGcqlEO2lHEdPu6cegPk8Q # uTA25POqaQmoi35komWUEftuMvH1uzitzcCTEdUyeEpLNypM81zctoXAu3AwVXjW # mP5UbX9xqUgaeN1Gdy4besAzivhKKIwSqHPPLfnTI/KeGeANlCig69saUaCVgo4o # a6TOnXbeqXOqSGpZQ65f6vgPBkKd3wZolv4qoHRbY2beayy4eKpNcG3wLPEHFX41 # tOa1DKKZpdcVazUOhdbgLMzgDCS4fFILHpl878jIxYxYaa+rPeHPzH0VrhS/inHf # ypex2EfqHIXgRU4SHBQpWMxv03/LvsEOSm8gnK7ZczJZCOctkqEaEf4ymKZdK5fg # i9OczG21Da5HYzhHF1tvE9pqEG4fSbdEW7QICodaWQR2EaGndwITHDGCBV0wggVZ # AgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX # BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIg # QXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0ECEAq50xD7ISvojIGz0sLozlEwDQYJ # YIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG # 9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIB # FTAvBgkqhkiG9w0BCQQxIgQgrBAQiB0/raQ5dMHDiGoJ/0HIDBUA9aBEqZBmR/d3 # BHAwDQYJKoZIhvcNAQEBBQAEggEAg6w+HOUf5zqVvz4YlnyMGKgCG5noK1ZcfCAz # UDQL0JqRzWSUBjg+rC57AWqjD8u5gzxetgdxHs/Q54nf+yg4eOVb5Wz+SU6DbyPd # WAFgSxnLxqVqT7B2bKloZmtauGfKbXOb3+M+7Y8IGebqf2X0givNlkr4g8poCWft # oRSFuP/yCIKp5+D1gbXp08YvuWS2vvn56a+ZtCPVPgev9qzxz3OFL+wEuadvSwYE # qGol88Ppit9l9qDx07X/L6kUPr2FzVDwvi1ogqvDPjsysQFkDBRSG/UpFdYA04u2 # s17b1PF7olA+Bf7c4ahujWIJP0rkx7/5Mbc993tTypeJ8FIqXqGCAyAwggMcBgkq # hkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5E # aWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0 # MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAp6SoieyZlCkAZjOE2Gl50wDQYJ # YIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3 # DQEJBTEPFw0yMjA5MDgwOTI5MjVaMC8GCSqGSIb3DQEJBDEiBCDt7IVfYRx6nwvJ # YKW87yEJ4Uja4xyheLrSrZ/u+QG7uDANBgkqhkiG9w0BAQEFAASCAgBCbrh66biJ # a9hsHpsewatfRkxaE3OIXzl014hoo00ZkXHiUyWef39GVlOAx5uHnV5PuoCYlJIK # XNMAMlnJei4TZ778N1IOi4YDhRRjB+7l+BUNzTLuuQrJ9eQ4r0OO1weD1p1oX6r4 # gnXFP41jAqV4964jjrz+tgol5EaT76Fko1+5nlSV3eLjlfnHJYt/ZTlfipGKdA05 # /fBiT2llxo6TldKq6w2qyrx8xfPnNQpdWJKXl+fvh23UzVulrHWru18aSxQdKVvQ # QA+03SebNcWGl3ZP4emYuNcU2gZBgfO3kESmCL61G2KhGxuKNQSfS5xR7khJkjbp # Y0TSoAFo/ViuxupoL2oa2ZXT667HK2g9q53nJYjRKjB4p5qmp71ICj1nArlrExce # vGzWiWu6Je1zEhUS5aSv13g8x4Y99v5+RGHze5/JBvzpnk7QDbSVGA/4YBmMaByb # Al9LVKXDMyRCVmHb7F/y2wx9IwrNUbuhhUXcskUfny0l/9OxJwWbt4W5JhPy8a57 # BAdVm7Fn2VZQTF/snuheNP7ng32Dv++JZ9J8CgVFxvQmB9MC9NsKmpP1GcmzXqnc # 0h71mQY6CAD6misPmkaxmd0avNQjIth954/yomJ74K7JBXq/4zV1ATYotBaJczTx # e+pbAL6RxY8zj1E8jqAdXtxOCOAgoSlwwQ== # SIG # End signature block |