public/Revoke-UcmOffice365UserLicence.ps1
#PerformScriptSigning Function Revoke-UcmOffice365UserLicence { <# .SYNOPSIS Revokes a licence to an Office365 user .DESCRIPTION Function will check for revoke the requested licence from the supplied user .EXAMPLE PS> Revoke-UcmOffice365UserLicence -upn 'button.mash@contoso.com' -LicenceType 'MCOEV' Grants the Microsoft Phone System Licence to the user Button Mash .INPUTS This function accepts both parameter and pipline input .OUTPUT This Cmdet returns a PSCustomObject with multiple Keys to indicate status $Return.Status $Return.Message Return.Status can return one of four values "OK" : The licence has been removed. "Warn" : The licence was not assigned to the user, no changes have been made "Error" : Unable to revoke licence, check $return.message for more information "Unknown" : Cmdlet reached the end of the fucntion without returning anything, this shouldnt happen, if it does please log an issue on Github Return.Message returns descriptive text based on the outcome, mainly for logging or reporting .NOTES Version: 1.1 Date: 03/04/2021 .VERSION HISTORY 1.1: Updated to "Ucm" naming convention 1.0: Initial Public Release .REQUIRED FUNCTIONS/MODULES Modules AzureAD (Install-Module AzureAD) MSOnline (Install-Module MSOnline) UcmPSTools (Install-Module UcmPsTools) Includes Cmdlets below. Cmdlets Write-UcmLog: https://github.com/Atreidae/UcmPSTools/blob/main/public/Write-UcmLog.ps1 Test-UcmMSOLConnection: https://github.com/Atreidae/UcmPSTools/blob/main/public/Test-UcmMSOLConnection.ps1 New-UcmMSOLConnection: https://github.com/Atreidae/UcmPSTools/blob/main/public/New-UcmMSOLConnection.ps1 Write-UcmHTMLReport: https://github.com/Atreidae/UcmPSTools/blob/main/public/Write-UcmHTMLReport.ps1 .REQUIRED PERMISSIONS 'Office365 User Admin' or better .LINK https://www.UcMadScientist.com https://github.com/Atreidae/UcmPSTools .ACKNOWLEDGEMENTS Assign licenses for specific services in Office 365 using PowerShell: #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')] #Todo, update return variable to return an array of pipeline objects, so we can report the status of each one to the calling function Param ( [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=1,HelpMessage='The UPN of the user you wish to revoke the licence from, eg: button.mash@contoso.com')] [string]$UPN, [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=2,HelpMessage='The licence you wish to revoke, eg: MCOEV')] [string]$LicenceType ) #region functionSetup, Set Default Variables for HTML Reporting and Write Log $Function = 'Revoke-UcmOffice365UserLicence' [hashtable]$Return = @{} $return.function = $Function $return.Status = "Unknown" $return.Message = "Function did not return a status message" # Log why we were called Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function Write-UcmLog -Message "Parameters" -Severity 1 -Component $function -LogOnly Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly Write-UcmLog -Message "Parameters Values" -Severity 1 -Component $function -LogOnly Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly Write-UcmLog -Message "Optional Arguments" -Severity 1 -Component $function -LogOnly Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly #endregion functionSetup #region functionWork #Check to see if we are connected to MSOL $Test = (Test-UcmMSOLConnection -Reconnect) If ($Test.Status -ne "OK") { #MSOL check failed, return an error. Write-UcmLog -Message "Something went wrong granting $UPN's licence" -Severity 3 -Component $function Write-UcmLog -Message "Test-UcmMSOLConnection could not locate an MSOL connection" -Severity 2 -Component $function $Return.Status = "Error" $Return.Message = "No MSOL Connection" Return $Return } #Get the details of exisiting licences to build licence names Write-UcmLog -Message "Building Licence Prefix" -Severity 2 -Component $function Try { #Office365 does this thing where it preprends the tenant name on the licence #For example PHONESYSTEM_VIRTUALUSER would be "contoso:PHONESYSTEM_VIRTUALUSER" #So we need to learn the prefix, we do this by looking for the licence and storing it $O365AcctSku = $null $O365AcctSku = Get-MsolAccountSku | Where-Object {$_.SkuPartNumber -like $LicenceType} #Using the stored details, build the full licence name $LicenceToRevoke = "$($O365AcctSku.AccountName):$LicenceType" } Catch { #We couldnt get the licence details, it could be a permissions issue or the connection might be broken. Return an error. Write-UcmLog -Message "Error Running Get-MsolAccountSku" -Severity 3 -Component $function Write-UcmLog -Message $error[0] -Severity 3 -Component $function $Return.Status = "Error" $Return.Message = "Unable to run Get-MsolAccountSku to obtain tenant prefix" Return $Return } If ($null -eq $O365AcctSku) { #The licence requested doesnt exist on the tenant, return an error Write-UcmLog -Message "Unable to locate Licence on Tenant" -Severity 3 -Component $function $Return.Status = "Error" $Return.Message = "Unable to locate $LicenceType Licence" Return $Return } #We have built the licence details, check to see if the specified user exists. Try { Write-UcmLog -Message "Checking for Existing User $UPN ..." -Severity 2 -Component $function $O365User = (Get-MsolUser -UserPrincipalName $UPN -ErrorAction Stop) Write-UcmLog -Message "User Exists. checking licences..." -Severity 2 -Component $function #Found the user, check to see if they have the licence. If ($O365User.Licenses.accountSkuID -Notcontains $LicenceToRevoke) { #Looks like the user doesnt have that licence, skip and return a warning. Write-UcmLog -Message "User doesnt have that licence, Skipping" -Severity 3 -Component $function $Return.Status = "Warning" $Return.Message = "Skipped: Not Licenced" Return $Return } #User has the licence, try revoking licence from the user Try { Write-UcmLog -Message "User has licence, Revoke Licence" -Severity 2 -Component $function #Try Removing the licence [Void] (Set-MsolUserLicense -UserPrincipalName $UPN -RemoveLicenses $LicenceToRevoke -ErrorAction stop) Write-UcmLog -Message "Licence Revoked" -Severity 2 -Component $function #Everything went well, return OK $Return.Status = "OK" $Return.Message = "Licence Revoked" Return $Return } #Something went wrong removing the licence. Catch { #Return an error Write-UcmLog -Message "Something went wrong removing the licence from user $UPN" -Severity 3 -Component $function Write-UcmLog -Message $Error[0] -Severity 3 -Component $function $Return.Status = "Error" $Return.Message = $Error[0] Return $Return } } #End User Check try block #Todo, split this up into seperate blocks to minimise nesting. #User doesnt exist, return an error message Catch { #Return an error Write-UcmLog -Message "Something went wrong revoking $UPN's licence" -Severity 3 -Component $function Write-UcmLog -Message "Could not locate user $UPN" -Severity 2 -Component $function $Return.Status = "Error" $Return.Message = "User Not Found" Return $Return } #endregion FunctionWork #region functionReturn #Default Return Variable for my HTML Reporting Fucntion Write-UcmLog -Message "Reached end of $function without a Return Statement" -Severity 3 -Component $function $return.Status = "Unknown" $return.Message = "Write-UcmLog did not encounter return statement" Return $Return #endregion functionReturn } # SIG # Begin signature block # MIIRwgYJKoZIhvcNAQcCoIIRszCCEa8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUrTqUhu6UaBZcxHsvo7aX7X+V # cNaggg4OMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B # AQwFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk # IFJvb3QgRzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg # Q0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5 # WRuxiEL1M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJP # DqFX/IiZwZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXz # ENOLsvsI8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bq # HPNlaJGiTUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTC # fMjqGzLmysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaD # G7dqZy3SvUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urO # kfW+0/tvk2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7AD # K5GyNnm+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4 # R+Z1MI3sMJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlN # Wdt4z4FKPkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0I # U0F8WD1Hs/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYB # Af8CAQAwHQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaA # FOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK # BggrBgEFBQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v # Y3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4 # oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv # b3RHNC5jcmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcN # AQEMBQADggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcT # Ep6QRJ9L/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WT # auPrINHVUHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9 # ntSZz0rdKOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np37 # 5SFTWsPK6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0 # HKKlS43Nb3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL # 6TEa/y4ZXDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+1 # 6oh7cGvmoLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8 # M4+uKIw8y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrF # hsP2JjMMB0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy # 1lKQ/a+FSCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIHVjCC # BT6gAwIBAgIQDyLHeeRvkUFg5QtSFTT8FjANBgkqhkiG9w0BAQsFADBpMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD # ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg # Q0ExMB4XDTIzMDIyMzAwMDAwMFoXDTI2MDIyMzIzNTk1OVowXjELMAkGA1UEBhMC # QVUxETAPBgNVBAgTCFZpY3RvcmlhMRAwDgYDVQQHEwdCZXJ3aWNrMRQwEgYDVQQK # EwtKYW1lcyBBcmJlcjEUMBIGA1UEAxMLSmFtZXMgQXJiZXIwggIiMA0GCSqGSIb3 # DQEBAQUAA4ICDwAwggIKAoICAQC47oExh25TrxvApIYdMRYvjOdZCb8WwgeTemm3 # ZY7BElIWu6+gzRGqQe8RFsN7oIgin5pvjTYIToxt1CCag2A5o8L0NtULmxJEegc+ # VaF24DZQqI4qGQGH/Qnglqys6+yPkwLnfeSxpeWe4u49HUUGDFIxHCh42MlCLp/f # fHT49QhhpO+LyeLnDoUs6DmahyIb6NeE2cW5AYRXEesW7GRNfXzygBSlVWJOgvcy # V5Y4IvAZVx2hKKMTjYFIz4/RYMg7fwYZEJ2LRJ/GnVazobKAvh6ZBet5KwVNI9EI # 29DtWQyK/RoPOguTRcB5VuiZVlv0xjBYM7iJuH2Soa3StQYVxL/5gjZCC9WOs4NR # EIGU3XmHoogFDvoT1vf1izMPFQzdZfgPvy/XXsbgTVo5ncesJ6WtZwqwCXG1K0XW # IPZqTHolc1MyU6K1bEHO+7YWLpKgM9THl644G7PEhcKpNDsHlfvLVQdYhI55UJtc # iyMrTw11CNECvk3GK1mrluvKsrxdaH6G3Sp9VVHRtef6OZ5SlzkM5ID4egB2bXRb # R/69bEuZr5hhm+v2lBSWIbZj/Mva6i/a/TAvy4vvPLo3DRcASkYZDC4T8gDMzmpG # Xs4jAc9sfTL9z+o5u1PLJHFGRjJ+Wa2CgSftCdbKLjn+AY9m8ipc8jmOBKNY9yGI # pQWapQIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0 # TkIwHQYDVR0OBBYEFOBsg1xudlbXVSql8pWbiHoTyZS/MA4GA1UdDwEB/wQEAwIH # gDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu # Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hB # Mzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcC # ARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCB # hDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUF # BzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVk # RzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAA # MA0GCSqGSIb3DQEBCwUAA4ICAQBOh5vRXqTCQzv0T1F2EgDkq5VnljlHsa7Ov/d/ # lpOvr8NebBiatxbhfhPCnToY7BD2f7YVsUuQ+VDdcIYsskcU5spBHcFYidg2jGu4 # 59FGMaS765XStDwGGTN/360gEsNYSnKWYL4+8jYWHlzRO0jHloyWz+gF5dYWzdDJ # u1dudLIJ0RgrEVJeLSgIBWygLL5EyIzOPlrxztsILMSbdPTQLeBIm7ipOk4EACx1 # hhBVUsUoCAlASH+yCKDU4v2HFd7SzrkRUrf7XJ2Na2YsiHjiTGqHIE86KyvxGDhT # 3n2/jX23Nh/bkWHurHwTfaTCOQ44ZlAbnZQjBlmrFn5hPMXRpciiQFmrKTPD/nuo # 9MVnCciHEpHJ63/JZNF/eno1122/wVkL7MuRlCVHN7L/wuNQxQk3ARdIju6OD/Gi # Mwg0Qih6HVWJtkHK3ExoUKKKUZCOvIeHxzp+K6FWUupPZKUgWzn4AHMxm6zr+Sde # laIAACqAkxYsDYKbM7WlNi3uIH2HeXqU9uSDt5tgPpImrog/ab4HrhpDfITRgT1c # cxaWQezpJEPC+kqVD41T3wlEie1Qm4vYWg+oBVEMBxVLh6CYbeppCRTEXRGnAiCH # /Ma1uwyWnNCWxrhd1uSi6sj4ISzgnFyGCvsI0gavKpS5AQhapJgk6/fULTFeS+Ee # kRH9FDGCAx4wggMaAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD # ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2ln # bmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDyLHeeRvkUFg5QtSFTT8FjAJ # BgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B # CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAj # BgkqhkiG9w0BCQQxFgQU7K/iVMLN1Fry+CQYvvax3elSeT0wDQYJKoZIhvcNAQEB # BQAEggIAgp12U/BTRoaDLngwZRIR+O/Rq0LkYLWLc3WMVT+NBTlTBGdMtBCVkR4e # 6hZZkHy+o3+AniCY/vJ5RyEg7ExCMfbsDve8sjD6j66uYeGMg1MGl/puQIiRHdIz # Q7BTosALnYqGgr46RBmKx694oA8n9vh4uoc0R+SVecax95kkQM1ubcb+VUhXmXeV # mI+el0BT7yf5S5aub14ACL+sDOtTVaSkFgXqjpx2PhcbxD1fFNI4z0DNTNkzmfD0 # uMjnVxu4rJNd13baODmLvf+kvU3fAEfnRsDch7A056Wk6B19SHeCuxo1sD3tfAof # xQUQvmtxE4dani6DxjLe7tsCotwRtW/qqj1ZVb0C7eTxV4jjudJFgXCxNgt1Nk73 # VGtLub4UW1rZTCUMayOUoBSKTGqy/wSQZgL/n4YfH0qYBqEHCDqVeiymjUnCUWHe # Ws/9NnJ3uI8TfduugFiv0SB/8UvuFkwJDEBP4fBGFg7Y/952Hh8Kp+bb3sl6aKRo # sRgJH/5mXsJBcNYq3bepKTUqy6VrGQ0l1NiaZQ8vJq1fF+rbvY+5T1X0J9hnLG5x # 1czkIyZtLtVF1vjeIvgv1bGsTpU2Z3DB/Q5mV6IwFlZNsOAnZOxVuTXJcOhMx2FS # dOveSQAcFfMrvXaGw6AoqebDZ4/TNMLYna+2YMjvem87TU2pIWo= # SIG # End signature block |