【问题标题】:Powershell command to sync Office 365 user to Mailchimp用于将 Office 365 用户同步到 Mailchimp 的 Powershell 命令
【发布时间】:2022-01-01 03:08:45
【问题描述】:

我发现了一个 Powershell 脚本,它可以将 Office 365 用户同步到 Mailchimp 联系人。它运行良好,但唯一的问题是我希望它在 Mailchimp 中“取消订阅”或“存档”联系人,如果 Office 365 中的先前许可用户不再具有任何活动许可证。

现在,如果 Office 365 中的用户许可证发生更改,它将更新许可证,但如果所有许可证都已删除,则不会。我认为脚本中的以下行是导致没有许可证的用户无法同步的原因,但我不知道解决它的最佳方法...

$users = Get-MsolUser | Where-Object {$_.islicensed}

如果我将此行更改为$users = Get-MsolUser -All,那么无论用户是否获得许可,它都会检索用户,但这仍然不能完全解决我的问题,因为我的目标仍然是只同步许可用户,但要确保一旦 O365 用户未获得许可,他们将在 Mailchimp 中“取消订阅”或“存档”(两者都可以)。

我正在粘贴下面的整个脚本,并在我上面提到的代码行旁边添加了一个

$user = "abc"
$apiKey = "544492bf22f6895d4ae-us15"
$pair = "${user}:${apiKey}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$Headers = @{
   Authorization = $basicAuthValue 
}
$baseUri = "https://us15.api.mailchimp.com"
$listInfo = @{
   listName            = "Office 365 Customer Sync"
   company             = "abc"
   address1            = "abc"
   address2            = " "
   city                = "abc"
   state               = "NY"
   zip                 = "11111"
   country             = "US"
   phone               = ""
   permission_reminder = "You are receiving this email because you are a client of Abtech Technologies"
   from_name           = "John Doe"
   from_email          = "jdoe@email.com"
   subject             = ""
   language            = "en"
}
$Office365members = @()
Connect-MsolService

function Ensure-MailChimpList ($ListName) {
   $lists = Invoke-RestMethod -URI $baseUri/3.0/lists?offset=0"&"count=100 -Method Get -Headers $Headers
   if ($lists.lists.name -notcontains $ListName) {
       $list = New-MailChimpList -ListInfo $listInfo
       return $list
   }
   else {
       $list = $lists.lists | Where-object {$_.name -contains $listName}
       return $list
   }
}


function New-MailChimpList ($ListInfo) {
   $listBody = @{
       name                = $listInfo.listName
       contact             = @{
           company  = $listInfo.company
           address1 = $listInfo.address1
           address2 = $listInfo.address2
           city     = $listInfo.city
           state    = $listInfo.state
           zip      = $listInfo.zip
           country  = $listInfo.country
           phone    = $listInfo.phone
       }
       permission_reminder = $listInfo.permission_reminder
       campaign_defaults   = @{
           from_name  = $listInfo.from_name
           from_email = $listInfo.from_email
           subject    = $listInfo.subject
           language   = $listInfo.language
       }
       email_type_option   = $false
   }

   $listBody = $listBody | ConvertTo-Json

   $newList = Invoke-RestMethod -URI $baseUri/3.0/lists -Method POST -Headers $Headers -Body $listBody
   return $newList
}

function Get-MailChimpListSegments {
   $segments = Invoke-RestMethod -URI $baseUri/3.0/lists/$listId/segments?offset=0"&"count=200 -Method Get -Headers $Headers
   return $segments
}

function Get-MailChimpListMergeFields {
   $mergeFields = Invoke-RestMethod -URI $baseUri/3.0/lists/$listId/merge-fields -Method Get -Headers $Headers
   return $mergeFields
}

function New-MailChimpListMergeField ($Name, $Type, $Tag) {
   $merge_field = @{
       name = $Name
       type = $Type
       tag  = $Tag
   }
   $merge_field = $merge_field | ConvertTo-Json
   Invoke-RestMethod -URI $baseUri/3.0/lists/$listId/merge-fields -Method POST -Headers $Headers -Body $merge_field
}

