|
13 | 13 | parser.add('--env', required=True, metavar="prod", help='Vault environment eg: prod') |
14 | 14 | parser.add('--cluster', action='append', metavar="cluster:role:namespace", default=[], help='Can be used multiple times, exported in KUBE_CONFIG_PATH: ~/.kube/atlantis/config') |
15 | 15 | parser.add('--azure', action='store_true', help='Get Azure credentials, exported as ARM_CLIENT_ID and ARM_CLIENT_SECRET') |
| 16 | +parser.add('--azure-tenant-id', help='Azure tenant ID. Required if --azure is set') |
| 17 | +parser.add('--azure-subscription-id', help='Azure subscription ID. Subscription ID for which your state is stored. Required if --azure is set') |
| 18 | +parser.add('--azure-resource-group', help='Azure resource group name. Resource group for which your state is stored. Required if --azure is set') |
16 | 19 | parser.add('--azure-no-arm', action='store_true', help='Do not export ARM_CLIENT_ID and ARM_CLIENT_SECRET, only TF_VAR_azure_client_id and TF_VAR_azure_client_secret') |
17 | 20 | parser.add('--gcp', action='append', metavar="my-project", default=[], help='GCP project names, creates TF_VAR_gcp_project_name for use in "credentials" in google provider') |
18 | 21 | parser.add('--terraform-registry', action='store_true', help='Get Terraform registry token, expects to be found in vault under "token" in secret/applications/{name}/{env}/terraform-registry') |
@@ -170,6 +173,40 @@ def vault_request(path, method='GET', data=None): |
170 | 173 | response = urllib.request.urlopen(req) |
171 | 174 | return json.loads(response.read()) |
172 | 175 |
|
| 176 | +def _azure_get_token(tenant_id: str, client_id: str, client_secret: str) -> str: |
| 177 | + token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" |
| 178 | + data = urllib.parse.urlencode( |
| 179 | + { |
| 180 | + "client_id": client_id, |
| 181 | + "client_secret": client_secret, |
| 182 | + "scope": "https://management.azure.com/.default", |
| 183 | + "grant_type": "client_credentials", |
| 184 | + } |
| 185 | + ).encode("utf-8") |
| 186 | + req = urllib.request.Request( |
| 187 | + token_url, |
| 188 | + data=data, |
| 189 | + headers={"Content-Type": "application/x-www-form-urlencoded"}, |
| 190 | + method="POST", |
| 191 | + ) |
| 192 | + with urllib.request.urlopen(req, timeout=10) as resp: |
| 193 | + payload = json.load(resp) |
| 194 | + return payload["access_token"] |
| 195 | + |
| 196 | +def azure_check_resource_group(tenant_id: str, subscription_id: str, resource_group: str, token: str) -> bool: |
| 197 | + url = ( |
| 198 | + f"https://management.azure.com/subscriptions/{subscription_id}" |
| 199 | + f"/resourcegroups/{resource_group}?api-version=2021-04-01" |
| 200 | + ) |
| 201 | + req = urllib.request.Request(url, headers={"Authorization": f"Bearer {token}"}) |
| 202 | + try: |
| 203 | + with urllib.request.urlopen(req, timeout=10): |
| 204 | + return True |
| 205 | + except urllib.error.HTTPError as exc: |
| 206 | + if exc.code in (403, 404): |
| 207 | + return False |
| 208 | + raise |
| 209 | + |
173 | 210 | env_vars = [] |
174 | 211 |
|
175 | 212 | # Log in using approle |
@@ -269,6 +306,30 @@ def vault_request(path, method='GET', data=None): |
269 | 306 | ("ARM_CLIENT_ID", azure_creds['client_id']), |
270 | 307 | ("ARM_CLIENT_SECRET", azure_creds['client_secret']) |
271 | 308 | ]) |
| 309 | + |
| 310 | + tries=0 |
| 311 | + status("Validating Azure credentials...") |
| 312 | + while True: |
| 313 | + token = _azure_get_token( |
| 314 | + tenant_id=args.azure_tenant_id, |
| 315 | + client_id=azure_creds['client_id'], |
| 316 | + client_secret=azure_creds['client_secret'] |
| 317 | + ) |
| 318 | + if azure_check_resource_group( |
| 319 | + tenant_id=args.azure_tenant_id, |
| 320 | + subscription_id=args.azure_subscription_id, |
| 321 | + resource_group=args.azure_resource_group, |
| 322 | + token=token |
| 323 | + ): |
| 324 | + status("Azure credentials are valid and resource group is accessible.") |
| 325 | + break |
| 326 | + else: |
| 327 | + status("Waiting for Azure credentials to propagate...") |
| 328 | + tries += 1 |
| 329 | + if tries >= 6: |
| 330 | + status("Azure credentials did not propagate in time, exiting...", True) |
| 331 | + break |
| 332 | + time.sleep(5) |
272 | 333 |
|
273 | 334 | if args.gcp: |
274 | 335 | for project in args.gcp: |
|
0 commit comments