PowerShell Cmdlets of Office 365: Finding The Changes

Taking what we learned from the last blog post on the changing of Office 365 cmdlet we will now assemble a full fledge script that will reporting on these changes in a daily fashion. Where do we start? If we remember from the other post multiple connections to Office 365 will be required. Each of these connections connect to a different PowerShell URL. Would it be efficient to constantly have to enter credentials in order to connect to each individual service? Probably not. So, let’s start with that and work our way to building a script that will automate this process for us.

STEP ONE: Create Password file

In order to store the password for reference later, we’ll need to read in the password and store it securely using this methodology:

 read-host -assecurestring | convertfrom-securestring | out-file C:\securestring.txt

Now we have our password stored in a secure file for later reference:

Further Reading on the cmdlets used:

Link – https://technet.microsoft.com/en-us/library/ee176935.aspx
Link – https://msdn.microsoft.com/en-us/powershell/reference/5.0/microsoft.powershell.security/convertfrom-securestring

Caution: Remember that this file contains your password. Make sure to secure it so that no one else has access to use it.

STEP TWO: Credential Configuration

For connecting to Office 365, we’ll need to User name in addition to the password created in Step One. First we can store the user name of the account connecting to PowerShell in the $UserName variable. The we can read the password file and store that in a variable. Taking these two bits of information, we can then create a variable to store proper credentials for logging into Office 365’s PowerShell URLs.

$username = "damian8192@scoles.onmicrosoft.com"
$password = cat C:\securestring-scolesfamily.txt | convertto-securestring
$LiveCred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password

** Note ** One thing to remember is that PowerShell cmdlets that are revealed are limited by Microsoft’s RBAC. Microsoft has provided documentation on this with on premises solutions as well as cloud based ones. For example, Azure PowerShell’s RBAC is documented here:

Link – https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-manage-access-powershell
Further Reading on secure passwords

Link – https://social.technet.microsoft.com/wiki/contents/articles/4546.working-with-passwords-secure-strings-and-credentials-in-windows-powershell.aspx

STEP THREE: Variable Definitions

When it comes to static variables this script requires a few be defined in order for the script to perform as expected. For example, the script will send an email out and as such we need to define SMTP parameters like sending address, recipients and more. We’ll also keep track of the current date and time for other functions in the script.

$From = "Notifications@Domain.Com"
$SMTPServer = "<IP OF SMTP SERVER>"
$To = "Damian@Domain.Com"
$AdminAddress = "Admin@Domain.Com"
$Date = get-date -Format "MM.dd.yyyy-hh.mm-tt"
$EmailDate = get-date -Format "MM.dd.yyyy"

These variables will be referenced in file creation and for email reports being sent.

STEP FOUR: Import Chart of Current Cmdlet Counts

Now this step is a bit out of order as we need to create a chart before we can import it. However, let’s assume that you created the chart at the end of the last blog post and can now reference it for this PowerShell script. Keep in mind that the list of cmdlets is not comprehensive, but concentrated on the most common workloads This chart contains the count of cmdlets for each PowerShell Module that was found the last time the script was run

# Read in CSC file for comparison
$CSV = Import-CSV 'c:\Downloads\PowershellCmdlets\Historical\CurrentChart.csv'

Foreach ($line in $CSV) {
    $ExchangeNum = $Line.ExchangeOnline
    $ADSyncNum = $Line.ADSync
    $AzureNum = $Line.Azure
    $AzureStorageNum = $Line.AzureStorage
    $AzureRMNum = $Line.AzureRM
    $MicrosoftNum = $Line.Microsoft
    $MSOLNum = $Line.MSOnline
    $SaCNum = $Line.SecurityAndCompliance
    $SkypeNum = $Line.Skype
}

The order of connections is not important. What is important is that all of the services offered by Office 365 be connected to. Now all the values for previously found cmdlet counts are stored in variables to be referenced below.

STEP Five: Connecting to Each Service

First we’ll make a connect to our tenant on the https://ps.outlook.com/powershell/ URL. There are a few modules that exist off this particular PowerShell URL. Once we connect to this URL, we can get a list of cmdlets with the below modules:

  • ADSync
  • Azure
  • Azure Storage
  • AzureRM
  • Microsoft
  • Exchange Online