function New-MailChimpListBatch ($MemberBatch) {

   $body = @{
       members         = $MemberBatch
       update_existing = $true
   }
   
   $body = $body | ConvertTo-Json -Depth 10

   $batchresult = Invoke-RestMethod -URI $baseUri/3.0/lists/$listId -Method POST -Headers $Headers -Body $body
   return $batchresult
}
function New-MailChimpListMember ($email, $FirstName, $LastName, $Company, $Licenses) {
   
   $merge_fields = @{
       FNAME    = $FirstName
       LNAME    = $LastName
       COMPANY  = $Company
       LICENSES = $Licenses
   }

   
   $member = @{
       email_address = $email
       status        = "subscribed"
       merge_fields  = $merge_fields
   }
   return $member
}
function Update-MailChimpListMember ($ExistingMember, $Office365Member) {
   
   $merge_fields = @{
       FNAME    = $Office365Member.FirstName
       LNAME    = $Office365Member.LastName
       COMPANY  = $Office365Member.Company
       LICENSES = $Office365Member.Licenses
   }

   
   $member = @{
       email_address = $Office365Member.Email
       status        = $existingMember.Status
       merge_fields  = $merge_fields
   }
   return $member
}

# function Unsubscribe-MailChimpListMember ($ExistingMember) {

  # $member = @{
   #   email_address = $ExistingMember.Email
  #     status        = "unsubscribed"
  # }
  # return $member

#   Invoke-RestMethod -URI $baseUri/3.0/lists/$listId/members/$($existingMember.SubscriberHash) -Method PATCH -Headers $Headers -Body $member
# }

function Get-ExistingMailChimpListMembers {
   $existingMembers = $null
   $existingmembers = Invoke-RestMethod -URI $baseUri/3.0/lists/$listId/members?offset=0"&"count=100 -Method Get -Headers $Headers
   for ($i = 100; $i -le $existingMembers.total_items; $i += 100) {
       $members = Invoke-RestMethod -URI $baseUri/3.0/lists/$listId/members?offset=$i"&"count=100 -Method Get -Headers $Headers
       $existingMembers.members += $members.members
   }
   return $existingMembers
}

function New-LicenseSegment ($SkuPartNumber) {
   $conditions = @()
   
   $conditionproperty = @{
       condition_type = "TextMerge"
       field          = "LICENSES"
       op             = "contains"
       value          = $SkuPartNumber.TrimStart("License: ")
   }
   $conditions += $conditionproperty
   
   $segment = @{
       name    = $SkuPartNumber
       options = @{
           match      = "any"
           conditions = $conditions
       }
   }
   
   $segment = $segment | ConvertTo-Json -Depth 10
   
   $newSegment = Invoke-RestMethod -URI $baseUri/3.0/lists/$listId/segments -Method POST -Headers $Headers -Body $segment
   return $newSegment
}

function Create-ExistingMemberCollection ($ExistingMembers) {
   
   $existingMemberCollection = @()
   foreach ($existingmember in $existingmembers) {
       
       $memberObject = New-Object PSObject -Property @{
           Licenses       = $existingmember.merge_fields.LICENSES
           FirstName      = $existingmember.merge_fields.FNAME
           LastName       = $existingmember.merge_fields.LNAME
           Company        = $existingmember.merge_fields.COMPANY
           Email          = $existingmember.email_address
           SubscriberHash = $existingmember.id
           Status         = $existingmember.status
       }
       $existingMemberCollection += $memberObject
   }
   

   return $existingMemberCollection
}

function Create-UploadBatch ($collection) {
   # Upload the collection in batches of 100
   $counter = [pscustomobject] @{ Value = 0 }
   $batchsize = 100
   $batches = $collection | Group-Object -Property { [math]::Floor($counter.Value++ / $batchSize) }
   foreach ($batch in $batches) { New-MailChimpListBatch -MemberBatch $batch.Group}
}


# Check whether list exists and create it if it doesn't
$listID = (Ensure-MailChimpList -ListName $listInfo.listName).id

# Check for current merge fields in list
Write-Host "Retrieving Merge Fields"
$mergeFields = Get-MailChimpListMergeFields
$updateSegments = $false

$requiredMergeFields = "Company", "Licenses"
foreach ($requiredMergeField in $requiredMergeFields) {
   if (!$mergeFields.merge_fields.Name.contains($requiredMergeField)) {
       Write-Host "Creating Merge Field: $requiredMergeField" -ForegroundColor Yellow
       $newMergeField = New-MailChimpListMergeField -Name $requiredMergeField -Type text -Tag $requiredMergeField.ToUpper()
   }
}


# Retrieve existing segments, then filter by license related segments
$existingSegments = Get-MailChimpListSegments
$existingLicenseSegments = $existingSegments | Where-Object {$_.segments.name -match "License: "}
$requiredSegments = @()


