集成阿里云的费用账单,是公司运维小伙伴经常要承担的工作。阿里云也开放了按照多个维维度查询费用账单的API供企业客户使用,集成到云管平台,财务系统等等。不过在实际工作中,常常会发现需要集成的云供应商不止一家,每一家提供的API格式都不一样。如果想要接入已经开发好的系统,数据结构不一致就需要做额外的数据Mapping工作,费时费力。小编在阿里云上发现了一款神器,可对阿里云提供的API做快速的定制,输出想要的格式,这就是阿里云的逻辑编排。
举个实际的例子,阿里云账单的API QueryInstanceBill可返回按照实例维度或者计费项维度的账单,返回的格式是这样的:
"Data": {
"Items": {
"Item": [
{
"SubscriptionType": "PayAsYouGo",
"ProductCode": "cdn",
"RecordID": "2019080352963162",
"BillingItem": "InlandNetworkOut",
"ProductDetail": "cdn",
"DeductedByPrepaidCard": 0,
"PretaxAmount": 0,
"DeductedByCoupons": 0,
"RoundDownDiscount": 0,
"UsageStartTime": "2019-08-08 12:00:00",
"UsageEndTime": "2019-08-08 13:00:00",
"Status": "NoSettle",
"PaymentTime": "",
"PaymentAmount": 0,
"Item": "PayAsYouGoBill",
"OutstandingAmount": 0,
"ProductType": "",
"DeductedByCashCoupons": 0,
"OwnerID": "",
"ProductName": "cdn",
"Currency": "CNY",
"PretaxGrossAmount": 0,
"InvoiceDiscount": 0
}
]
},
如果在集成的过程中,仅希望InlandNetworkOut这一个计费项的用量和金额汇总,输出一个特定Json格式的结果,可以按照下面的步骤操作。
第一步 权限设置
逻辑编排过程中需要调用QueryInstanceBill接口,因此需要相关的权限。可以使用AliyunBSSFullAccess权限,但为了保证最小可用原则,创建一个新的权限策略AliyunBSSBillReadOnly,使用以下的Statement
{
"Version": "1",
"Statement": [
{
"Action": ["bss:Query*", "bss:Describe*"],
"Resource": "*",
"Effect": "Allow"
}
]
}
同时创建一个拥有此权限Ram角色,并添加以下的信任策略:
- 创建RAM Role,选择Alibaba Cloud Service类型
- 添加刚刚创建的策略AliyunBSSBillReadOnly
-
修改信任策略,让逻辑编排可以访问
{"Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "Service": [ "composer.aliyuncs.com" ] } } ], "Version": "1"
}
第二步 编排流程
登录国际站并访问逻辑编排控制台https://lc.console.aliyun.com/,在左侧的菜单选择“我的工作流”,然后创建一个新的流程,输入名称和描述。创建完成后,转到“代码设计”,然后复制本文给出的一个段脚本。
{
"actions": {
"查询_InstanceBill_第一页数据": {
"type": "ACS::BSS::QueryInstanceBill",
"inputs": {
"method": "POST",
"host": {
"product": "BSS",
"api": "QueryInstanceBill",
"apiVersion": "2017-12-14"
},
"connection": {},
"queries": {
"RegionId": "ap-southeast-1",
"BillingCycle": "@get(triggerOutputs(), 'queries.BillingCycle')",
"ProductCode": "cdn",
"PageNum": 1,
"PageSize": 300,
"IsBillingItem": true
}
},
"runAfter": {
"初始化变量存放所有记录": [
"Succeeded"
]
}
},
"初始化变量存放所有记录": {
"type": "InitializeVariable",
"inputs": {
"name": "records",
"type": "Array",
"value": []
},
"runAfter": {}
},
"存入第一页数据": {
"type": "AppendToArrayVariable",
"inputs": {
"name": "records",
"value": "@body('查询_InstanceBill_第一页数据')['Data']['Items']['Item']"
},
"runAfter": {
"查询_InstanceBill_第一页数据": [
"Succeeded"
]
}
},
"遍历剩余页数": {
"type": "Until",
"inputs": {},
"runAfter": {
"初始化变量存放当前页数": [
"Succeeded"
]
},
"actions": {
"查询某一页_InstanceBill_数据": {
"type": "ACS::BSS::QueryInstanceBill",
"inputs": {
"method": "POST",
"host": {
"product": "BSS",
"api": "QueryInstanceBill",
"apiVersion": "2017-12-14"
},
"connection": {},
"queries": {
"BillingCycle": "@get(triggerOutputs(), 'queries.BillingCycle')",
"RegionId": "ap-southeast-1",
"ProductCode": "cdn",
"PageNum": "@variables('pageNumber')",
"PageSize": 300,
"IsBillingItem": true
}
},
"runAfter": {
"设置页数": [
"Succeeded"
]
}
},
"追加值至数组变量": {
"type": "AppendToArrayVariable",
"inputs": {
"name": "records",
"value": "@body('查询某一页_InstanceBill_数据')['Data']['Items']['Item']"
},
"runAfter": {
"查询某一页_InstanceBill_数据": [
"Succeeded"
]
}
},
"设置页数": {
"runAfter": {},
"type": "SetVariable",
"inputs": {
"name": "pageNumber",
"value": "@add(variables('pageNumber'), 1)"
}
}
},
"expression": "@lte(sub(ceil(div(body('查询_InstanceBill_第一页数据').Data.TotalCount, 300)), variables('pageNumber')), 0)"
},
"初始化变量存放当前页数": {
"type": "InitializeVariable",
"inputs": {
"name": "pageNumber",
"type": "Integer",
"value": 1
},
"runAfter": {
"存入第一页数据": [
"Succeeded"
]
}
},
"计算总数": {
"type": "JavascriptCode",
"inputs": {
"code": "const records = $context.variables('records');\n\nconst allUsage = records.reduce((sum, group) => {\n if (Array.isArray(group)) {\n group.forEach((record) => {\n if (record.BillingItem === 'InlandNetworkOut') {\n if (record.Usage) {\n sum.Usage += parseFloat(record.Usage) || 0;\n }\n if (record.PretaxAmount) {\n sum.PretaxAmount += parseFloat(record.PretaxAmount) || 0;\n }\n }\n \n });\n } else if (group.Usage || group.PretaxAmount) {\n if (group.BillingItem === 'InlandNetworkOut') {\n sum.Usage += parseFloat(group.Usage) || 0;\n sum.PretaxAmount += parseFloat(group.PretaxAmount) || 0;\n }\n }\n \n return sum;\n}, { Usage: 0, PretaxAmount: 0 });\n\nreturn allUsage;"
},
"runAfter": {
"遍历剩余页数": [
"Succeeded"
]
}
},
"响应": {
"type": "Response",
"outputs": {
"statusCode": 500,
"body": {
"cost_data": [
{
"usage": "@body('计算总数').Usage",
"cost": "@body('计算总数').PretaxAmount"
}
]
},
"headers": {
"Content-Type": "application/json"
}
},
"runAfter": {
"计算总数": [
"Succeeded"
]
}
}
}
}
返回图形化设置,这时需要对其中调用API的过程进行授权。点击并展开“查询InstanceBill第一页数据”,在下方点击Change Permissions,添加刚刚创建新的RAM Role,注意Authorized Policies是否包括了应该有的权限。再点开“遍历剩余页数”这一步,再展开“查询某一页InstanceBill数据”,进行如上的权限添加步骤。点击右上角的保存按键保存流程。
第三步 调用流程
在流程的第一步中,可以找到服务发起的URL,Copy到浏览器中可进行测试。在上面的例子中,调用时需要添加一个查询账期的参数BillingCycle=YYYY-MM。根据所需,可更改调用方式为POST或者GET。
这就是一个利用流程编改变API结构的方式,希望能够帮助到您。