Making the connection

With the two one-liners below, we leverage the credentials stored above in the $Livecred variable:

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession $Session

Once the connection is made, we can list the cmdlets and see the various cmdlets respective Modules:



The same list can be sorted by ModuleName to provide a cleaner sense of what is available in terms of modules:

get-command |Sort-Object 'modulename' 

To keep track of how many cmdlets there are per module, we can use the following method:

$Service = 'ADSync'
$NewADSyncNum = (get-command | where {$_.modulename -eq $Service}).count 

Each of the above modules cmdlet counts can be gathered using the same method.:

$NewAzureNum = (get-command | where {$_.modulename -eq $Service}).count
$NewAzureStorageNum = (get-command | where {$_.modulename -eq $Service}).count
$NewAzureRMNum = (get-command | where {$_.modulename -like $Service}).count
$NewMicrosoftNum = (get-command | where {$_.modulename -like $Service}).count



However, Exchange Online is not listed as a valid Module Name. How do we find the Module name for Exchange cmdlets? If we look at the initial connection to Office 365, we’ll see that there is a Module Name provided:


Just looking at the module name, it’s apparent that the name is dynamic and thus temporary. This means that a script that uses that name, will have a hard time the next time its run as that module name will surely be different. How can we isolate just that one set of modules then if the name is dynamic? Maybe there is another criteria that we can filter by to find Exchange cmdlets?

Taking another look at the full list of cmdlets we see that there is only one module name that matches the ModuleType of ‘Script’ and that is the one for Exchange Online cmdlets. This then leaves us to code a more complex way to filter out these cmdlets.

$ModuleName = (Get-Module | where {$_.ModuleType -eq "Script"}).name
foreach ($Name in $ModuleName) {
	if ($Name -like "tmp*") {
		        $NewExchangeNum = (Get-Command | where {$_.ModuleName -eq $Name}).Count
	}
}

Below is a summary of variables in user to keep track of the cmdlet counts for each module:



We’ve now gathered all we want from this PowerShell URL. We should close our connection:

# Cleanup - Skype Online
Get-PsSession | Remove-PSSession

Once that connection is closed we can open a new one. The next one we can connect to is ‘MSOnline’:

Import-Module MsOnline
Connect-MsolService -Credential $LiveCred

Once connected we can gather cmdlet counts:

$NewMSOLNum = (get-command | where {$_.modulename -like 'MSOnlin*'}).count

Then close the session:

# Cleanup - MSOnline Connection
Get-PsSession | Remove-PSSession

Then once that connection is closed we can open a new one. The next one we can connect to is the Security and Compliance Center:

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession $Session

Once connected we can gather cmdlet counts:

$NewSaCNum = (get-command | where {$_.modulename -eq $Name}).count

Then close the session:

# Cleanup - Security and Compliance Connection
Get-PsSession | Remove-PSSession

Next we’ll connect to Skype Online:

Import-Module SkypeOnlineConnector
$sfboSession = New-CsOnlineSession -Credential $LiveCred
Import-PSSession $sfboSession

Once connected we can gather cmdlet counts:

$NewSkypeNum = (Get-Command | where {$_.ModuleName -eq $Name}).Count

Then close the session:

# Cleanup - SkypeOnline Connection
Get-PsSession | Remove-PSSession

Finally we’ll connect to SharePoint Online:

$orgName="<Office 365 Tenant Name>"
Connect-SPOService -Url https://$orgName-admin.sharepoint.com -Credential $LiveCred

Once connected we can gather cmdlet counts:

$orgName="Scoles"
Connect-SPOService -Url https://$orgName-admin.sharepoint.com -Credential $LiveCred
$Service = "Microsoft.Online.SharePoint.PowerShell"
$NewSharepointNum = (Get-Command | where {$_.ModuleName -eq $Service}).Count
Get-PsSession | Remove-PSSession

Then close the session:

# Cleanup - SharePoint Online Connection
Get-PsSession | Remove-PSSession

STEP SIX: Comparing values

In this section we can compare the values we’ve stored in our cmdlet count variables to see if any changes have occurred since the script was last run. We’ll need to checked each individually using IF statements like so:

if ($NewExchangeNum -ne $ExchangeNum) 

Within the loop we can keep track of which cmdlets registered changes and which did not. We do so with a variable called $Change, setting a different value if changes have occurred or not. For example, if no changes occurred, we would register this:

“AdSync Cmdlets did not change in number.”

And if cmdlet have changed, then we can register this:

“AzureStorage Cmdlets changed from $AzureStorageNum to $NewAzureStorageNum.”

Once we’ve gathered all of the changes (or no changes), the variable can be referenced in the email that is sent out as a notification.
STEP SEVEN: Current Cmdlet List

In order to know what has changed, a dump of all cmdlets for each Module is created. The file name used will have the name of the module as well as a current timestamp.

$Service = "MSOnline"
get-command | where {$_.ModuleName -like "msonlin*"} | select-object name > "c:\downloads\PowerShellCmdlets\$Service-$Date.txt"

STEP EIGHT: Comparing values

The last step of the script is to send an email out to

# Send email if change was made
if ($MailRequired -ceq 1) {
$Subject = "Some Office 365 PowerShell Cmdlets Changed on $EmailDate"
    $body = (Get-Content c:\Downloads\PowershellCmdlets\Historical\CurrentChanges.csv) -join '<BR>'
    send-mailmessage -to $To -from $From -subject $subject -bodyashtml -body $body -smtpserver $SMTPServer 
} Else {

    $Subject = "No Office 365 PowerShell Cmdlets Changed on $EmailDate"
    $body = "No Office 365 PowerShell Cmdlets Changed on $EmailDate."
    send-mailmessage -to $To -from $From -subject $subject -bodyashtml -body $body -smtpserver $SMTPServer 
}

STEP NINE: Full Script

All the parts from above are included in the script below. Some additional logic and information has been placed into the script to make it more useful:

# Office 365 User Name and Password
$username = "<Office 365 account with appropriate rights>"
$password = cat C:\securestring.txt | convertto-securestring
$LiveCred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password

# Other Variables
$from = "Notifications@Domain.Com"
$SMTPServer = "<SMTP Server's IP Address>"
$to = "Damian@Domain.Com"
$AdminAddress = "Admin@Domain.Com"
$Date = get-date -Format "MM.dd.yyyy-hh.mm-tt"
$EmailDate = get-date -Format "MM.dd.yyyy"

# Read in CSC file for comparison
$CSV = Import-CSV 'c:\Scripting\PowershellCmdlets\Historical\CurrentChart.csv'

Foreach ($line in $CSV) {
    $ExchangeNum = $Line.ExchangeOnline
    $ADSyncNum = $Line.ADSync
    $AzureNum = $Line.Azure
    $AzureStorageNum = $Line.AzureStorage
    $AzureRMNum = $Line.AzureRM
    $MicrosoftNum = $Line.Microsoft
    $MSOLNum = $Line.MSOnline
    $SaCNum = $Line.SecurityAndCompliance
    $SkypeNum = $Line.Skype
    $SharePointNum = $Line.SharePoint
}

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession $Session
$ModuleName = (Get-Module | where {$_.ModuleType -eq "Script"}).name
foreach ($Name in $ModuleName) {
    if ($Name -like "tmp*") {
        $NewExchangeNum = (Get-Command | where {$_.ModuleName -eq $Name}).Count
        Write-Host "Exchange has $NewExchangeNum Cmdlets now."
        $Service = "ExchangeOnline"
        get-command | where {$_.ModuleName -eq $Name} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"
    }
}

# ADSync Cmdlets
$Service = 'ADSync'
$NewADSyncNum = (get-command | where {$_.modulename -eq $Service}).count  
Write-Host "The service $Service has $NewADSyncNum cmdlets now."
get-command | where {$_.ModuleName -eq $Service} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"

# Azure Cmdlets
$Service = 'Azure'
$NewAzureNum = (get-command | where {$_.modulename -eq $Service}).count  
Write-Host "The service $Service has $NewAzureNum cmdlets now."
get-command | where {$_.ModuleName -eq $Service} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"
 
