addProjectIamBinding method

Future<({String error, bool success})> addProjectIamBinding({
  1. required String projectId,
  2. required String member,
  3. required String role,
  4. String? account,
})

Add an IAM binding (gcloud projects add-iam-policy-binding) granting role to member on projectId. Idempotent: re-running on a project where the binding already exists is a no-op (gcloud reports success).

member must be a fully qualified principal such as serviceAccount:foo@bar.iam.gserviceaccount.com or user:alice@example.com. The call runs without _authEnvironment so that a credentialed human account can grant roles. When account is provided, the command passes --account=<email> rather than switching the user's global active gcloud account.

Returns (success, errorMessage). The error message is a stripped stderr useful for direct display in the wizard's prompt.

Implementation

Future<({bool success, String error})> addProjectIamBinding({
  required String projectId,
  required String member,
  required String role,
  String? account,
}) async {
  final ProcessResult r = await _runner.run('gcloud', <String>[
    'projects',
    'add-iam-policy-binding',
    projectId,
    '--member=$member',
    '--role=$role',
    '--condition=None',
    '--quiet',
    if (account != null && account.trim().isNotEmpty)
      '--account=${account.trim()}',
  ]);
  if (r.success) {
    return (success: true, error: '');
  }
  final String stderr = _stripAnsi(r.stderr).trim();
  final String stdout = _stripAnsi(r.stdout).trim();
  return (
    success: false,
    error: stderr.isNotEmpty
        ? stderr
        : (stdout.isNotEmpty
              ? stdout
              : 'gcloud add-iam-policy-binding exited ${r.exitCode}'),
  );
}