# Create collection containing required info for all users in all tenants

Write-Host "Retrieving users and license info" -ForegroundColor Blue
$users = $null
$users = Get-MsolUser | Where-Object {$_.islicensed}   <----------------------------------------------
$company = (Get-MsolCompanyInformation).DisplayName
foreach ($user in $users) {
   [string[]]$userLicenses = $null
   foreach ($license in $user.Licenses.AccountSku.SkuPartNumber) {
       $userLicenses += "'$license'"
   }
   $userLicensesString = $userLicenses -join ','
       

   # Check if these licenses already have segments created or queued
   foreach ($license in $userlicenses) {
       $expectedSegmentName = "License: $license"
       if ($existingLicenseSegments.segments.Name -notcontains $expectedSegmentName -and $requiredSegments -notcontains $expectedSegmentName) {
           $requiredSegments += $expectedSegmentName
           Write-Host "Adding $expectedSegmentName to required segments list. Update pending." -ForegroundColor Yellow
           $updateSegments = $true
       }
   }

   $firstName = $user.FirstName
   if (!$user.FirstName) {
       $firstName = $user.DisplayName.Split(" ")[0]
   }
   $lastName = $user.LastName
   if (!$user.lastName -and !$user.firstname) {
       $lastName = $user.DisplayName.Split(" ") | Where-Object {$_ -notcontains $user.DisplayName.Split(" ")[0]}
       $lastName = $lastName -join " "
   }
   elseif (!$user.lastName -and $user.firstname) {
       $lastname = ""
   }
   elseif (!$user.LastName -and !($user.DisplayName.Split(" ") | Where-Object {$_ -notcontains $user.DisplayName.Split(" ")[0]})) {
       $lastname = ""
   }
       
   $email = $user.UserPrincipalName
   $memberObject = New-Object PSObject -Property @{
       Licenses  = $userLicensesString
       FirstName = $firstName
       LastName  = $lastName
       Company   = $company
       Email     = $email
   }
   $Office365members += $memberObject
}

Write-Host "Retrieved $($office365Members.count) licensed users" -ForegroundColor Green

# If an update is required, add new segments
if ($updateSegments) {
   Write-Host "New segments required: $requiredSegments" -ForegroundColor Green
   # Create new license segments

   if ($requiredSegments.count -gt 0) {
       foreach ($requiredSegment in $requiredSegments) {
           Write-Host "Creating new segment - $requiredSegment" -ForegroundColor Blue
           $newSegment = New-LicenseSegment -SkuPartNumber $requiredSegment
       }
   }
}

# Check if members exist in list
# Build list of existing members

Write-Host "Getting existing members"

$existingMembers = (Get-ExistingMailChimpListMembers).members

$newMemberCollection = @()

# Check if Office 365 users already exist in mailchimp. If they don't, add them to a collection for upload
foreach ($member in $Office365members) {
   if ($existingMembers.email_address -notcontains $member.Email) {
       Write-Host "Adding $($member.firstName) $($member.lastName) to new subscriber collection" -ForegroundColor Green
       $newMemberObject = New-MailChimpListMember -email $member.Email -FirstName $member.FirstName -LastName $member.LastName -Licenses $member.Licenses -Company $member.Company
       $newMemberCollection += $newMemberObject
   }
}

Create-UploadBatch -collection $newMemberCollection

# Retrieve updated list of existing members

$existingMembers = (Get-ExistingMailChimpListMembers).members
$existingMemberCollection = Create-ExistingMemberCollection -ExistingMembers $existingMembers

# Update users when details have changed.