# Azure Storage Cmdlets
$Service = 'Azure.Storage'
$NewAzureStorageNum = (get-command | where {$_.modulename -eq $Service}).count  
Write-Host "The service $Service has $NewAzureStorageNum cmdlets now."
get-command | where {$_.ModuleName -eq $Service} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"

# AzureRM Cmdlets
$Service = 'AzureRM*'
$NewAzureRMNum = (get-command | where {$_.modulename -like $Service}).count  
Write-Host "The service $Service has $NewAzureRMNum cmdlets now."
$Name = $Service.Substring(0,$Service.Length-1)
get-command | where {$_.ModuleName -like $Service} | select-object name > "c:\Scripting\PowerShellCmdlets\$Name-$Date.txt"

# Microsoft Cmdlets
$Service = 'Microsoft*'
$NewMicrosoftNum = (get-command | where {$_.modulename -like $Service}).count  
Write-Host "The service $Service has $NewMicrosoftNum cmdlets now."
$Name = $Service.Substring(0,$Service.Length-1)
get-command | where {$_.ModuleName -like $Service} | select-object name > "c:\Scripting\PowerShellCmdlets\$Name-$Date.txt"

# Cleanup - Main Connection
Get-PsSession | Remove-PSSession

# MSOnline
Import-Module MsOnline
Connect-MsolService -Credential $LiveCred
$NewMSOLNum = (get-command | where {$_.modulename -like 'MSOnlin*'}).count
Write-Host "The MSOL Service has $NewMSOLNum Cmdlets now."
$Service = "MSOnline"
get-command | where {$_.ModuleName -like "msonlin*"} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"
# Cleanup - MSOnline Connection
Get-PsSession | Remove-PSSession
#Email results to me

# Compliance
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession $Session
$ModuleName = (Get-Module | where {$_.ModuleType -eq "Script"}).name
foreach ($name in $ModuleName) {
    if ($Name -like "tmp*") {
        $NewSaCNum = (get-command | where {$_.modulename -eq $Name}).count
        Write-Host "The Security and Complance Center has $NewSaCNum Cmdlets now."
        $Service = "SecurityAndCompliance"
        get-command | where {$_.ModuleName -eq $Service} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"        
    }
}
# Cleanup - Compliance Connection
Get-PsSession | Remove-PSSession

# Skype Online
Import-Module SkypeOnlineConnector
$sfboSession = New-CsOnlineSession -Credential $LiveCred
Import-PSSession $sfboSession
$ModuleName = (Get-Module | where {$_.ModuleType -eq "Script"}).name
foreach ($Name in $ModuleName) {
    if ($Name -like "tmp*") {
        $NewSkypeNum = (Get-Command | where {$_.ModuleName -eq $Name}).Count
        Write-Host "Skype Online has $NewSkypeNum cmdlets now."
        $Service = "SkypeOnline"
        get-command | where {$_.ModuleName -eq $Name} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"
    }
}
# Cleanup - Skype Online
Get-PsSession | Remove-PSSession

# SharePoint Online
# Connect
$orgName="Scoles"
Connect-SPOService -Url https://$orgName-admin.sharepoint.com -Credential $LiveCred
$Service = "Microsoft.Online.SharePoint.PowerShell"
$NewSharepointNum = (Get-Command | where {$_.ModuleName -eq $Service}).Count
Write-Host "SharePoint Online has $NewSharepointNum cmdlets now."
get-command | where {$_.ModuleName -eq $Name} | select-object name > "c:\Scripting\PowerShellCmdlets\$Service-$Date.txt"
# Cleanup - SharePoint Online
Get-PsSession | Remove-PSSession

# ReWrite the CSV File
# Header
$HeaderRow = "ExchangeOnline,"+"ADSync,"+"Azure,"+"AzureStorage,"+"AzureRM,"+"Microsoft,"+"MSOnline,"+"SecurityAndCompliance,"+"Skype,"+"SharePoint"
$HeaderRow > 'c:\Scripting\PowershellCmdlets\Historical\CurrentChart.csv'
# New numbers
$NewRow = "$NewExchangeNum," + "$NewADSyncNum," + "$NewAzureNum," + "$NewAzureStorageNum," + "$NewAzureRMNum," + "$NewMicrosoftNum," + "$NewMSOLNum," + "$NewSaCNum," + "$NewSkypeNum," + "$NewSharePointNum"
Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChart.csv' $NewRow

