---
# **Agenda**
- 前言
- Identity
- Role
- IAM policy
- 整體架構
- IAM Resource Hierarchy
- 實際演練
---
## 1. 前言
CLOUD IAM 是 GCP 管理資源權限的服務,全名叫Identity and Access Management,中文意思即「身分識別與存取管理」,主要功能是讓GCP知道 **誰(Identity)** 可以對哪些 **資源(Resource)** 做 **哪些行為(Role)**。
---
## 2. **Identity**
- Google Account : 可能是開發者或是管理員的電子郵件地址,通常是 gmail.com,也可以是其他域名。
- Service Account : 提供給應用程式的帳號,例如應用程式要使用gcp的資源時,就會以被指定的服務帳號來驗證其身分,若有相關權限,即可對GCP資源做權限所允許的行為。
- Google Group : 是一個由多個 Google Account 或 Service Account 組成的集合,而每一個Google Group都會有唯一的電子郵件帳號,其用途在管理者可透過這個帳號,直接對集合中的帳號統一授權或是權限調整,提高管理上的便利性 ; 惟須留意,Google Group帳號是無法作為登入GCP時所使用的帳號的。
- Google Workspace : 主要是以一個域名來管理,例如某組織註冊域名 xxx.com,並用這個域名為底下員工註冊google帳戶,如 employee_1@xxx.com、employee_2@xxx.com,那麼設定iam的時候,就能一次性的使用xxx.com來管理組織下的權限。
- Cloud Identity Domain : 類似於Google Workspace,差別在於如果沒有要使用Workspace的服務,那使用 Cloud Identity Domain 即可。
---
## 3. **Roles**
Roles 與兩項目有相關,即 Resource、Permission,故先簡單描述兩者,接著再介紹Roles。
### Resource :
即GCP提供的各種資源,例如 storage、cloudsql、memorystore等等。
### Permission :
可以對gcp資源做什麼行為,例如刪除gce的vm,即需要如下圖的權限。

### Roles
Roles 是由一個或多個 Permission 所組成的角色,其類型有以下三種 :
1. **Basic Roles (基本角色)** :
有Owner/Editor/Viewer三種role,在啟用gcp project後就會存在了。其中Owner權限最大,擁有project內所有資源的存取權,而Editor是可以修改既已存在的資源並包含Viewer的權限,Viewer則只能對資源進行檢視。這種類型的role在管理權限上是非常不細致的,因此並不建議使用這種方式來管理權限。
2. **Predefined Roles (預定義角色)** :
由GCP提供並持續維護與更新的role,這種角色可以針對特定的資源去配置權限,相較於Basic Roles是對整個專案的資源都適用,Predefined Roles在權限控管上的精細度是有所提升的。實例如下圖紅框處,該權限僅限於GCS的建立、讀取、更新和刪除物件及分段上傳:

3. **Custom Roles (自定義角色)** :
此種 role 比 Predefined role 又更精細,如下圖紅框處,可以只針對特定權限授予,例如只想授予storage的建立權限,那只加入 storage.bucket.create 即可達到需求。

---
## 4. **IAM policy**
當角色都制定好,再來就是要如何授權給使用者了,此時就會使用到 IAM policy。

以上圖來說明,IAM policy是由一個或多個Binding的集合,其中Binding就是將某種Roles綁定給使用者的概念。
---
## 5. 整體架構

Cloud IAM 會根據 IAM policy 去查看**誰(Identity)** 可以對哪些 **資源(Resource)** 做 **哪些行為(Role)**,所以當使用者沒有被綁定到Roles,就無法執行該Roles所擁有的權限,此即為IAM的整體概念。
---
## 6. IAM Resource Hierarchy
在 GCP 上,所有資源都是以階層的方式進行組織,從上到下依序為:
1. Organization: 根節點,資源的最上層
2. Folder: Organization 的子項目
3. Project: Organization 或是 Folder 的子項目
4. Resource: Project 下面的子項目