$updatedMemberCollection = @()
foreach ($existingMember in $existingMemberCollection) {
   $office365Member = $null
   $office365Member = $Office365members | Where-Object {$_.Email -contains $existingMember.Email}
   if ($office365member) {
       if ($office365Member.FirstName -notmatch $existingMember.FirstName -or `
               $office365Member.LastName -notmatch $existingMember.LastName -or `
               $office365Member.Licenses -notmatch $existingMember.Licenses -or `
               $office365Member.Company -notmatch $existingMember.Company){
           
           Write-Host "Updating record for $($existingMember.Email)" -ForegroundColor Yellow
           $updatedMemberObject = Update-MailChimpListMember -ExistingMember $existingMember -Office365Member $office365Member
           $updatedMemberCollection += $updatedMemberObject
           
       }
   }
}

Create-UploadBatch ($updatedMemberCollection)

# Unsubscribe users who no longer exist in Office 365

# $unsubscribedMemberCollection = @()
# foreach ($existingMember in $existingMemberCollection) {
#    if ($Office365members.License -notcontains $existingmember.License -and $existingmember.status -notcontains "unsubscribed") {
#        Write-Host "$($existingmember.License) is no longer licensed in Office 365, unsubscribing." -ForegroundColor Red
#        $unsubscribedMemberObject = Unsubscribe-MailChimpListMember -ExistingMember $existingMember
#        $unsubscribedMemberCollection += $unsubscribedMemberObject 
#    }
# }

# Create-UploadBatch ($unsubscribedMemberCollection)                                                                                                                                                                                                          

【问题讨论】:

    标签: powershell office365 mailchimp


    【解决方案1】:

    如果您查看您提供的最后几行代码,看起来代码的作者已经尝试解决此问题,但可能无法使其正常工作或不再需要它,因此他们将其注释掉.这是您现有的代码,没有任何修改:

    # Unsubscribe users who no longer exist in Office 365
    
    $unsubscribedMemberCollection = @()
    foreach ($existingMember in $existingMemberCollection) {
       if ($Office365members.License -notcontains $existingmember.License -and $existingmember.status -notcontains "unsubscribed") {
           Write-Host "$($existingmember.License) is no longer licensed in Office 365, unsubscribing." -ForegroundColor Red
           $unsubscribedMemberObject = Unsubscribe-MailChimpListMember -ExistingMember $existingMember
           $unsubscribedMemberCollection += $unsubscribedMemberObject 
       }
    }
    
    Create-UploadBatch ($unsubscribedMemberCollection) 
    

    粗略检查一下,一个关键的变化很明显 - $existingmember.License 在您的运行代码中不再是单数,它现在是 $existingmember.Licenses 的集合,所以这可能是一个原因先前设计的取消订阅方法无法按预期工作。 我假设 unsubscribe 行为正常,但您只是缺少正确识别没有当前许可证的用户的方法,我正在继续这个答案。

    目前,此代码检索带有$Office365members.License 的许可证列表,然后检查它是否包含为$existingmember 存储的许可证。如果$existingmember 删除了一个许可证但另一个许可证仍然正确,则上述代码将取消订阅它们。所以我们需要重构代码,只对当前没有任何许可证的用户采取行动:

    # Unsubscribe users who no longer exist in Office 365
        
    $unsubscribedMemberCollection = @()
    foreach ($existingMember in $existingMemberCollection) {
        # create a new object to hold a list of True/False values for each checked license        
        $licenseCheck = [System.Collections.ArrayList]::new() 
        forEach($license in $existingMember.Licenses){
            if ($Office365members.License -notcontains $License -and $existingmember.status -notcontains "unsubscribed") {
                # Add $false for a non-existent license
                [void]$licenseCheck.Add($false) 
                Write-Host "$($License) not found." -ForegroundColor Red
            }else{
                # Add $true wherever the user still has a valid license
                [void]$licenseCheck.Add($true)  
                #Write-Host "$($existingmember.License) found." -ForegroundColor Green
            }
        }
    
        # Trigger the unsubscribe if the user does not have any valid licenses
        if($licenseCheck -notcontains $true){   
            Write-Host "$($License) is no longer licensed in Office 365, unsubscribing." -ForegroundColor Red
            $unsubscribedMemberObject = Unsubscribe-MailChimpListMember -ExistingMember $existingMember
            $unsubscribedMemberCollection += $unsubscribedMemberObject 
        }
    }
        
    Create-UploadBatch ($unsubscribedMemberCollection) 
    

    我要补充一点,上面的代码是不完善的,因为它会取消订阅具有没有有效许可证的用户(我认为这是必需的行为),但如果他们仍然没有更新他们的许可证,则不会更新持有至少一份有效执照。但是话又说回来,您说您的其他功能已经这样做了,所以我假设此代码只需要涵盖现有用户已删除所有许可证的情况。

    【讨论】:

    • 谢谢史蒂夫。我将测试脚本并与您联系。新年快乐,感谢您为我整理了这些内容。
    • 感谢@JeffreyFilmore,希望它能完成这项工作!
    • @JeffreyFilmore - 如果这有帮助,请随时投票和/或接受答案
    猜你喜欢
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    相关资源
    最近更新 更多