# Add row to historical data
$NewRow = "$Date,"+"$NewExchangeNum," + "$NewADSyncNum," + "$NewAzureNum," + "$NewAzureStorageNum," + "$NewAzureRMNum," + "$NewMicrosoftNum," + "$NewMSOLNum," + "$NewSaCNum," + "$NewSkypeNum," + "$NewSharePointNum"
Add-Content 'c:\Scripting\PowershellCmdlets\Historical\FullChart.csv' $NewRow

# Email
if ($NewExchangeNum -ne $ExchangeNum) {
    $change = "ExchangeOnline Cmdlets changed from $ExchangeNum to $NewExchangeNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "ExchangeOnline Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewADSyncNum -ne $ADSyncNum) {
    $change = "AdSync Cmdlets changed from $ADSyncNum to $NewADSyncNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "AdSync Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewAzureNum -ne $AzureNum) {
    $change = "Azure Cmdlets changed from $AzureNum to $AzureNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "Azure Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewAzureStorageNum -ne $AzureStorageNum) {
    $change = "AzureStorage Cmdlets changed from $AzureStorageNum to $NewAzureStorageNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "AzureStorage Cmdlets did not change in number."
}
if ($NewAzureRMNum -ne $AzureRMNum) {
    $change = "AzureRM Cmdlets changed from $AzureRMNum to $NewAzureRMNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "ExchangeOnline Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewMicrosoftNum -ne $MicrosoftNum) {
    $change = "Microsoft Cmdlets changed from $MicrosoftNum to $NewMicrosoftNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "AzureRM Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewMSOLNum -ne $MSOLNum) {
    $change = "MSOnline Cmdlets changed from $MSOLNum to $NewMSOLNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "MSOnline Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewSaCNum -ne $SaCNum) {
    $change = "Security and Compliance Cmdlets changed from $SaCNum to $NewSaCNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "Security and Compliance Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewSkypeNum -ne $SkypeNum) {
    $change = "SkypeOnline Cmdlets changed from $SkypeNum to $NewSkypeNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "SkypeOnline Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}
if ($NewSharePointNum -ne $SharePointNum) {
    $change = "SharePointOnline Cmdlets changed from $SharePointNum to $NewSharePointNum."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $Change
    $MailRequired = 1
} Else {
    $NoChange = "SharePointOnline Cmdlets did not change in number."
    Add-Content 'c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv' $NoChange
}

# Send email if change was made
if ($MailRequired -ceq 1) {
$Subject = "Some Office 365 PowerShell Cmdlets Changed on $EmailDate"
    $body = (Get-Content c:\Scripting\PowershellCmdlets\Historical\CurrentChanges.csv) -join '<BR>'
    send-mailmessage -to $To -from $From -subject $subject -bodyashtml -body $body -smtpserver $SMTPServer 
} Else {

    $Subject = "No Office 365 PowerShell Cmdlets Changed on $EmailDate"
    $body = "No Office 365 PowerShell Cmdlets Changed on $EmailDate."
    send-mailmessage -to $To -from $From -subject $subject -bodyashtml -body $body -smtpserver $SMTPServer 
}

# Final Cleanup
Get-PSSession | Remove-PSSession
Remove-Item "C:\Scripting\PowerShellCmdlets\historical\CurrentChanges.csv"

Email results

No Changes


Changes


Final Steps

The last step is to schedule the script. This is outside the scope of the blog post, but you can easily follow steps provided:

Here – https://blogs.technet.microsoft.com/heyscriptingguy/2012/08/11/weekend-scripter-use-the-windows-task-scheduler-to-run-a-windows-powershell-script/

OR

Here – https://dmitrysotnikov.wordpress.com/2011/02/03/how-to-schedule-a-powershell-script/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s