- 熟练程度
- 专家

引言
AWS Cognito 是一款强大的无服务器用户管理产品。但没有任何产品或服务是百分之百完备的,遗憾的是,AWS Cognito 也不例外。
亚马逊的“80% 哲学”主张:一旦达到这个“就绪”的门槛,托管服务便会发布给早期采用者。从那时起,它就会根据爱好者们的反馈——主要通过 GitHub 以及 AWS 自家的 re:Post 社区——一条抱怨接着一条抱怨地逐步改进。
AWS Cognito 无疑一直在进行滚动式的更新。它是一款非常成熟的产品,但有些方面仍未跟上节奏,因此心怀不满的开发者们纷纷订阅那些拖了很久的 GitHub 讨论帖,盼着这些问题能尽快得到解决。因此,有些操作仍然需要你卷起袖子,拂去命令行“功夫”上的尘埃,重新露一手。批量更新 AWS Cognito 用户就是其中之一。

问题所在
如果你正在用 AWS Cognito 来管理你的用户池,你很快就会注意到,这项服务远不如其他 AWS 托管服务那样“可塑”。其中一些限制是设计使然,例如由于不存储密码,你无法轻易地把用户从一个池迁移到另一个池。然而,另一些限制则是开发团队未能跟上功能需求的副产品。
举例来说,根本没有办法批量编辑用户属性。这一点在方方面面都是如此:AWS CLI、AWS Web 控制台、AWS CloudFormation、AWS SAM、AWS SDK、AWS CDK 等等。就本文撰写之时而言,如果你想要打理大量的用户属性,就得稍微“极客”一把。

解决方案
注意:AWS Cognito 不允许你在用户池层面修改全局属性设置。我们能够修改的,是用户层面上的这些属性。如果你需要更改用户池的全局设置,那就别无选择,只能新建一个用户池,并实现一套渐进式的用户迁移机制,把用户从旧池拉入新池。如果你在这方面需要帮助,请联系我们。
第 1 步:安装并配置 AWS CLI
既然 AWS 的 Web 控制台不让你这么做,我们就退而求其次,改用 AWS CLI。因此,首要的前提条件就是安装并妥善配置好这个基础工具。
AWS 命令行的安装与配置超出了本文的范围,所以如果你还没把它配置好,请遵循官方说明来完成配置。请确保你安装的是本文撰写时的最新版本,即 v2。
第 2 步:安装 jq 命令行工具
你还会需要一个 jq 命令行工具——在你惯用的 shell 环境中处理 JSON 数据,它是最舒服的方式。你可以通过它的官方网站。
使用 Homebrew,运行以下命令:
brew install jq && jq --version如果控制台显示出类似下方输出的软件包版本,则说明这一步已成功完成。
jq-1.7.1
第 3 步:构造并运行命令
由于我们操作的是 AWS Cognito,相应的 CLI 命令将是 cognito-idp。要进一步了解如何使用这项服务的 CLI 接口,请运行 aws cognito-idp help。这么做时,你会再一次意识到,没有任何一条命令能够一次性作用于多个用户。
为了说明这一点,我们将运行 admin-delete-user-attributes 命令。不过,你可以把这种方法推而广之,应用到其他那些原本只允许作用于单个用户的操作上。同样地,你可以运行以下命令来了解更多细节。
aws cognito-idp admin-delete-user-attributes help我们想做的第一件事,是从我们的用户池中检索出所有的用户名。
aws cognito-idp list-users --user-pool-id yourUserPoolID --no-paginate --profile yourCustomAWSProfile在这个示例中,我们没有筛选掉任何用户,但如果你有此需要,请使用 --filter 标志。你可以通过运行以下命令来进一步了解它:
aws cognito-idp list-users help你懂的,老规矩。
我们还希望一次性拿到所有结果,而不进行分页——因此才用到 --no-paginate 标志。
上面这段代码块的运行结果,会返回给我们一个格式大致如下的 JSON 文件:
{
"Users": [
{
"Username": "some-username-or-uuid",
"Attributes": [
{
"Name": "sub",
"Value": "uuid"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "custom:yourCustomAttribute",
"Value": "some-value"
},
{
"Name": "email",
"Value": "test@test.com"
}
],
"UserCreateDate": "2024-02-14T00:30:59.508000+08:00",
"UserLastModifiedDate": "2024-02-14T00:31:19.059000+08:00",
"Enabled": true,
"UserStatus": "CONFIRMED"
}
]
}其中 Attributes 的内容可能有所不同,但结构会是类似的。
现在,我们需要用 jq。
jq -r '.Users.[].Username'上面这段代码片段会遍历由 aws cognito-idp list-users 返回的、预先定义好的 JSON 树形数据结构。我们从根 。 开始,解包 Users 属性——它的值是一个数组 []——最后抓取这个列表中每一项的 Username 属性值。标志 -r 只是向 jq 表明:它应当返回不带前后引号的值。 如果你不使用这个标志,那些引号就会被当作下一步要搜索的值的一部分,从而导致查不到任何结果。 所以,这个标志在此处是必不可少的。
让我们把上述操作组合起来,得到一份纯用户名的列表,每行一个:
aws cognito-idp list-users --user-pool-id yourUserPoolID --no-paginate --profile yourCustomAWSProfile | jq -r '.Users.[].Username'这个 | 字符会把第一条命令的输出作为第二条命令的输入传递(或者说“管道传输”)过去。这样一来,AWS CLI 的 Cognito 请求输出就会被 jq。
现在,让我们对这份干净的用户名列表进行迭代,以执行针对单个用户的操作:
for username in $(aws cognito-idp list-users --user-pool-id yourUserPoolID --no-paginate --profile yourCustomAWSProfile | jq -r '.Users.[].Username'); do SOME VALUABLE WORK HERE; done如你所见,到了这一步,可谓天高任鸟飞、无所不能。为了把这个示例做完整,我们来删除一个属性:
for username in $(aws cognito-idp list-users --user-pool-id yourUserPoolID --no-paginate --profile yourCustomAWSProfile | jq -r '.Users.[].Username'); do aws cognito-idp admin-delete-user-attributes --user-pool-id yourUserPoolID --username $username 瞧,你的用户池就这样打理好了。几乎所有用户层面的 CRUD 操作都可以用这种方式实现自动化。而现在,你已经具备了这么做的本领。
结论
AWS Cognito 是一款强大、高度可定制且留有扩展空间的用户管理解决方案。它应当能够满足你大部分的认证需求。然而,一些你本以为理应存在的基础操作却并不具备——要么是设计使然,要么是因为相关功能仍躺在团队的待办事项列表里,比如在通过链接完成注册验证后自动跳转。唉,也没办法。
尽管高级用户很可能会撞上各种限制,但在大多数情况下总有变通之法,哪怕这些方法需要一个“应急出口”——就像上面描述的那样,为了达到预期效果,你不得不“跳出”AWS 的工具集,把数据放进一个外部的“研磨机”里过一遍,再把它“倒回”原处。而且,如果这能算是一点安慰的话,它让我们所有人都成了更出色的工程师。