---
## 7. 實際演練
以下會以GCS作為示範的資源,建置則以terraform方式建立。
簡單描述演練項目及步驟:
1. terraform前置準備
2. 創建自定義的角色
3. 創建服務帳號
4. 創建iam_policy
5. 驗證iam_policy
6. condition功能演示
<br>
- **terraform前置準備 (此步驟只限terraform練習才要,用ui練習者可忽略)**
- ui介面操作:
(1) 先為terraform建置服務帳號

接著產出服務帳戶的金鑰:

(2)創建一個自訂義的角色,角色要擁有以下權限:
```
iam.roles.get
iam.roles.create
iam.roles.delete
iam.roles.update
iam.roles.undelete
iam.serviceAccountKeys.create
iam.serviceAccountKeys.delete
iam.serviceAccountKeys.get
iam.serviceAccounts.create
iam.serviceAccounts.delete
iam.serviceAccounts.get
resourcemanager.projects.setIamPolicy
resourcemanager.projects.getIamPolicy
```

(3) 建置好角色後,將角色綁訂至terraform的服務帳號

- terraform腳本:
```terraform=
// provider.tf
provider "google" {
credentials = var.credentials
project = var.project
region = var.region
}
provider "google-beta" {
credentials = var.credentials
project = var.project
region = var.region
}
// variable.tf
variable "credentials" {
type = string
default = "terraform-test.json"
}
variable "project" {
type = string
default = "iamrich-lab"
}
variable "region" {
type = string
default = "asia-east1"
}
//terraform-test.json
-> 將剛剛產出的金鑰內容貼到此份檔案
```
- **創建自定義角色**
- ui的設定位置在:

- terraform建置腳本:
```terraform=
# main.tf
resource "google_project_iam_custom_role" "custom_role"{
for_each = var.custom_role_list
role_id = each.value.role_id
title = each.value.title
permissions = each.value.permissions
description = each.value.description
}
# variable.tf
variable "custom_role_list"{
type = map(object({
role_id = string
title = string
permissions = list(string)
description = string
}))
default = {
custom_role_1 = {
role_id = "CustomRoleFortest1"
title = "Custom Role Test 1"
permissions = ["storage.buckets.create","storage.buckets.get"]
description = "forIamTest-storage-create"
}
}
}
```
- **創建服務帳號**
- ui的設定位置在:

- terraform建置腳本:
```terraform=
# *main.tf*
#--創建服務帳號
resource "google_service_account" "for-test" {
for_each = var.servcieaccount_list
account_id = each.value.account_id
display_name = each.value.display_name
}
#--產出服務帳號的金鑰
resource "google_service_account_key" "mykey" {
for_each = var.key_list
service_account_id = google_service_account.for-test[each.key].name
public_key_type = each.value.public_key_type
}
#--將上面服務帳號的私鑰 匯出至本機指定的目錄 (等等切換帳號時需用到)
resource "local_file" "download_key_json"{
for_each = var.keyjson_list
content = base64decode(google_service_account_key.mykey[each.key].private_key)
filename = each.value.filename
}
# variable.tf
variable "servcieaccount_list"{
type = map(map(string))
default = {
for_test = {
account_id = "for-test"
display_name = "Allow to editor storage."
}
}
}
variable "key_list" {
type = map(map(string))
default = {
for_test = {
public_key_type = "TYPE_X509_PEM_FILE"
}
}
}
variable "keyjson_list"{
type = map(map(string))
default = {
for_test ={
filename = "./serviceaccount_demo/for-test.json"
}
}
}
```
- **創建iam_policy**
- ui的設定位置在:

