Friday, August 10, 2018

Module for searching your full PowerShell command history

By default the Get-History cmdlet only returns lines from your current session. Therefore I've created a small module that gets history items from your full history, by default the most recent 4096 commands.
It features an easy search option and a few switches for case sensitivit and uniqueness.

To have it automatically available every time you start PowerShell do the following.

1. Find a module path by running
$env:PSModulePath
2. Select one. I picked "C:\Users\jgste\OneDrive\Documenten\WindowsPowerShell\Modules"
3. Ensure this folder exists, else create it
4. Create a subfolder "FullHistory"
5. Save the code below as "FullHistory.psm1" in the new folder
6. Start a new PowerShell session. Get-FullHistory should be available.


Function Get-FullHistory {

<#
.Synopsis
Get lines from the full history of PowerShell commands
.Description
This command will query your PowerShell command log. By default this keeps your most recent 4096 commands
.Parameter FilterValue
A string to filter your results by.
.Parameter CaseSensitive
Set to filter Case Sensitive. Default is insensitive
.Parameter Unique
Set to false to receive duplicate results. Default is true
.Parameter HistoryPath
Set to use an alternative path to the history file. Usefull when you want use another users history.

.Example
PS C:\> Get-FullHistory content
get-command Get-PnPContentType
Get-PnPProvisioningTemplate -Out ".\MyFields.pnp"  -Handlers Fields,ContentTypes
get-content .\newfile.txt
get-content .\bubblesort.ps1
        
.Example
PS C:\> Get-FullHistory content -Unique:$false
get-command Get-PnPContentType
Get-PnPProvisioningTemplate -Out ".\MyFields.pnp"  -Handlers Fields,ContentTypes
get-command Get-PnPContentType
get-content .\newfile.txt
get-content .\newfile.txt
get-content .\bubblesort.ps1

.Example
PS C:\> Get-FullHistory content -CaseSensitive
get-content .\newfile.txt
get-content .\bubblesort.ps1

.Link
https://sharedpointers.blogspot.com
#>
    [cmdletbinding()]
    Param(
        [Parameter(Position=0)]        
        [String]$FilterValue,
        [Switch]$CaseSensitive = $false,
        [Switch]$Unique = $true,
        [String]$HistoryPath = $HOME + "\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt"
    )
 
    Begin {
        Write-Verbose "Starting $($MyInvocation.Mycommand)"
        if (-not (Test-Path $HistoryPath)) {
            Write-Host "Could not find history file at $HistoryPath !"
            exit
        } 
        $FilterValue = "*" + $FilterValue + "*"
    } 
 
    Process { 
        [System.IO.File]::ReadLines($HistoryPath)| Foreach-Object { 
            if ($FilterValue) {
                if ($CaseSensitive) {
                    if ($_ -clike $FilterValue){
                        return $_
                    }
                }
                else {
                    if ($_ -like $FilterValue) {
                        return $_
                    }
                }
            }
            else {
                return $_
            }
        } | Select-Object -Unique:$Unique 
    } 
 
    End {
        Write-Verbose "Ending $($MyInvocation.Mycommand)"
    } 
}


Thursday, July 13, 2017

Removing SP App Principals through PowerShell

PowerShell support for SharePoint Add-ins is very minimal. A limitation I ran into today is that you can use PowerShell to register an App Principal, or to retrieve one, but deleting the principal is not possible.

Luckily, Anand Srinivasan already blogged on how to achieve this through .Net code:
link

I've taken the liberty to update his sample to PowerShell code, since that fits my usage scenario's better;

$site = get-spweb "https://spsiteurl.local" 
$clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$manager = [Microsoft.SharePoint.SPAppPrincipalManager]::GetManager($site)
$realm = Get-SPAuthenticationRealm -ServiceContext $site.Site
$nameIdentifier = $clientId + '@' + $realm
$appPrincipal = Get-SPAppPrincipal -Site $site -NameIdentifier $nameIdentifier
$manager.DeleteAppPrincipal($appPrincipal)

Thursday, November 10, 2016

Powershell script for retrieving stored wireless passwords

On W10 it is trivially easy to retrieve stored passwords for wireless netwerk. I cobled the following script together to store the passwords before a migration. Note that this assumes an English version of Windows, on some machines you may need to modify the 'User Profiles' and 'Key Content' strings.

Run the following to find the string on your system;



And



And the full script:

$output = netsh wlan show profiles
$from = $output.IndexOf("User profiles")

$output = $output[($from+2)..($output.length-2)]

$results = @()

foreach ($entry in $output) {
  $networkName = ($entry.Split(':')[1]).Trim()

  $networkInfo = netsh wlan show profile name=$networkName key=clear

  foreach($infoLine in $networkInfo) {
    if ($infoLine.Trim().StartsWith("Key Content")){
      $password = ($infoLine.Split(':')[1]).Trim()

      if ($password -ne "Absent"){
        Write-Host Processing $networkName
        $networkProperties = @{
          Name = $networkName
          Password = $password
        }
                      $results += (New-Object PSObject -Property $networkProperties)
      }
    }
  }
}

$results | ft Name,Password


Monday, September 19, 2016

Change password in nested RDP session

Just documenting two methods that work, since I'm tired of googling this every few months:

1. PowerShell command:
(New-Object -COM Shell.Application).WindowsSecurity()

2. On screen keyboard

- Start OSK in the session you want to change the password for
- Hold Ctrl+Alt on your physical keyboard
- Press Del on the OSK

Monday, February 29, 2016

Downloading from MSDN and Microsoft with Internet Explorer

Internet Explorer on Windows Server (2012) is locked down. In an attempt to make the browser more secure, scripts downloaded from untrusted domains can not be executed. This makes downloading the software you need to install on the server difficult.

Usually, you would use another machine for downloading. However, if that's not an option, and you don't want to install another browser or open all security settings for Internet Explorer, you'll need to reconfigure Internet Explorer to trust some specific domains. I've noted below the domains I had to add to my trusted sites list, in order to get MSDN and Microsoft downloads to work. Add them by clicking the "cog wheel" -> "Internet Options" -> "Security" -> "Trusted Sites" -> "Sites". Then add the URL's and press "Add"  for everyone.

For MSDN Subscriber Downloads

  • https://auth.gfx.ms
  • https://login.live.com
  • https://*.sec.s-msft.com
  • https://ajax.aspnetcdn.com
  • https://*.microsoft.com
  • http://download.msdn.microsoft.com
Microsoft downloads (www.microsoft.com)

  • https://assets.onestore.ms
  • https://mem.gfx.ms
  • https://*.s-microsoft.com

Also ensure you have script execution enabled for trusted sites: "cog wheel" -> "Internet Options" -> "Security" -> "Trusted Sites" -> "Custom Level"





Friday, November 27, 2015

Limiting Add-in visibility in the corporate store

SharePoint Add-ins * are distributed through the App Catalog. This is a great method for end users to add functionality to their sites. Most apps you want to have available to as many users as possible, everybody should be able to add the local weather app, or the handy currency calculator to their sites.

However, some Add-ins should not be available to everyone. Think for example of Add-ins that allow for access into line of business applications. Users will probably not even be able to use the Add-in, if they are not able to access the LOB application, but you don't want them to be able to add the app to their sites in the first place.

I see five ways of preventing users from adding the app to their site. Spoiler alert; I think the fifth is the right way, so skip ahead if you are just looking for that :)


1. Sideloading the Add-in
Add-ins don't have to be distributed through the catalog. You can enable sideloading and just publish the Add-in to the target site directly. However, this is a development feature, meant for developers to quickly deploy and test their Add-ins. The catalog gives you a central place to push updates to the Add-in, and get error reports from the instances. Basically, you should not use developer tools to power a production scenario.

Still want to use sideloading? Look here for ways to enable this.


2. Remove the Add-in from the catalog
You could just quickly remove the Add-in from the catalog after you have deployed it to the right sites. However, you would have to add it again every time you want to add the Add-in somewhere else, and you lose the centralized error tracking. Furthermore, you would have to add it again to perform upgrades, which you then have to execute quickly on all instances, before pulling the Add-in again. All-in-all, quite cumbersome.


3. Check the location in the app installed event
If the app is a provider hosted app, you could execute the 'App Installing' event. This allows you to execute checks on for example the location the Add-in is being added, or the identity of the person doing the adding. If there is a mismatch, you can block the installation.

To use this method however, you need to be the developer of the Add-in. Furthermore, it requires you to either package the app with allowed locations or identities, or also create a location to manage this (the web.config of the provider hosted Add-in would be a logical place). Finally, this method doesn't prevent the user from seeing the Add-in in the catalog, just from adding it to the site.


4. Ask for extra permissions for the App
This might just be the easiest solution, but also the worst. Do not do this. :)

You could let your app just ask for extra permissions. For instance, tenant admin permissions. Remember, a user cannot install an Add-in with more permissions than the user has. So only tenant admins will be able to add an Add-in which requests tenant admin permissions.

However, if somebody manages to subvert the Add-in into doing something it shouldn't, it will be doing so with tenant admin permissions. This is bad and goes against the principle of least privilege.


5. Break permissions in the App Catalog
Still with us? This is the one I ended up using. You can use item level security in the App catalog. Just add the Add-in as you always would, but afterwards edit the security for the item:


 And stop inheriting:



Remember, the App Catalog is just a SharePoint library, so this is all possible with the OOTB tooling. Just remove the permissions to see the Add-in for anyone you don't want to let install the Add-in.

I like this way because it's easy, intuitive, and secure. It does not mess with updates or error reporting. However, I haven't been able to gather info anywhere on whether this is a supported scenario. Does Microsoft support breaking these permissions? Until I find somewhere that they don't, this is the way I'm leaning.



So, the fifth it is, according to me. But it is still not ideal. Do you disagree, or have another way? Please leave a comment below.

* note that I'm mention Add-in, instead of App. I seem to have finally accepted the change!

SharePoint remote event receivers returning 405 (Method not allowed)

The SharePoint app* infrastructure is a notably fickle beast. I've seen grown system administrators reduced to tears by obtuse error messages and exotic requirements which don't fit company infrastructure policies (wildcard certificates, no support for SSL offloading, multiple IP's or NICs etc etc.).

This week I was confronted by a new one. A provider hosted app we were deploying was unable to register remote event receivers (which we do from another remote event receiver triggered by the app installed event). The provider hosted app returned http error 405 (method not allowed). The relevant section of the ULS:

Error when get token for app i:0i.t|ms.sp.ext|xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, exception: Microsoft.SharePoint.SPException: The Azure Access Control service is unavailable.    
 at Microsoft.SharePoint.ApplicationServices.SPApplicationContext.GetApplicationSecurityTokenServicesUri(SPServiceContext serviceContext)    
 at Microsoft.SharePoint.ApplicationServices.SPApplicationContext..ctor(SPServiceContext serviceContext, SPIdentityContext userIdentity, OAuth2EndpointIdentity applicationEndPoint)     

Calling remote event receiver failed. URL = [https://xxxxxxxxxxxxxx.com/xxxx/Services/AppEventReceiver.svc], App Identifier = [i:0i.t|ms.sp.ext|xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx], Event Type = [AppInstalled], Exception = [The remote server returned an unexpected response: (405) Method Not Allowed.] 


The message that jumped at me directly is "The Azure Access Control service is unavailable". I'd hope so with my on premises SharePoint and provider hosted app deployment! This message however is a red herring and expected in an on premises configuration. I found multiple other possible reasons for a 405, including;
  • Certificate problems
  • SSL / TLS supported version mismatch between App server and SharePoint server
  • No handler in IIS for the incoming request

As the famous detective states; "when you have eliminated the impossible, whatever remains, however improbable, must be the truth". It turned out the App Server was missing a feature, specifically the ".Net Framework 4.5 > WCF Services > HTTP Activation" feature:



Adding this one allowed me to finally install the App. It's not clear why the feature was not installed, it was in the install scripts for the App server.


I hope if you're confronted by this 405 you'll find this post, and spend less time on it then I did.



* note the deliberate use of 'App' instead of 'Add-in'. It will require me some time to make the mental switch.

Rating