- terraform建置腳本:
分別有兩種不同情境:
1. 針對單一帳號設定 policy
```terraform=
# main.tf
resource "google_project_iam_member" "iam_policy" {
for_each = var.google_project_iam_member_list
project = var.project
role = each.value.role
member = each.value.member
dynamic "condition" {
for_each = each.value.condition_config
content {
title = condition.value["title"]
description = condition.value["description"]
expression = condition.value["expression"]
}
}
}
# variable.tf
variable "google_project_iam_member_list" {
type = map(object({
role = string
member = string
condition_config = list(object({
title = string
description = string
expression = string
}))
}))
default = {
for_iam_member_1 = {
role = "projects/iamrich-lab/roles/CustomRoleFortest1"
member = "serviceAccount:for-test@iamrich-lab.iam.gserviceaccount.com"
condition_config = []
}
}
}
```
2. 針對多組帳號設定 policy
```terraform=
# main.tf
resource "google_project_iam_binding" "iam_policy" {
for_each = var.google_project_iam_binding_list
project = var.project
role = each.value.role
member = each.value.muli_member
dynamic "condition" {
for_each = each.value.condition_config
content {
title = condition.value["title"]
description = condition.value["description"]
expression = condition.value["expression"]
}
}
}
# variable.tf
variable "google_project_iam_binding_list" {
type = map(object({
role = string
muli_member = list(string)
condition_config = list(object({
title = string
description = string
expression = string
}))
}))
default = {
for_iam_muliMember = {
role = "projects/iamrich-lab/roles/CustomRoleFortest1"
muli_member = [
"serviceAccount:for-test@iamrich-lab.iam.gserviceaccount.com",
]
condition_config = []
}
}
}
```
關於Permission有哪些 可參考[GCP文件](https://cloud.google.com/iam/docs/understanding-roles#cloud-storage-roles)
- **驗證iam_policy (使用剛創建的服務帳號創建+刪除bucket)**
- ui的設定位置在:

- terraform建置腳本:
到這邊我們需要切換資料夾,因為接下來要使用剛剛建好的服務帳號執行,來驗證iam_policy生效。新的資料夾會有以下:
```terraform=
# main.tf
resource "google_storage_bucket" "bucket"{
name = "test-20240605"
location = "ASIA"
force_destroy = false
storage_class = "STANDARD"
cors {
origin = ["*"]
method = ["GET"]
response_header = ["Access-Control-Allow-Origin"]
max_age_seconds = 3600
}
}
# variable.tf
variable "credentials" {
type = string
default = "for-test.json"
}
variable "project" {
type = string
default = "iamrich-lab"
}
variable "region" {
type = string
default = "asia-east1"
}
# provider.tf
provider "google" {
credentials = var.credentials
project = var.project
region = var.region
}
provider "google-beta" {
credentials = var.credentials
project = var.project
region = var.region
}
```
- **condition功能演示**
- ui的設定位置在:

(在創建iam_policy時可以順便設定的)
- terraform腳本:
```terraform=
variable "google_project_iam_member_list" {
type = map(object({
role = string
member = string
condition_config = list(object({
title = string
description = string
expression = string
}))
}))
default = {
for_iam_member_1 = {
role = "projects/iamrich-lab/roles/CustomRoleFortest1"
member = "serviceAccount:for-test-1@iamrich-lab.iam.gserviceaccount.com"
condition_config = [
{
title = "condition_test"
description = "for-condition-test"
expression = "request.time.getHours('Asia/Taipei') >= 9 && request.time.getHours('Asia/Taipei') <= 15 && request.time.getDayOfWeek('Asia/Taipei') >= 1 && request.time.getDayOfWeek('Asia/Taipei') <= 5"
}
]
}
}
}
```
---
## 參考文件
https://cloud.google.com/iam/docs/overview?hl=zh-cn
https://cloud.google.com/iam/docs/conditions-overview?hl=zh-cn
https://blog.cloud-ace.tw/identity-security/what-is-cloud-iam/
https://hackmd.io/@kevinhuangtw/SJ2KjoRKc
terraform項目:
[google_project_iam_custom_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam_custom_role)
[google_service_account](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account)
[google_service_account_key](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account_key)
[local_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file)
[google_project_iam](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam)
[google_storage_bucket](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket)